diff --git a/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h b/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h index 4d3002c..25684ff 100644 --- a/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h +++ b/include/FramelessHelper/Quick/private/quickstandardtitlebar_p.h @@ -50,6 +50,7 @@ class FRAMELESSHELPER_QUICK_API QuickStandardTitleBar : public QQuickRectangle Q_PROPERTY(QuickStandardSystemButton* maximizeButton READ maximizeButton CONSTANT FINAL) Q_PROPERTY(QuickStandardSystemButton* closeButton READ closeButton CONSTANT FINAL) Q_PROPERTY(bool extended READ isExtended WRITE setExtended NOTIFY extendedChanged FINAL) + Q_PROPERTY(bool useAlternativeBackground READ isUsingAlternativeBackground WRITE setUseAlternativeBackground NOTIFY useAlternativeBackgroundChanged FINAL) public: explicit QuickStandardTitleBar(QQuickItem *parent = nullptr); @@ -66,6 +67,9 @@ public: Q_NODISCARD bool isExtended() const; void setExtended(const bool value); + Q_NODISCARD bool isUsingAlternativeBackground() const; + void setUseAlternativeBackground(const bool value); + protected: void itemChange(const ItemChange change, const ItemChangeData &value) override; Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; @@ -82,6 +86,7 @@ private Q_SLOTS: Q_SIGNALS: void titleLabelAlignmentChanged(); void extendedChanged(); + void useAlternativeBackgroundChanged(); private: void initialize(); @@ -98,6 +103,7 @@ private: QMetaObject::Connection m_windowActiveChangeConnection = {}; QMetaObject::Connection m_windowTitleChangeConnection = {}; bool m_extended = false; + bool m_useAlternativeBackground = false; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/include/FramelessHelper/Widgets/private/standardtitlebar_p.h b/include/FramelessHelper/Widgets/private/standardtitlebar_p.h index 2ec7ec1..692ddb4 100644 --- a/include/FramelessHelper/Widgets/private/standardtitlebar_p.h +++ b/include/FramelessHelper/Widgets/private/standardtitlebar_p.h @@ -57,6 +57,9 @@ public: Q_NODISCARD bool isExtended() const; void setExtended(const bool value); + Q_NODISCARD bool isUsingAlternativeBackground() const; + void setUseAlternativeBackground(const bool value); + public Q_SLOTS: void updateMaximizeButton(); void updateTitleBarStyleSheet(); @@ -79,6 +82,7 @@ private: Qt::Alignment m_labelAlignment = {}; QSpacerItem *m_labelLeftStretch = nullptr; QSpacerItem *m_labelRightStretch = nullptr; + bool m_useAlternativeBackground = false; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/include/FramelessHelper/Widgets/standardtitlebar.h b/include/FramelessHelper/Widgets/standardtitlebar.h index 0cd3287..9f06cb1 100644 --- a/include/FramelessHelper/Widgets/standardtitlebar.h +++ b/include/FramelessHelper/Widgets/standardtitlebar.h @@ -44,6 +44,7 @@ class FRAMELESSHELPER_WIDGETS_API StandardTitleBar : public QWidget Q_PROPERTY(StandardSystemButton* maximizeButton READ maximizeButton CONSTANT FINAL) Q_PROPERTY(StandardSystemButton* closeButton READ closeButton CONSTANT FINAL) Q_PROPERTY(bool extended READ isExtended WRITE setExtended NOTIFY extendedChanged FINAL) + Q_PROPERTY(bool useAlternativeBackground READ isUsingAlternativeBackground WRITE setUseAlternativeBackground NOTIFY useAlternativeBackgroundChanged FINAL) public: explicit StandardTitleBar(QWidget *parent = nullptr); @@ -60,12 +61,16 @@ public: Q_NODISCARD bool isExtended() const; void setExtended(const bool value); + Q_NODISCARD bool isUsingAlternativeBackground() const; + void setUseAlternativeBackground(const bool value); + protected: void paintEvent(QPaintEvent *event) override; Q_SIGNALS: void extendedChanged(); void titleLabelAlignmentChanged(); + void useAlternativeBackgroundChanged(); private: QScopedPointer d_ptr; diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index a087234..935e781 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include "framelessmanager.h" #include "framelessmanager_p.h" @@ -45,7 +44,7 @@ struct Win32HelperData { SystemParameters params = {}; bool trackingMouse = false; - WId dragBarWindowId = 0; + WId fallbackTitleBarWindowId = 0; }; struct Win32Helper @@ -53,11 +52,13 @@ struct Win32Helper QMutex mutex; QScopedPointer nativeEventFilter; QHash data = {}; - QHash dragBarToParentWindowMapping = {}; + QHash fallbackTitleBarToParentWindowMapping = {}; }; Q_GLOBAL_STATIC(Win32Helper, g_win32Helper) +static constexpr const wchar_t FALLBACK_TITLEBAR_CLASS_NAME[] = L"FALLBACK_TITLEBAR_WINDOW_CLASS\0"; + FRAMELESSHELPER_BYTEARRAY_CONSTANT2(Win32MessageTypeName, "windows_generic_MSG") FRAMELESSHELPER_STRING_CONSTANT(MonitorFromWindow) FRAMELESSHELPER_STRING_CONSTANT(GetMonitorInfoW) @@ -83,7 +84,7 @@ FRAMELESSHELPER_STRING_CONSTANT(SetWindowPos) FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent) FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) -[[nodiscard]] static inline LRESULT CALLBACK DragBarWindowProc +[[nodiscard]] static inline LRESULT CALLBACK FallbackTitleBarWindowProc (const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) { Q_ASSERT(hWnd); @@ -92,11 +93,11 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) } const auto windowId = reinterpret_cast(hWnd); g_win32Helper()->mutex.lock(); - if (!g_win32Helper()->dragBarToParentWindowMapping.contains(windowId)) { + if (!g_win32Helper()->fallbackTitleBarToParentWindowMapping.contains(windowId)) { g_win32Helper()->mutex.unlock(); return DefWindowProcW(hWnd, uMsg, wParam, lParam); } - const WId parentWindowId = g_win32Helper()->dragBarToParentWindowMapping.value(windowId); + const WId parentWindowId = g_win32Helper()->fallbackTitleBarToParentWindowMapping.value(windowId); if (!g_win32Helper()->data.contains(parentWindowId)) { g_win32Helper()->mutex.unlock(); return DefWindowProcW(hWnd, uMsg, wParam, lParam); @@ -108,7 +109,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) // Hit-testing event should not be considered as a mouse event. const bool isMouseEvent = (((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST)) || ((uMsg >= WM_NCMOUSEMOVE) && (uMsg <= WM_NCXBUTTONDBLCLK))); - // We only use this drag bar window to activate the snap layouts feature, if the parent + // We only use this fallback title bar window to activate the snap layouts feature, if the parent // window is not resizable, the snap layouts feature should also be disabled at the same time, // hence forward everything to the parent window, we don't need to handle anything here. if (data.params.isWindowFixedSize()) { @@ -119,7 +120,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) } // Forward all mouse events to the parent window to let the controls inside // our homemade title bar still continue to work normally. But ignore these - // events in this drag bar window due to there are no controls in it. + // events in this fallback title bar window due to there are no controls in it. if (isMouseEvent) { SendMessageW(parentWindowHandle, uMsg, wParam, lParam); return 0; @@ -269,7 +270,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) // metrics instead of our own. case WM_NCLBUTTONDOWN: case WM_NCLBUTTONDBLCLK: { - // Manual handling for mouse clicks in the drag bar. If it's in a + // Manual handling for mouse clicks in the fallback title bar. If it's in a // caption button, then tell the title bar to "press" the button, which // should change its visual state. // @@ -304,7 +305,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) return 0; } case WM_NCLBUTTONUP: { - // Manual handling for mouse RELEASES in the drag bar. If it's in a + // Manual handling for mouse RELEASES in the fallback title bar. If it's in a // caption button, then manually handle what we'd expect for that button. // // If it's not in a caption button, then just forward the message along @@ -353,12 +354,12 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) return DefWindowProcW(hWnd, uMsg, wParam, lParam); } -[[nodiscard]] static inline bool resizeDragBarWindow - (const WId parentWindowId, const WId dragBarWindowId, const bool hide) +[[nodiscard]] static inline bool resizeFallbackTitleBarWindow + (const WId parentWindowId, const WId fallbackTitleBarWindowId, const bool hide) { Q_ASSERT(parentWindowId); - Q_ASSERT(dragBarWindowId); - if (!parentWindowId || !dragBarWindowId) { + Q_ASSERT(fallbackTitleBarWindowId); + if (!parentWindowId || !fallbackTitleBarWindowId) { return false; } const auto parentWindowHandle = reinterpret_cast(parentWindowId); @@ -368,15 +369,18 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) return false; } const int titleBarHeight = Utils::getTitleBarHeight(parentWindowId, true); - const auto dragBarWindowHandle = reinterpret_cast(dragBarWindowId); + const auto fallbackTitleBarWindowHandle = reinterpret_cast(fallbackTitleBarWindowId); const UINT flags = (SWP_NOACTIVATE | (hide ? SWP_HIDEWINDOW : SWP_SHOWWINDOW)); - // As you can see from the code, we only use the drag bar window to activate the + // As you can see from the code, we only use the fallback title bar window to activate the // snap layouts feature introduced in Windows 11. So you may wonder, why not just - // limit it to the rectangle of the three system buttons, instead of covering the + // limit it to the area of the three system buttons, instead of covering the // whole title bar area? Well, I've tried that solution already and unfortunately - // it doesn't work. Since our current solution works well, I have no plan to dig + // it doesn't work. And according to my experiment, it won't work either even if we + // only reduce the window width for some pixels. So we have to make it expand to the + // full width of the parent window to let it occupy the whole top area, and this time + // it finally works. Since our current solution works well, I have no interest in digging // into all the magic behind it. - if (SetWindowPos(dragBarWindowHandle, HWND_TOP, 0, 0, + if (SetWindowPos(fallbackTitleBarWindowHandle, HWND_TOP, 0, 0, parentWindowClientRect.right, titleBarHeight, flags) == FALSE) { qWarning() << Utils::getSystemErrorMessage(kSetWindowPos); return false; @@ -384,7 +388,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) return true; } -[[nodiscard]] static inline bool createDragBarWindow(const WId parentWindowId, const bool hide) +[[nodiscard]] static inline bool createFallbackTitleBarWindow(const WId parentWindowId, const bool hide) { Q_ASSERT(parentWindowId); if (!parentWindowId) { @@ -392,7 +396,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) } static const bool isWin10OrGreater = Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1507); if (!isWin10OrGreater) { - qWarning() << "The drag bar window is only supported on Windows 10 and onwards."; + qWarning() << "The fallback title bar window is only supported on Windows 10 and onwards."; return false; } const auto parentWindowHandle = reinterpret_cast(parentWindowId); @@ -402,49 +406,43 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) qWarning() << Utils::getSystemErrorMessage(kGetModuleHandleW); return false; } - static const QString dragBarWindowClassName = QUuid::createUuid().toString().toUpper(); - Q_ASSERT(!dragBarWindowClassName.isEmpty()); - if (dragBarWindowClassName.isEmpty()) { - qWarning() << "Failed to generate a new UUID."; - return false; - } - static const ATOM dragBarWindowClass = [instance]() -> ATOM { + static const ATOM fallbackTitleBarWindowClass = [instance]() -> ATOM { WNDCLASSEXW wcex; SecureZeroMemory(&wcex, sizeof(wcex)); wcex.cbSize = sizeof(wcex); wcex.style = (CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS); - wcex.lpszClassName = qUtf16Printable(dragBarWindowClassName); + wcex.lpszClassName = FALLBACK_TITLEBAR_CLASS_NAME; wcex.hbrBackground = static_cast(GetStockObject(BLACK_BRUSH)); wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW); - wcex.lpfnWndProc = DragBarWindowProc; + wcex.lpfnWndProc = FallbackTitleBarWindowProc; wcex.hInstance = instance; return RegisterClassExW(&wcex); }(); - Q_ASSERT(dragBarWindowClass); - if (!dragBarWindowClass) { + Q_ASSERT(fallbackTitleBarWindowClass); + if (!fallbackTitleBarWindowClass) { qWarning() << Utils::getSystemErrorMessage(kRegisterClassExW); return false; } - const HWND dragBarWindowHandle = CreateWindowExW((WS_EX_LAYERED | WS_EX_NOREDIRECTIONBITMAP), - qUtf16Printable(dragBarWindowClassName), nullptr, WS_CHILD, 0, 0, 0, 0, + const HWND fallbackTitleBarWindowHandle = CreateWindowExW((WS_EX_LAYERED | WS_EX_NOREDIRECTIONBITMAP), + FALLBACK_TITLEBAR_CLASS_NAME, nullptr, WS_CHILD, 0, 0, 0, 0, parentWindowHandle, nullptr, instance, nullptr); - Q_ASSERT(dragBarWindowHandle); - if (!dragBarWindowHandle) { + Q_ASSERT(fallbackTitleBarWindowHandle); + if (!fallbackTitleBarWindowHandle) { qWarning() << Utils::getSystemErrorMessage(kCreateWindowExW); return false; } - if (SetLayeredWindowAttributes(dragBarWindowHandle, 0, 255, LWA_ALPHA) == FALSE) { + if (SetLayeredWindowAttributes(fallbackTitleBarWindowHandle, 0, 255, LWA_ALPHA) == FALSE) { qWarning() << Utils::getSystemErrorMessage(kSetLayeredWindowAttributes); return false; } - const auto dragBarWindowId = reinterpret_cast(dragBarWindowHandle); - if (!resizeDragBarWindow(parentWindowId, dragBarWindowId, hide)) { - qWarning() << "Failed to re-position the drag bar window."; + const auto fallbackTitleBarWindowId = reinterpret_cast(fallbackTitleBarWindowHandle); + if (!resizeFallbackTitleBarWindow(parentWindowId, fallbackTitleBarWindowId, hide)) { + qWarning() << "Failed to re-position the fallback title bar window."; return false; } QMutexLocker locker(&g_win32Helper()->mutex); - g_win32Helper()->data[parentWindowId].dragBarWindowId = dragBarWindowId; - g_win32Helper()->dragBarToParentWindowMapping.insert(dragBarWindowId, parentWindowId); + g_win32Helper()->data[parentWindowId].fallbackTitleBarWindowId = fallbackTitleBarWindowId; + g_win32Helper()->fallbackTitleBarToParentWindowMapping.insert(fallbackTitleBarWindowId, parentWindowId); return true; } @@ -479,8 +477,8 @@ void FramelessHelperWin::addWindow(const SystemParameters ¶ms) if (isWin10OrGreater) { const FramelessConfig * const config = FramelessConfig::instance(); if (!config->isSet(Option::DisableWindowsSnapLayouts)) { - if (!createDragBarWindow(windowId, data.params.isWindowFixedSize())) { - qWarning() << "Failed to create the drag bar window."; + if (!createFallbackTitleBarWindow(windowId, data.params.isWindowFixedSize())) { + qWarning() << "Failed to create the fallback title bar window."; } } static const bool isWin10RS1OrGreater = Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1607); @@ -1058,14 +1056,14 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me } } static const bool isWin10OrGreater = Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1507); - if (isWin10OrGreater && data.dragBarWindowId) { + if (isWin10OrGreater && data.fallbackTitleBarWindowId) { switch (uMsg) { case WM_SIZE: // Sent to a window after its size has changed. case WM_DISPLAYCHANGE: // Sent to a window when the display resolution has changed. { const bool isFixedSize = data.params.isWindowFixedSize(); - if (!resizeDragBarWindow(windowId, data.dragBarWindowId, isFixedSize)) { - qWarning() << "Failed to re-position the drag bar window."; + if (!resizeFallbackTitleBarWindow(windowId, data.fallbackTitleBarWindowId, isFixedSize)) { + qWarning() << "Failed to re-position the fallback title bar window."; } } break; default: diff --git a/src/quick/quickstandardtitlebar.cpp b/src/quick/quickstandardtitlebar.cpp index 3f3fd71..1f0c0df 100644 --- a/src/quick/quickstandardtitlebar.cpp +++ b/src/quick/quickstandardtitlebar.cpp @@ -122,6 +122,20 @@ void QuickStandardTitleBar::setExtended(const bool value) Q_EMIT extendedChanged(); } +bool QuickStandardTitleBar::isUsingAlternativeBackground() const +{ + return m_useAlternativeBackground; +} + +void QuickStandardTitleBar::setUseAlternativeBackground(const bool value) +{ + if (m_useAlternativeBackground == value) { + return; + } + m_useAlternativeBackground = value; + Q_EMIT useAlternativeBackgroundChanged(); +} + void QuickStandardTitleBar::updateMaximizeButton() { const QQuickWindow * const w = window(); @@ -149,10 +163,12 @@ void QuickStandardTitleBar::updateTitleBarColor() return; } const bool active = w->isActive(); + const bool dark = Utils::shouldAppsUseDarkMode(); + const bool colorizedTitleBar = Utils::isTitleBarColorized(); QColor backgroundColor = {}; QColor foregroundColor = {}; if (active) { - if (Utils::isTitleBarColorized()) { + if (colorizedTitleBar) { #ifdef Q_OS_WINDOWS backgroundColor = Utils::getDwmColorizationColor(); #endif @@ -164,7 +180,7 @@ void QuickStandardTitleBar::updateTitleBarColor() #endif foregroundColor = kDefaultWhiteColor; } else { - if (Utils::shouldAppsUseDarkMode()) { + if (dark) { backgroundColor = kDefaultBlackColor; foregroundColor = kDefaultWhiteColor; } else { @@ -173,14 +189,16 @@ void QuickStandardTitleBar::updateTitleBarColor() } } } else { - if (Utils::shouldAppsUseDarkMode()) { + if (dark) { backgroundColor = kDefaultSystemDarkColor; } else { backgroundColor = kDefaultWhiteColor; } foregroundColor = kDefaultDarkGrayColor; } - setColor(backgroundColor); + if (!m_useAlternativeBackground) { + setColor(backgroundColor); + } m_windowTitleLabel->setColor(foregroundColor); m_minimizeButton->setInactive(!active); m_maximizeButton->setInactive(!active); diff --git a/src/widgets/standardtitlebar.cpp b/src/widgets/standardtitlebar.cpp index 809f6a3..f7b73c1 100644 --- a/src/widgets/standardtitlebar.cpp +++ b/src/widgets/standardtitlebar.cpp @@ -124,6 +124,21 @@ void StandardTitleBarPrivate::setExtended(const bool value) Q_EMIT q->extendedChanged(); } +bool StandardTitleBarPrivate::isUsingAlternativeBackground() const +{ + return m_useAlternativeBackground; +} + +void StandardTitleBarPrivate::setUseAlternativeBackground(const bool value) +{ + if (m_useAlternativeBackground == value) { + return; + } + m_useAlternativeBackground = value; + Q_Q(StandardTitleBar); + Q_EMIT q->useAlternativeBackgroundChanged(); +} + void StandardTitleBarPrivate::updateMaximizeButton() { const bool max = m_window->isMaximized(); @@ -168,9 +183,10 @@ void StandardTitleBarPrivate::updateTitleBarStyleSheet() StandardSystemButtonPrivate::get(m_minimizeButton.data())->setInactive(!active); StandardSystemButtonPrivate::get(m_maximizeButton.data())->setInactive(!active); StandardSystemButtonPrivate::get(m_closeButton.data())->setInactive(!active); - Q_Q(StandardTitleBar); - q->setStyleSheet(kStyleSheetBackgroundColorTemplate.arg(titleBarBackgroundColor.name())); - q->update(); + if (!m_useAlternativeBackground) { + Q_Q(StandardTitleBar); + q->setStyleSheet(kStyleSheetBackgroundColorTemplate.arg(titleBarBackgroundColor.name())); + } } void StandardTitleBarPrivate::retranslateUi() @@ -327,6 +343,18 @@ void StandardTitleBar::setExtended(const bool value) d->setExtended(value); } +bool StandardTitleBar::isUsingAlternativeBackground() const +{ + Q_D(const StandardTitleBar); + return d->isUsingAlternativeBackground(); +} + +void StandardTitleBar::setUseAlternativeBackground(const bool value) +{ + Q_D(StandardTitleBar); + d->setUseAlternativeBackground(value); +} + void StandardTitleBar::paintEvent(QPaintEvent *event) { Q_UNUSED(event);