Compare commits

..

No commits in common. "58afa6aa95b54c5a6db524669f5c753060b11c67" and "ca968b7ba673da076d7cdde807ac7a55999850bd" have entirely different histories.

7 changed files with 267 additions and 60 deletions

2
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "cmake"] [submodule "cmake"]
path = cmake path = cmake
url = ../cmake-utils.git url = https://github.com/wangwenx190/cmake-utils.git

View File

@ -31,6 +31,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
class MicaMaterial; class MicaMaterial;
struct Transform
{
qreal Horizontal = 0;
qreal Vertical = 0;
};
class FRAMELESSHELPER_CORE_API MicaMaterialPrivate : public QObject class FRAMELESSHELPER_CORE_API MicaMaterialPrivate : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -46,6 +52,9 @@ public:
Q_NODISCARD static QColor systemFallbackColor(); Q_NODISCARD static QColor systemFallbackColor();
Q_NODISCARD static QSize monitorSize();
Q_NODISCARD static QSize wallpaperSize();
Q_NODISCARD QPoint mapToWallpaper(const QPoint &pos) const; Q_NODISCARD QPoint mapToWallpaper(const QPoint &pos) const;
Q_NODISCARD QSize mapToWallpaper(const QSize &size) const; Q_NODISCARD QSize mapToWallpaper(const QSize &size) const;
Q_NODISCARD QRect mapToWallpaper(const QRect &rect) const; Q_NODISCARD QRect mapToWallpaper(const QRect &rect) const;
@ -69,7 +78,7 @@ private:
bool fallbackEnabled = true; bool fallbackEnabled = true;
QBrush micaBrush = {}; QBrush micaBrush = {};
bool initialized = false; bool initialized = false;
QSize wallpaperSize = {}; Transform transform = {};
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -57,6 +57,8 @@ public:
Q_NODISCARD QSize getRecommendedButtonSize() const; Q_NODISCARD QSize getRecommendedButtonSize() const;
Q_NODISCARD bool isHovered() const;
Q_NODISCARD bool isPressed() const;
Q_NODISCARD QColor getHoverColor() const; Q_NODISCARD QColor getHoverColor() const;
Q_NODISCARD QColor getPressColor() const; Q_NODISCARD QColor getPressColor() const;
Q_NODISCARD QColor getNormalColor() const; Q_NODISCARD QColor getNormalColor() const;
@ -65,6 +67,8 @@ public:
Q_NODISCARD bool isActive() const; Q_NODISCARD bool isActive() const;
Q_NODISCARD int iconSize2() const; Q_NODISCARD int iconSize2() const;
void setHovered(const bool value);
void setPressed(const bool value);
void setHoverColor(const QColor &value); void setHoverColor(const QColor &value);
void setPressColor(const QColor &value); void setPressColor(const QColor &value);
void setNormalColor(const QColor &value); void setNormalColor(const QColor &value);
@ -73,6 +77,8 @@ public:
void setActive(const bool value); void setActive(const bool value);
void setIconSize2(const int value); void setIconSize2(const int value);
void enterEventHandler(QT_ENTER_EVENT_TYPE *event);
void leaveEventHandler(QEvent *event);
void paintEventHandler(QPaintEvent *event); void paintEventHandler(QPaintEvent *event);
private: private:
@ -87,6 +93,8 @@ private:
QColor m_normalColor = {}; QColor m_normalColor = {};
QColor m_activeForegroundColor = {}; QColor m_activeForegroundColor = {};
QColor m_inactiveForegroundColor = {}; QColor m_inactiveForegroundColor = {};
bool m_hovered = false;
bool m_pressed = false;
bool m_active = false; bool m_active = false;
std::optional<int> m_iconSize2 = std::nullopt; std::optional<int> m_iconSize2 = std::nullopt;
}; };

View File

