diff --git a/include/FramelessHelper/Widgets/framelesswidgetshelper.h b/include/FramelessHelper/Widgets/framelesswidgetshelper.h index 826b9ed..65ed1c5 100644 --- a/include/FramelessHelper/Widgets/framelesswidgetshelper.h +++ b/include/FramelessHelper/Widgets/framelesswidgetshelper.h @@ -60,6 +60,7 @@ public Q_SLOTS: void setTitleBarWidget(QWidget *widget); void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType); void setHitTestVisible(QWidget *widget, const bool visible = true); + void setHitTestVisible(const QRect &rect, const bool visible = true); void showSystemMenu(const QPoint &pos); void windowStartSystemMove2(const QPoint &pos); diff --git a/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h b/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h index 222d571..def5f81 100644 --- a/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h +++ b/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h @@ -52,6 +52,7 @@ public: void attachToWindow(); void setSystemButton(QWidget *widget, const Global::SystemButtonType buttonType); void setHitTestVisible(QWidget *widget, const bool visible = true); + void setHitTestVisible(const QRect &rect, const bool visible = true); void showSystemMenu(const QPoint &pos); void windowStartSystemMove2(const QPoint &pos); void windowStartSystemResize2(const Qt::Edges edges, const QPoint &pos); diff --git a/include/FramelessHelper/Widgets/private/standardsystembutton_p.h b/include/FramelessHelper/Widgets/private/standardsystembutton_p.h index 4fc440e..c8a6fb4 100644 --- a/include/FramelessHelper/Widgets/private/standardsystembutton_p.h +++ b/include/FramelessHelper/Widgets/private/standardsystembutton_p.h @@ -28,6 +28,7 @@ #include "standardsystembutton.h" #include #include +#include QT_BEGIN_NAMESPACE class QEnterEvent; @@ -65,6 +66,7 @@ public: Q_NODISCARD QColor getActiveForegroundColor() const; Q_NODISCARD QColor getInactiveForegroundColor() const; Q_NODISCARD bool isActive() const; + Q_NODISCARD int iconSize2() const; void setHovered(const bool value); void setPressed(const bool value); @@ -74,6 +76,7 @@ public: void setActiveForegroundColor(const QColor &value); void setInactiveForegroundColor(const QColor &value); void setActive(const bool value); + void setIconSize2(const int value); void enterEventHandler(QT_ENTER_EVENT_TYPE *event); void leaveEventHandler(QEvent *event); @@ -94,6 +97,7 @@ private: bool m_hovered = false; bool m_pressed = false; bool m_active = false; + std::optional m_iconSize2 = std::nullopt; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/include/FramelessHelper/Widgets/private/standardtitlebar_p.h b/include/FramelessHelper/Widgets/private/standardtitlebar_p.h index 58a8ea1..1501f04 100644 --- a/include/FramelessHelper/Widgets/private/standardtitlebar_p.h +++ b/include/FramelessHelper/Widgets/private/standardtitlebar_p.h @@ -77,6 +77,12 @@ public: Q_NODISCARD QFont titleFont() const; void setTitleFont(const QFont &value); + void mouseEventHandler(const QMouseEvent *event); + + Q_NODISCARD QRect windowIconRect() const; + Q_NODISCARD bool windowIconVisible_real() const; + Q_NODISCARD bool isInTitleBarIconArea(const QPoint &pos) const; + public Q_SLOTS: void updateMaximizeButton(); void updateTitleBarColor(); @@ -100,9 +106,10 @@ private: bool m_hideWhenClose = false; QScopedPointer m_chromePalette; bool m_titleLabelVisible = true; - QSize m_windowIconSize = {}; + std::optional m_windowIconSize = std::nullopt; bool m_windowIconVisible = false; std::optional m_titleFont = std::nullopt; + bool m_closeTriggered = false; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/include/FramelessHelper/Widgets/standardsystembutton.h b/include/FramelessHelper/Widgets/standardsystembutton.h index f2f6401..aaa9ae7 100644 --- a/include/FramelessHelper/Widgets/standardsystembutton.h +++ b/include/FramelessHelper/Widgets/standardsystembutton.h @@ -49,6 +49,7 @@ class FRAMELESSHELPER_WIDGETS_API StandardSystemButton : public QAbstractButton Q_PROPERTY(QColor activeForegroundColor READ activeForegroundColor WRITE setActiveForegroundColor NOTIFY activeForegroundColorChanged FINAL) Q_PROPERTY(QColor inactiveForegroundColor READ inactiveForegroundColor WRITE setInactiveForegroundColor NOTIFY inactiveForegroundColorChanged FINAL) Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged FINAL) + Q_PROPERTY(int iconSize2 READ iconSize2 WRITE setIconSize2 NOTIFY iconSize2Changed FINAL) public: explicit StandardSystemButton(QWidget *parent = nullptr); @@ -66,6 +67,7 @@ public: Q_NODISCARD QColor activeForegroundColor() const; Q_NODISCARD QColor inactiveForegroundColor() const; Q_NODISCARD bool isActive() const; + Q_NODISCARD int iconSize2() const; public Q_SLOTS: void setButtonType(const Global::SystemButtonType value); @@ -78,6 +80,7 @@ public Q_SLOTS: void setActiveForegroundColor(const QColor &value); void setInactiveForegroundColor(const QColor &value); void setActive(const bool value); + void setIconSize2(const int value); protected: void enterEvent(QT_ENTER_EVENT_TYPE *event) override; @@ -95,6 +98,7 @@ Q_SIGNALS: void activeForegroundColorChanged(); void inactiveForegroundColorChanged(); void activeChanged(); + void iconSize2Changed(); private: QScopedPointer d_ptr; diff --git a/include/FramelessHelper/Widgets/standardtitlebar.h b/include/FramelessHelper/Widgets/standardtitlebar.h index 63b1a01..69dfa34 100644 --- a/include/FramelessHelper/Widgets/standardtitlebar.h +++ b/include/FramelessHelper/Widgets/standardtitlebar.h @@ -86,6 +86,8 @@ public: protected: void paintEvent(QPaintEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; Q_SIGNALS: void extendedChanged(); diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index 5b5572b..2c50d53 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -63,6 +63,7 @@ struct WidgetsHelperData QPointer minimizeButton = nullptr; QPointer maximizeButton = nullptr; QPointer closeButton = nullptr; + QList hitTestVisibleRects = {}; }; struct WidgetsHelper @@ -310,6 +311,26 @@ void FramelessWidgetsHelperPrivate::setHitTestVisible(QWidget *widget, const boo } } +void FramelessWidgetsHelperPrivate::setHitTestVisible(const QRect &rect, const bool visible) +{ + Q_ASSERT(rect.isValid()); + if (!rect.isValid()) { + return; + } + const QMutexLocker locker(&g_widgetsHelper()->mutex); + WidgetsHelperData *data = getWindowDataMutable(); + if (!data) { + return; + } + const bool exists = data->hitTestVisibleRects.contains(rect); + if (visible && !exists) { + data->hitTestVisibleRects.append(rect); + } + if (!visible && exists) { + data->hitTestVisibleRects.removeAll(rect); + } +} + void FramelessWidgetsHelperPrivate::attachToWindow() { QWidget * const window = getWindow(); @@ -535,6 +556,13 @@ bool FramelessWidgetsHelperPrivate::isInTitleBarDraggableArea(const QPoint &pos) } } } + if (!data.hitTestVisibleRects.isEmpty()) { + for (auto &&rect : qAsConst(data.hitTestVisibleRects)) { + if (rect.isValid()) { + region -= rect; + } + } + } return region.contains(pos); } @@ -824,6 +852,16 @@ void FramelessWidgetsHelper::setHitTestVisible(QWidget *widget, const bool visib d->setHitTestVisible(widget, visible); } +void FramelessWidgetsHelper::setHitTestVisible(const QRect &rect, const bool visible) +{ + Q_ASSERT(rect.isValid()); + if (!rect.isValid()) { + return; + } + Q_D(FramelessWidgetsHelper); + d->setHitTestVisible(rect, visible); +} + void FramelessWidgetsHelper::showSystemMenu(const QPoint &pos) { Q_D(FramelessWidgetsHelper); diff --git a/src/widgets/standardsystembutton.cpp b/src/widgets/standardsystembutton.cpp index 618ed45..57f2a95 100644 --- a/src/widgets/standardsystembutton.cpp +++ b/src/widgets/standardsystembutton.cpp @@ -154,6 +154,11 @@ bool StandardSystemButtonPrivate::isActive() const return m_active; } +int StandardSystemButtonPrivate::iconSize2() const +{ + return m_iconSize2.value_or(FramelessManagerPrivate::getIconFont().pointSize()); +} + void StandardSystemButtonPrivate::setHovered(const bool value) { if (m_hovered == value) { @@ -287,6 +292,21 @@ void StandardSystemButtonPrivate::setActive(const bool value) Q_EMIT q->activeChanged(); } +void StandardSystemButtonPrivate::setIconSize2(const int value) +{ + Q_ASSERT(value > 0); + if (value <= 0) { + return; + } + if (iconSize2() == value) { + return; + } + m_iconSize2 = value; + Q_Q(StandardSystemButton); + q->update(); + Q_EMIT q->iconSize2Changed(); +} + void StandardSystemButtonPrivate::enterEventHandler(QT_ENTER_EVENT_TYPE *event) { Q_ASSERT(event); @@ -343,7 +363,13 @@ void StandardSystemButtonPrivate::paintEventHandler(QPaintEvent *event) } return kDefaultBlackColor; }()); - painter.setFont(FramelessManagerPrivate::getIconFont()); + painter.setFont([this]() -> QFont { + QFont font = FramelessManagerPrivate::getIconFont(); + if (m_iconSize2.has_value()) { + font.setPointSize(m_iconSize2.value()); + } + return font; + }()); painter.drawText(buttonRect, Qt::AlignCenter, m_code); } painter.restore(); @@ -360,11 +386,13 @@ void StandardSystemButtonPrivate::initialize() connect(q, &StandardSystemButton::released, this, [this](){ setPressed(false); }); } -StandardSystemButton::StandardSystemButton(QWidget *parent) : QAbstractButton(parent), d_ptr(new StandardSystemButtonPrivate(this)) +StandardSystemButton::StandardSystemButton(QWidget *parent) + : QAbstractButton(parent), d_ptr(new StandardSystemButtonPrivate(this)) { } -StandardSystemButton::StandardSystemButton(const SystemButtonType type, QWidget *parent) : StandardSystemButton(parent) +StandardSystemButton::StandardSystemButton(const SystemButtonType type, QWidget *parent) + : StandardSystemButton(parent) { setButtonType(type); } @@ -467,6 +495,12 @@ bool StandardSystemButton::isActive() const return d->isActive(); } +int StandardSystemButton::iconSize2() const +{ + Q_D(const StandardSystemButton); + return d->iconSize2(); +} + void StandardSystemButton::setPressColor(const QColor &value) { Q_D(StandardSystemButton); @@ -497,6 +531,12 @@ void StandardSystemButton::setActive(const bool value) d->setActive(value); } +void StandardSystemButton::setIconSize2(const int value) +{ + Q_D(StandardSystemButton); + d->setIconSize2(value); +} + void StandardSystemButton::enterEvent(QT_ENTER_EVENT_TYPE *event) { QAbstractButton::enterEvent(event); diff --git a/src/widgets/standardtitlebar.cpp b/src/widgets/standardtitlebar.cpp index 0b3e35e..70fe903 100644 --- a/src/widgets/standardtitlebar.cpp +++ b/src/widgets/standardtitlebar.cpp @@ -25,8 +25,11 @@ #include "standardtitlebar.h" #include "standardtitlebar_p.h" #include "standardsystembutton.h" +#include "framelesswidgetshelper.h" #include +#include #include +#include #include FRAMELESSHELPER_BEGIN_NAMESPACE @@ -144,9 +147,7 @@ void StandardTitleBarPrivate::paintTitleBar(QPaintEvent *event) if (m_windowIconVisible) { const QIcon icon = m_window->windowIcon(); if (!icon.isNull()) { - const QSize size = (m_windowIconSize.isEmpty() ? kDefaultWindowIconSize : m_windowIconSize); - const int y = qRound(qreal(q->height() - size.height()) / qreal(2)); - const QRect rect = {QPoint(kDefaultTitleBarContentsMargin, y), size}; + const QRect rect = windowIconRect(); titleLabelLeftOffset = (rect.left() + rect.width()); icon.paint(&painter, rect); } @@ -200,7 +201,7 @@ void StandardTitleBarPrivate::setTitleLabelVisible(const bool value) QSize StandardTitleBarPrivate::windowIconSize() const { - return m_windowIconSize; + return m_windowIconSize.value_or(kDefaultWindowIconSize); } void StandardTitleBarPrivate::setWindowIconSize(const QSize &value) @@ -209,7 +210,7 @@ void StandardTitleBarPrivate::setWindowIconSize(const QSize &value) if (value.isEmpty()) { return; } - if (m_windowIconSize == value) { + if (windowIconSize() == value) { return; } m_windowIconSize = value; @@ -229,6 +230,7 @@ void StandardTitleBarPrivate::setWindowIconVisible(const bool value) return; } m_windowIconVisible = value; + FramelessWidgetsHelper::get(m_window)->setHitTestVisible(windowIconRect(), windowIconVisible_real()); Q_Q(StandardTitleBar); q->update(); Q_EMIT q->windowIconVisibleChanged(); @@ -250,6 +252,82 @@ void StandardTitleBarPrivate::setTitleFont(const QFont &value) Q_EMIT q->titleFontChanged(); } +void StandardTitleBarPrivate::mouseEventHandler(const QMouseEvent *event) +{ + Q_ASSERT(event); + if (!event) { + return; + } + if (!m_window) { + return; + } + Q_Q(const StandardTitleBar); + const Qt::MouseButton button = event->button(); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + const QPoint scenePos = event->scenePosition().toPoint(); +#else + const QPoint scenePos = event->windowPos().toPoint(); +#endif + const bool interestArea = isInTitleBarIconArea(scenePos); + switch (event->type()) { + case QEvent::MouseButtonRelease: + if (interestArea) { + // Sadly the mouse release events are always triggered before the + // mouse double click events, and if we intercept the mouse release + // events here, we'll never get the double click events afterwards, + // so we have to wait for a little bit to make sure the double + // click events are handled first, before we actually handle the + // mouse release events here. + // We need a copy of the "scenePos" variable here, otherwise it will + // soon fall out of scope when the lambda function actually runs. + QTimer::singleShot(150, this, [this, button, q, scenePos](){ + // The close event is already triggered, don't try to show the + // system menu anymore, otherwise it will prevent our window + // from closing. + if (m_closeTriggered) { + return; + } + FramelessWidgetsHelper::get(m_window)->showSystemMenu([button, q, &scenePos]() -> QPoint { + if (button == Qt::LeftButton) { + return {0, q->height()}; + } + return scenePos; + }()); + }); + } + break; + case QEvent::MouseButtonDblClick: + if ((button == Qt::LeftButton) && interestArea) { + m_closeTriggered = true; + m_window->close(); + } + break; + default: + break; + } +} + +QRect StandardTitleBarPrivate::windowIconRect() const +{ + Q_Q(const StandardTitleBar); + const QSize size = windowIconSize(); + const int y = qRound(qreal(q->height() - size.height()) / qreal(2)); + return {QPoint(kDefaultTitleBarContentsMargin, y), size}; +} + +bool StandardTitleBarPrivate::windowIconVisible_real() const +{ + return (m_windowIconVisible && !m_window->windowIcon().isNull()); +} + +bool StandardTitleBarPrivate::isInTitleBarIconArea(const QPoint &pos) const +{ + if (!windowIconVisible_real()) { + return false; + } + return windowIconRect().contains(pos); +} + void StandardTitleBarPrivate::updateMaximizeButton() { const bool max = m_window->isMaximized(); @@ -515,4 +593,18 @@ void StandardTitleBar::paintEvent(QPaintEvent *event) d->paintTitleBar(event); } +void StandardTitleBar::mouseReleaseEvent(QMouseEvent *event) +{ + QWidget::mouseReleaseEvent(event); + Q_D(StandardTitleBar); + d->mouseEventHandler(event); +} + +void StandardTitleBar::mouseDoubleClickEvent(QMouseEvent *event) +{ + QWidget::mouseDoubleClickEvent(event); + Q_D(StandardTitleBar); + d->mouseEventHandler(event); +} + FRAMELESSHELPER_END_NAMESPACE