@ -25,19 +25,21 @@
#pragma once #pragma once
#include <FramelessHelper/Widgets/framelesshelperwidgets_global.h> #include <FramelessHelper/Widgets/framelesshelperwidgets_global.h>
#include <QtWidgets/qpushbutton.h> #include <QtWidgets/qabstractbutton.h>
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardSystemButtonPrivate; class StandardSystemButtonPrivate;
class FRAMELESSHELPER_WIDGETS_API StandardSystemButton : public QPushButton class FRAMELESSHELPER_WIDGETS_API StandardSystemButton : public QAbstractButton
{ {
Q_OBJECT Q_OBJECT
Q_DECLARE_PRIVATE(StandardSystemButton) Q_DECLARE_PRIVATE(StandardSystemButton)
Q_DISABLE_COPY_MOVE(StandardSystemButton) Q_DISABLE_COPY_MOVE(StandardSystemButton)
Q_PROPERTY(Global::SystemButtonType buttonType READ buttonType WRITE setButtonType NOTIFY buttonTypeChanged FINAL) Q_PROPERTY(Global::SystemButtonType buttonType READ buttonType WRITE setButtonType NOTIFY buttonTypeChanged FINAL)
Q_PROPERTY(QString glyph READ glyph WRITE setGlyph NOTIFY glyphChanged FINAL) Q_PROPERTY(QString glyph READ glyph WRITE setGlyph NOTIFY glyphChanged FINAL)
Q_PROPERTY(bool hovered READ isHovered WRITE setHovered NOTIFY hoveredChanged FINAL)
Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL)
Q_PROPERTY(QColor hoverColor READ hoverColor WRITE setHoverColor NOTIFY hoverColorChanged FINAL) Q_PROPERTY(QColor hoverColor READ hoverColor WRITE setHoverColor NOTIFY hoverColorChanged FINAL)
Q_PROPERTY(QColor pressColor READ pressColor WRITE setPressColor NOTIFY pressColorChanged FINAL) Q_PROPERTY(QColor pressColor READ pressColor WRITE setPressColor NOTIFY pressColorChanged FINAL)
Q_PROPERTY(QColor normalColor READ normalColor WRITE setNormalColor NOTIFY normalColorChanged FINAL) Q_PROPERTY(QColor normalColor READ normalColor WRITE setNormalColor NOTIFY normalColorChanged FINAL)
@ -54,6 +56,8 @@ public:
Q_NODISCARD QSize sizeHint() const override; Q_NODISCARD QSize sizeHint() const override;
Q_NODISCARD Global::SystemButtonType buttonType(); Q_NODISCARD Global::SystemButtonType buttonType();
Q_NODISCARD QString glyph() const; Q_NODISCARD QString glyph() const;
Q_NODISCARD bool isHovered() const;
Q_NODISCARD bool isPressed() const;
Q_NODISCARD QColor hoverColor() const; Q_NODISCARD QColor hoverColor() const;
Q_NODISCARD QColor pressColor() const; Q_NODISCARD QColor pressColor() const;
Q_NODISCARD QColor normalColor() const; Q_NODISCARD QColor normalColor() const;
@ -65,6 +69,8 @@ public:
public Q_SLOTS: public Q_SLOTS:
void setButtonType(const Global::SystemButtonType value); void setButtonType(const Global::SystemButtonType value);
void setGlyph(const QString &glyph); void setGlyph(const QString &glyph);
void setHovered(const bool value);
void setPressed(const bool value);
void setHoverColor(const QColor &value); void setHoverColor(const QColor &value);
void setPressColor(const QColor &value); void setPressColor(const QColor &value);
void setNormalColor(const QColor &value); void setNormalColor(const QColor &value);
@ -74,11 +80,15 @@ public Q_SLOTS:
void setIconSize2(const int value); void setIconSize2(const int value);
protected: protected:
void enterEvent(QT_ENTER_EVENT_TYPE *event) override;
void leaveEvent(QEvent *event) override;
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
Q_SIGNALS: Q_SIGNALS:
void buttonTypeChanged(); void buttonTypeChanged();
void glyphChanged(); void glyphChanged();
void hoveredChanged();
void pressedChanged();
void hoverColorChanged(); void hoverColorChanged();
void pressColorChanged(); void pressColorChanged();
void normalColorChanged(); void normalColorChanged();

View File

@ -86,7 +86,15 @@ struct ImageData
QMutex mutex{}; QMutex mutex{};
}; };
struct MetricsData
{
std::optional<QSize> monitorSize = std::nullopt;
std::optional<QSize> wallpaperSize = std::nullopt;
QMutex mutex{};
};
Q_GLOBAL_STATIC(ImageData, g_imageData) Q_GLOBAL_STATIC(ImageData, g_imageData)
Q_GLOBAL_STATIC(MetricsData, g_metricsData)
#ifndef FRAMELESSHELPER_CORE_NO_PRIVATE #ifndef FRAMELESSHELPER_CORE_NO_PRIVATE
template<const int shift> template<const int shift>
@ -492,11 +500,20 @@ public:
~WallpaperThread() override = default; ~WallpaperThread() override = default;
Q_SIGNALS: Q_SIGNALS:
void imageUpdated(); void imageUpdated(const Transform &);
protected: protected:
void run() override void run() override
{ {
Transform transform = {};
const QSize monitorSize = MicaMaterialPrivate::monitorSize();
const QSize imageSize = MicaMaterialPrivate::wallpaperSize();
// If we scaled the image size, record the scale factor and we need it to map our clip rect
// to the real (unscaled) rect.
if (imageSize != monitorSize) {
transform.Horizontal = (qreal(imageSize.width()) / qreal(monitorSize.width()));
transform.Vertical = (qreal(imageSize.height()) / qreal(monitorSize.height()));
}
const QString wallpaperFilePath = Utils::getWallpaperFilePath(); const QString wallpaperFilePath = Utils::getWallpaperFilePath();
if (wallpaperFilePath.isEmpty()) { if (wallpaperFilePath.isEmpty()) {
WARNING << "Failed to retrieve the wallpaper file path."; WARNING << "Failed to retrieve the wallpaper file path.";
@ -529,8 +546,7 @@ protected:
return; return;
} }
WallpaperAspectStyle aspectStyle = Utils::getWallpaperAspectStyle(); WallpaperAspectStyle aspectStyle = Utils::getWallpaperAspectStyle();
const QSize wallpaperSize = QGuiApplication::primaryScreen()->size(); QImage buffer(imageSize, kDefaultImageFormat);
QImage buffer(wallpaperSize, kDefaultImageFormat);
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
if (aspectStyle == WallpaperAspectStyle::Center) { if (aspectStyle == WallpaperAspectStyle::Center) {
buffer.fill(kDefaultBlackColor); buffer.fill(kDefaultBlackColor);
@ -546,11 +562,11 @@ protected:
mode = Qt::KeepAspectRatio; mode = Qt::KeepAspectRatio;
} }
QSize newSize = image.size(); QSize newSize = image.size();
newSize.scale(wallpaperSize, mode); newSize.scale(imageSize, mode);
image = image.scaled(newSize); image = image.scaled(newSize);
} }
static constexpr const QPoint desktopOriginPoint = {0, 0}; static constexpr const QPoint desktopOriginPoint = {0, 0};
const QRect desktopRect = {desktopOriginPoint, wallpaperSize}; const QRect desktopRect = {desktopOriginPoint, imageSize};
if (aspectStyle == WallpaperAspectStyle::Tile) { if (aspectStyle == WallpaperAspectStyle::Tile) {
QPainter bufferPainter(&buffer); QPainter bufferPainter(&buffer);
// Same as above, we prefer speed than quality here. // Same as above, we prefer speed than quality here.
@ -569,7 +585,7 @@ protected:
} }
{ {
const QMutexLocker locker(&g_imageData()->mutex); const QMutexLocker locker(&g_imageData()->mutex);
g_imageData()->blurredWallpaper = QPixmap(wallpaperSize); g_imageData()->blurredWallpaper = QPixmap(imageSize);
g_imageData()->blurredWallpaper.fill(kDefaultTransparentColor); g_imageData()->blurredWallpaper.fill(kDefaultTransparentColor);
QPainter painter(&g_imageData()->blurredWallpaper); QPainter painter(&g_imageData()->blurredWallpaper);
// Same here. // Same here.
@ -582,7 +598,7 @@ protected:
qt_blurImage(&painter, buffer, kDefaultBlurRadius, false, false); qt_blurImage(&painter, buffer, kDefaultBlurRadius, false, false);
#endif // FRAMELESSHELPER_CORE_NO_PRIVATE #endif // FRAMELESSHELPER_CORE_NO_PRIVATE
} }
Q_EMIT imageUpdated(); Q_EMIT imageUpdated(transform);
} }
}; };
@ -688,7 +704,7 @@ void MicaMaterialPrivate::paint(QPainter *painter, const QRect &rect, const bool
} }
prepareGraphicsResources(); prepareGraphicsResources();
static constexpr const QPoint originPoint = {0, 0}; static constexpr const QPoint originPoint = {0, 0};
const QRect wallpaperRect = { originPoint, wallpaperSize }; const QRect wallpaperRect = { originPoint, wallpaperSize() };
const QRect mappedRect = mapToWallpaper(rect); const QRect mappedRect = mapToWallpaper(rect);
painter->save(); painter->save();
// Same as above. Speed is more important here. // Same as above. Speed is more important here.
@ -746,7 +762,10 @@ void MicaMaterialPrivate::paint(QPainter *painter, const QRect &rect, const bool
void MicaMaterialPrivate::forceRebuildWallpaper() void MicaMaterialPrivate::forceRebuildWallpaper()
{ {
wallpaperSize = QGuiApplication::primaryScreen()->size(); g_metricsData()->mutex.lock();
g_metricsData()->monitorSize = std::nullopt;
g_metricsData()->wallpaperSize = std::nullopt;
g_metricsData()->mutex.unlock();
maybeGenerateBlurredWallpaper(true); maybeGenerateBlurredWallpaper(true);
} }
@ -757,7 +776,8 @@ void MicaMaterialPrivate::initialize()
g_threadData()->thread = std::make_unique<WallpaperThread>(); g_threadData()->thread = std::make_unique<WallpaperThread>();
qAddPostRoutine(threadCleaner); qAddPostRoutine(threadCleaner);
} }
connect(g_threadData()->thread.get(), &WallpaperThread::imageUpdated, this, [this](){ connect(g_threadData()->thread.get(), &WallpaperThread::imageUpdated, this, [this](const Transform &t){
transform = t;
if (initialized) { if (initialized) {
Q_Q(MicaMaterial); Q_Q(MicaMaterial);
Q_EMIT q->shouldRedraw(); Q_EMIT q->shouldRedraw();
@ -765,8 +785,6 @@ void MicaMaterialPrivate::initialize()
}); });
g_threadData()->mutex.unlock(); g_threadData()->mutex.unlock();
wallpaperSize = QGuiApplication::primaryScreen()->size();
tintColor = kDefaultTransparentColor; tintColor = kDefaultTransparentColor;
tintOpacity = kDefaultTintOpacity; tintOpacity = kDefaultTintOpacity;
// Leave fallbackColor invalid, we need to use this state to judge // Leave fallbackColor invalid, we need to use this state to judge
@ -806,24 +824,76 @@ QColor MicaMaterialPrivate::systemFallbackColor()
return ((FramelessManager::instance()->systemTheme() == SystemTheme::Dark) ? kDefaultFallbackColorDark : kDefaultFallbackColorLight); return ((FramelessManager::instance()->systemTheme() == SystemTheme::Dark) ? kDefaultFallbackColorDark : kDefaultFallbackColorLight);
} }
QSize MicaMaterialPrivate::monitorSize()
{
g_metricsData()->mutex.lock();
if (!g_metricsData()->monitorSize.has_value()) {
g_metricsData()->mutex.unlock();
const QScreen * const monitor = QGuiApplication::primaryScreen();
Q_ASSERT(monitor);
// We do not use the virtual desktop size here, instead, we only calculate the primary
// monitor size to simplify the logic, otherwise we will need a lot more code to take
// every case into account.
QSize size = (monitor ? monitor->size() : kMaximumPictureSize);
if (Q_UNLIKELY(size.isEmpty())) {
WARNING << "Failed to retrieve the monitor size. Using default size (1920x1080) instead ...";
size = kMaximumPictureSize;
}
g_metricsData()->mutex.lock();
g_metricsData()->monitorSize = size;
// Don't unlock the mutex here, we'll unlock it from outside.
DEBUG << "Primary monitor size:" << size * (monitor ? monitor->devicePixelRatio() : qreal(1));
}
const QSize result = g_metricsData()->monitorSize.value();
g_metricsData()->mutex.unlock();
return result;
}
QSize MicaMaterialPrivate::wallpaperSize()
{
g_metricsData()->mutex.lock();
if (!g_metricsData()->wallpaperSize.has_value()) {
g_metricsData()->mutex.unlock();
const QSize desktopSize = monitorSize();
// It's observed that QImage consumes too much memory if the image resolution is very large.
const QSize size = (desktopSize > kMaximumPictureSize ? kMaximumPictureSize : desktopSize);
g_metricsData()->mutex.lock();
g_metricsData()->wallpaperSize = size;
// Don't unlock the mutex here, we'll unlock it from outside.
DEBUG << "Wallpaper size:" << size;
}
const QSize result = g_metricsData()->wallpaperSize.value();
g_metricsData()->mutex.unlock();
return result;
}
QPoint MicaMaterialPrivate::mapToWallpaper(const QPoint &pos) const QPoint MicaMaterialPrivate::mapToWallpaper(const QPoint &pos) const
{ {
if (pos.isNull()) { if (pos.isNull()) {
return {}; return {};
} }
QPointF result = pos; QPointF result = pos;
if (!qFuzzyIsNull(transform.Horizontal) && (transform.Horizontal > qreal(0))
&& !qFuzzyCompare(transform.Horizontal, qreal(1))) {
result.setX(result.x() * transform.Horizontal);
}
if (!qFuzzyIsNull(transform.Vertical) && (transform.Vertical > qreal(0))
&& !qFuzzyCompare(transform.Vertical, qreal(1))) {
result.setY(result.y() * transform.Vertical);
}
const QSizeF imageSize = wallpaperSize();
// Make sure the position is always inside the wallpaper rectangle. // Make sure the position is always inside the wallpaper rectangle.
while (result.x() < qreal(0)) { while (result.x() < qreal(0)) {
result.setX(result.x() + wallpaperSize.width()); result.setX(result.x() + imageSize.width());
} }
while ((result.x() > wallpaperSize.width()) || qFuzzyCompare(result.x(), wallpaperSize.width())) { while ((result.x() > imageSize.width()) || qFuzzyCompare(result.x(), imageSize.width())) {
result.setX(result.x() - wallpaperSize.width()); result.setX(result.x() - imageSize.width());
} }
while (result.y() < qreal(0)) { while (result.y() < qreal(0)) {
result.setY(result.y() + wallpaperSize.height()); result.setY(result.y() + imageSize.height());
} }
while ((result.y() > wallpaperSize.height()) || qFuzzyCompare(result.y(), wallpaperSize.height())) { while ((result.y() > imageSize.height()) || qFuzzyCompare(result.y(), imageSize.height())) {
result.setY(result.y() - wallpaperSize.height()); result.setY(result.y() - imageSize.height());
} }
return result.toPoint(); return result.toPoint();
} }
@ -834,19 +904,28 @@ QSize MicaMaterialPrivate::mapToWallpaper(const QSize &size) const
return {}; return {};
} }
QSizeF result = size; QSizeF result = size;
// Make sure we don't get a size larger than the wallpaper's size. if (!qFuzzyIsNull(transform.Horizontal) && (transform.Horizontal > qreal(0))
if (result.width() > wallpaperSize.width()) { && !qFuzzyCompare(transform.Horizontal, qreal(1))) {
result.setWidth(wallpaperSize.width()); result.setWidth(result.width() * transform.Horizontal);
} }
if (result.height() > wallpaperSize.height()) { if (!qFuzzyIsNull(transform.Vertical) && (transform.Vertical > qreal(0))
result.setHeight(wallpaperSize.height()); && !qFuzzyCompare(transform.Vertical, qreal(1))) {
result.setHeight(result.height() * transform.Vertical);
}
const QSizeF imageSize = wallpaperSize();
// Make sure we don't get a size larger than the wallpaper's size.
if (result.width() > imageSize.width()) {
result.setWidth(imageSize.width());
}
if (result.height() > imageSize.height()) {
result.setHeight(imageSize.height());
} }
return result.toSize(); return result.toSize();
} }
QRect MicaMaterialPrivate::mapToWallpaper(const QRect &rect) const QRect MicaMaterialPrivate::mapToWallpaper(const QRect &rect) const
{ {
const auto wallpaperRect = QRectF{ QPointF{ 0, 0 }, wallpaperSize }; const auto wallpaperRect = QRectF{ QPointF{ 0, 0 }, wallpaperSize() };
const auto mappedRect = QRectF{ mapToWallpaper(rect.topLeft()), mapToWallpaper(rect.size()) }; const auto mappedRect = QRectF{ mapToWallpaper(rect.topLeft()), mapToWallpaper(rect.size()) };
if (!Utils::isValidGeometry(mappedRect)) { if (!Utils::isValidGeometry(mappedRect)) {
WARNING << "The calculated mapped rectangle is not valid."; WARNING << "The calculated mapped rectangle is not valid.";

View File

@ -664,17 +664,11 @@ void Utils::emulateQtMouseEvent(const QObject *target, const QWindow *window, co
#endif #endif
QCoreApplication::sendEvent(obj, &enterEvent); QCoreApplication::sendEvent(obj, &enterEvent);
if (hoverEnabled) { if (hoverEnabled) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QHoverEvent hoverEnterEvent(QEvent::HoverEnter, scenePos, globalPos, oldPos, modifiers); QHoverEvent hoverEnterEvent(QEvent::HoverEnter, scenePos, globalPos, oldPos, modifiers);
#elif (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
QHoverEvent hoverEnterEvent(QEvent::HoverEnter, localPos, globalPos, oldPos, modifiers);
#else
QHoverEvent hoverEnterEvent(QEvent::HoverEnter, localPos, oldPos, modifiers);
#endif
QCoreApplication::sendEvent(obj, &hoverEnterEvent); QCoreApplication::sendEvent(obj, &hoverEnterEvent);
} }
}; };
const auto sendLeaveEvent = [&localPos, &scenePos, &globalPos, &modifiers, hoverEnabled](QObject *obj) -> void { const auto sendLeaveEvent = [&scenePos, &globalPos, &modifiers, hoverEnabled](QObject *obj) -> void {
Q_ASSERT(obj); Q_ASSERT(obj);
if (!obj) { if (!obj) {
return; return;
@ -682,13 +676,7 @@ void Utils::emulateQtMouseEvent(const QObject *target, const QWindow *window, co
QEvent leaveEvent(QEvent::Leave); QEvent leaveEvent(QEvent::Leave);
QCoreApplication::sendEvent(obj, &leaveEvent); QCoreApplication::sendEvent(obj, &leaveEvent);
if (hoverEnabled) { if (hoverEnabled) {
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QHoverEvent hoverLeaveEvent(QEvent::HoverLeave, scenePos, globalPos, oldPos, modifiers); QHoverEvent hoverLeaveEvent(QEvent::HoverLeave, scenePos, globalPos, oldPos, modifiers);
#elif (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
QHoverEvent hoverLeaveEvent(QEvent::HoverLeave, localPos, globalPos, oldPos, modifiers);
#else
QHoverEvent hoverLeaveEvent(QEvent::HoverLeave, localPos, oldPos, modifiers);
#endif
QCoreApplication::sendEvent(obj, &hoverLeaveEvent); QCoreApplication::sendEvent(obj, &hoverLeaveEvent);
} }
}; };
@ -701,7 +689,7 @@ void Utils::emulateQtMouseEvent(const QObject *target, const QWindow *window, co
QMouseEvent event(QEvent::MouseMove, localPos, scenePos, globalPos, actualButton, buttons, modifiers); QMouseEvent event(QEvent::MouseMove, localPos, scenePos, globalPos, actualButton, buttons, modifiers);
QCoreApplication::sendEvent(obj, &event); QCoreApplication::sendEvent(obj, &event);
}; };
const auto sendHoverMoveEvent = [&localPos, &scenePos, &globalPos, &modifiers, hoverEnabled](QObject *obj) -> void { const auto sendHoverMoveEvent = [&scenePos, &globalPos, &modifiers, hoverEnabled](QObject *obj) -> void {
Q_ASSERT(obj); Q_ASSERT(obj);
if (!obj) { if (!obj) {
return; return;
@ -709,13 +697,7 @@ void Utils::emulateQtMouseEvent(const QObject *target, const QWindow *window, co
if (!hoverEnabled) { if (!hoverEnabled) {
return; return;
} }
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
QHoverEvent event(QEvent::HoverMove, scenePos, globalPos, oldPos, modifiers); QHoverEvent event(QEvent::HoverMove, scenePos, globalPos, oldPos, modifiers);
#elif (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
QHoverEvent event(QEvent::HoverMove, localPos, globalPos, oldPos, modifiers);
#else
QHoverEvent event(QEvent::HoverMove, localPos, oldPos, modifiers);
#endif
QCoreApplication::sendEvent(obj, &event); QCoreApplication::sendEvent(obj, &event);
}; };
const auto sendMousePressEvent = [&localPos, &scenePos, &globalPos, &buttons, &modifiers](QObject *obj) -> void { const auto sendMousePressEvent = [&localPos, &scenePos, &globalPos, &buttons, &modifiers](QObject *obj) -> void {
@ -731,18 +713,18 @@ void Utils::emulateQtMouseEvent(const QObject *target, const QWindow *window, co
if (!obj) { if (!obj) {
return; return;
} }
QMouseEvent event(QEvent::MouseButtonRelease, localPos, scenePos, globalPos, button, buttons, modifiers); static constexpr const auto fakePos = QPoint{ -999, -999 };
const QPoint tweakedLocalPos = (fake ? fakePos : localPos);
const QPoint tweakedScenePos = (fake ? fakePos : scenePos);
const QPoint tweakedGlobalPos = (fake ? fakePos : globalPos);
QMouseEvent event(QEvent::MouseButtonRelease, tweakedLocalPos, tweakedScenePos, tweakedGlobalPos, button, buttons, modifiers);
QCoreApplication::sendEvent(obj, &event); QCoreApplication::sendEvent(obj, &event);
}; };
switch (buttonState) { switch (buttonState) {
case ButtonState::Normal: { case ButtonState::Normal: {
targetObj->setProperty(kEnteredFlag, {}); targetObj->setProperty(kEnteredFlag, {});
// Send an extra mouse release event to let the control dismiss it's hover state. // Send an extra mouse release event to let the control dismiss it's hover state.
// But only when the mouse is not still hovering above the control, otherwise Qt will sendMouseReleaseEvent(targetObj, true);
// generate a mouse click event, which is not what the user would expect.
if (!underMouse) {
sendMouseReleaseEvent(targetObj);
}
if (isWidget) { if (isWidget) {
sendLeaveEvent(targetObj); sendLeaveEvent(targetObj);
} else { } else {

View File

@ -124,6 +124,16 @@ QSize StandardSystemButtonPrivate::getRecommendedButtonSize() const
return kDefaultSystemButtonSize; return kDefaultSystemButtonSize;
} }
bool StandardSystemButtonPrivate::isHovered() const
{
return m_hovered;
}
bool StandardSystemButtonPrivate::isPressed() const
{
return m_pressed;
}
QColor StandardSystemButtonPrivate::getHoverColor() const QColor StandardSystemButtonPrivate::getHoverColor() const
{ {
return m_hoverColor; return m_hoverColor;
@ -159,6 +169,55 @@ int StandardSystemButtonPrivate::iconSize2() const
return m_iconSize2.value_or(FramelessManagerPrivate::getIconFont().pointSize()); return m_iconSize2.value_or(FramelessManagerPrivate::getIconFont().pointSize());
} }
void StandardSystemButtonPrivate::setHovered(const bool value)
{
if (m_hovered == value) {
return;
}
m_hovered = value;
Q_Q(StandardSystemButton);
q->update();
#if 0
if (m_hovered) {
const QString toolTip = q->toolTip();
if (!toolTip.isEmpty() && !QToolTip::isVisible()) {
const auto yPos = [q]() -> int {
static const int h = kDefaultSystemButtonSize.height();
if (const QWidget * const window = q->window()) {
if (Utils::windowStatesToWindowState(window->windowState()) == Qt::WindowMaximized) {
return std::round(qreal(h) * qreal(0.5));
}
}
return -std::round(qreal(h) * qreal(1.3));
}();
QToolTip::showText(q->mapToGlobal(QPoint(-2, yPos)), toolTip, q, q->geometry());
}
} else {
if (QToolTip::isVisible()) {
QToolTip::hideText();
}
}
#endif
Q_EMIT q->hoveredChanged();
}
void StandardSystemButtonPrivate::setPressed(const bool value)
{
if (m_pressed == value) {
return;
}
m_pressed = value;
Q_Q(StandardSystemButton);
q->setDown(m_pressed);
q->update();
Q_EMIT q->pressedChanged();
if (m_pressed) {
Q_EMIT q->pressed();
} else {
Q_EMIT q->released();
}
}
void StandardSystemButtonPrivate::setHoverColor(const QColor &value) void StandardSystemButtonPrivate::setHoverColor(const QColor &value)
{ {
Q_ASSERT(value.isValid()); Q_ASSERT(value.isValid());
@ -260,6 +319,26 @@ void StandardSystemButtonPrivate::setIconSize2(const int value)
Q_EMIT q->iconSize2Changed(); Q_EMIT q->iconSize2Changed();
} }
void StandardSystemButtonPrivate::enterEventHandler(QT_ENTER_EVENT_TYPE *event)
{
Q_ASSERT(event);
if (!event) {
return;
}
setHovered(true);
event->accept();
}
void StandardSystemButtonPrivate::leaveEventHandler(QEvent *event)
{
Q_ASSERT(event);
if (!event) {
return;
}
setHovered(false);
event->accept();
}
void StandardSystemButtonPrivate::paintEventHandler(QPaintEvent *event) void StandardSystemButtonPrivate::paintEventHandler(QPaintEvent *event)
{ {
Q_ASSERT(event); Q_ASSERT(event);
@ -271,12 +350,12 @@ void StandardSystemButtonPrivate::paintEventHandler(QPaintEvent *event)
painter.save(); painter.save();
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing
| QPainter::SmoothPixmapTransform); | QPainter::SmoothPixmapTransform);
const auto backgroundColor = [this, q]() -> QColor { const auto backgroundColor = [this]() -> QColor {
// The pressed state has higher priority than the hovered state. // The pressed state has higher priority than the hovered state.
if (q->isDown() && m_pressColor.isValid()) { if (m_pressed && m_pressColor.isValid()) {
return m_pressColor; return m_pressColor;
} }
if (q->underMouse() && m_hoverColor.isValid()) { if (m_hovered && m_hoverColor.isValid()) {
return m_hoverColor; return m_hoverColor;
} }
if (m_normalColor.isValid()) { if (m_normalColor.isValid()) {
@ -289,8 +368,8 @@ void StandardSystemButtonPrivate::paintEventHandler(QPaintEvent *event)
painter.fillRect(buttonRect, backgroundColor); painter.fillRect(buttonRect, backgroundColor);
} }
if (!m_glyph.isEmpty()) { if (!m_glyph.isEmpty()) {
painter.setPen([this, q]() -> QColor { painter.setPen([this]() -> QColor {
if (!q->underMouse() && !m_active && m_inactiveForegroundColor.isValid()) { if (!m_hovered && !m_active && m_inactiveForegroundColor.isValid()) {
return m_inactiveForegroundColor; return m_inactiveForegroundColor;
} }
if (m_activeForegroundColor.isValid()) { if (m_activeForegroundColor.isValid()) {
@ -318,10 +397,12 @@ void StandardSystemButtonPrivate::initialize()
q->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); q->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
q->setFixedSize(kDefaultSystemButtonSize); q->setFixedSize(kDefaultSystemButtonSize);
q->setIconSize(kDefaultSystemButtonIconSize); q->setIconSize(kDefaultSystemButtonIconSize);
connect(q, &StandardSystemButton::pressed, this, [this](){ setPressed(true); });
connect(q, &StandardSystemButton::released, this, [this](){ setPressed(false); });
} }
StandardSystemButton::StandardSystemButton(QWidget *parent) StandardSystemButton::StandardSystemButton(QWidget *parent)
: QPushButton(parent), d_ptr(new StandardSystemButtonPrivate(this)) : QAbstractButton(parent), d_ptr(new StandardSystemButtonPrivate(this))
{ {
} }
@ -363,6 +444,30 @@ void StandardSystemButton::setGlyph(const QString &glyph)
d->setGlyph(glyph); d->setGlyph(glyph);
} }
bool StandardSystemButton::isHovered() const
{
Q_D(const StandardSystemButton);
return d->isHovered();
}
void StandardSystemButton::setHovered(const bool value)
{
Q_D(StandardSystemButton);
d->setHovered(value);
}
bool StandardSystemButton::isPressed() const
{
Q_D(const StandardSystemButton);
return d->isPressed();
}
void StandardSystemButton::setPressed(const bool value)
{
Q_D(StandardSystemButton);
d->setPressed(value);
}
QColor StandardSystemButton::hoverColor() const QColor StandardSystemButton::hoverColor() const
{ {
Q_D(const StandardSystemButton); Q_D(const StandardSystemButton);
@ -447,6 +552,20 @@ void StandardSystemButton::setIconSize2(const int value)
d->setIconSize2(value); d->setIconSize2(value);
} }
void StandardSystemButton::enterEvent(QT_ENTER_EVENT_TYPE *event)
{
QAbstractButton::enterEvent(event);
Q_D(StandardSystemButton);
d->enterEventHandler(event);
}
void StandardSystemButton::leaveEvent(QEvent *event)
{
QAbstractButton::leaveEvent(event);
Q_D(StandardSystemButton);
d->leaveEventHandler(event);
}
void StandardSystemButton::paintEvent(QPaintEvent *event) void StandardSystemButton::paintEvent(QPaintEvent *event)
{ {
Q_D(StandardSystemButton); Q_D(StandardSystemButton);