From 3871bfc5d3aff45e498fa2944c27e6eb5d146c8e Mon Sep 17 00:00:00 2001
From: SineStriker <trueful@163.com>
Date: 周三, 20 12月 2023 19:56:32 +0800
Subject: [PATCH] Add mac hot-switch implementations

---
 src/core/contexts/cocoawindowcontext.mm  |    9 +
 src/core/windowagentbase.h               |    3 
 examples/mainwindow/mainwindow.h         |   12 ++
 src/core/contexts/qtwindowcontext.cpp    |   15 ++
 examples/mainwindow/mainwindow.qrc       |    1 
 src/core/windowagentbase.cpp             |    4 
 examples/mainwindow/light-style.qss      |  154 ++++++++++++++++++++++++++++++
 examples/mainwindow/mainwindow.cpp       |   47 ++++++++-
 README.md                                |    2 
 src/core/contexts/win32windowcontext.cpp |   17 ++-
 10 files changed, 251 insertions(+), 13 deletions(-)

diff --git a/README.md b/README.md
index 3951ff2..7572d09 100644
--- a/README.md
+++ b/README.md
@@ -129,7 +129,7 @@
 Let `WidgetWindowAgent` know which widget the title bar is.
 
 ```c++
-agent->setTitleBarWidget(myTitleBar);
+agent->setTitleBar(myTitleBar);
 ```
 
 Next, set system button hints to let `WidgetWindowAgent` know the role of the child widgets, which is important for the Snap Layout to work.
diff --git a/examples/mainwindow/light-style.qss b/examples/mainwindow/light-style.qss
new file mode 100644
index 0000000..b2b7287
--- /dev/null
+++ b/examples/mainwindow/light-style.qss
@@ -0,0 +1,154 @@
+/* Window bar */
+
+QWK--WindowBar[bar-active=true] {
+    background-color: #195ABE;
+}
+
+QWK--WindowBar[bar-active=false] {
+    background-color: #195ABE;
+}
+
+
+/* Title label */
+
+QWK--WindowBar>QLabel#win-title-label {
+    padding: 0;
+    border: none;
+    color: #ECECEC;
+    background-color: transparent;
+    min-height: 28px;
+}
+
+
+/* System buttons */
+
+QWK--WindowBar>QAbstractButton[system-button=true] {
+    qproperty-iconSize: 12px 12px;
+    min-width: 50px;
+    border: none;
+    padding: 0;
+    background-color: transparent;
+}
+
+QWK--WindowBar>QAbstractButton#min-button {
+    qproperty-iconNormal: url(":/window-bar/minimize.svg");
+    qproperty-iconSize: 12px 12px;
+}
+
+QWK--WindowBar>QAbstractButton#min-button:hover,
+QWK--WindowBar>QAbstractButton#min-button:pressed {
+    background-color: rgba(0, 0, 0, 15%);
+}
+
+QWK--WindowBar>QAbstractButton#max-button {
+    qproperty-iconNormal: url(":/window-bar/maximize.svg");
+    qproperty-iconChecked: url(":/window-bar/restore.svg");
+}
+
+QWK--WindowBar>QAbstractButton#max-button:hover,
+QWK--WindowBar>QAbstractButton#max-button:pressed {
+    background-color: rgba(0, 0, 0, 15%);
+}
+
+QWK--WindowBar>QAbstractButton#close-button {
+    qproperty-iconNormal: url(":/window-bar/close.svg");
+}
+
+QWK--WindowBar>QAbstractButton#close-button:hover,
+QWK--WindowBar>QAbstractButton#close-button:pressed {
+    background-color: #e81123;
+}
+
+
+/* Icon button */
+
+QWK--WindowBar>QAbstractButton#icon-button {
+    qproperty-iconNormal: url(":/app/example.png");
+    qproperty-iconSize: 18px 18px;
+    min-width: 40px;
+    border: none;
+    padding: 0;
+    background-color: transparent;
+}
+
+
+/* Menu Bar */
+
+QMenuBar {
+    background-color: transparent;
+    border: none;
+}
+
+QMenuBar>QToolButton#qt_menubar_ext_button {
+    qproperty-icon: url(":/window-bar/more-line.svg");
+}
+
+QMenuBar>QToolButton#qt_menubar_ext_button:hover,
+QMenuBar>QToolButton#qt_menubar_ext_button:pressed {
+    background-color: rgba(255, 255, 255, 10%);
+}
+
+QMenuBar::item {
+    color: #EEEEEE;
+    border: none;
+    padding: 8px 12px;
+}
+
+QMenuBar::item:selected {
+    background-color: rgba(255, 255, 255, 10%);
+}
+
+
+/* Menu */
+
+QMenu {
+    padding: 4px;
+    background: #303030;
+    border: 1.25px solid transparent;
+}
+
+QMenu::indicator {
+    left: 6px;
+    width: 20px;
+    height: 20px;
+}
+
+QMenu::icon {
+    left: 6px;
+}
+
+QMenu::item {
+    background: transparent;
+    color: #CCCCCC;
+    padding: 6px 24px;
+}
+
+QMenu::item:selected {
+    color: white;
+    background-color: #0060C0;
+}
+
+QMenu::item:disabled {
+    color: #666666;
+    background-color: transparent;
+}
+
+QMenu::separator {
+    height: 2px;
+    background-color: #5B5B5B;
+    margin: 6px 0;
+}
+
+
+/* Window */
+
+MainWindow {
+    background-color: #F3F3F3;
+}
+
+QWidget#clock-widget {
+    font-size: 75px;
+    color: #333333;
+    font-weight: bold;
+    background-color: transparent;
+}
\ No newline at end of file
diff --git a/examples/mainwindow/mainwindow.cpp b/examples/mainwindow/mainwindow.cpp
index e0df17e..e289c3e 100644
--- a/examples/mainwindow/mainwindow.cpp
+++ b/examples/mainwindow/mainwindow.cpp
@@ -36,10 +36,7 @@
     clockWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
     setCentralWidget(clockWidget);
 
-    if (QFile qss(QStringLiteral(":/dark-style.qss"));
-        qss.open(QIODevice::ReadOnly | QIODevice::Text)) {
-        setStyleSheet(QString::fromUtf8(qss.readAll()));
-    }
+    loadStyleSheet(Dark);
 
     setWindowTitle(tr("Example MainWindow"));
     resize(800, 600);
@@ -111,18 +108,46 @@
     agent->setup(this);
 
     // 2. Construct your title bar
-    auto menuBar = []() {
+    auto menuBar = [this, agent]() {
         auto menuBar = new QMenuBar();
+
+        // Virtual menu
         auto file = new QMenu(tr("File(&F)"), menuBar);
         file->addAction(new QAction(tr("New(&N)"), menuBar));
         file->addAction(new QAction(tr("Open(&O)"), menuBar));
+        file->addSeparator();
 
         auto edit = new QMenu(tr("Edit(&E)"), menuBar);
         edit->addAction(new QAction(tr("Undo(&U)"), menuBar));
         edit->addAction(new QAction(tr("Redo(&R)"), menuBar));
 
+        // Theme action
+        auto darkAction = new QAction(tr("Dark Theme"), menuBar);
+        darkAction->setCheckable(true);
+        connect(darkAction, &QAction::triggered, this, [this](bool checked) {
+            loadStyleSheet(checked ? Dark : Light); //
+        });
+        connect(this, &MainWindow::themeChanged, darkAction, [this, darkAction]() {
+            darkAction->setChecked(currentTheme == Dark); //
+        });
+
+        // Agent action
+        auto framelessOnAction = new QAction(tr("Enable Frameless"), menuBar);
+        framelessOnAction->setCheckable(true);
+        framelessOnAction->setChecked(true);
+        connect(framelessOnAction, &QAction::triggered, agent, &QWK::WindowAgentBase::setEnabled);
+        connect(agent, &QWK::WindowAgentBase::enabledChanged, framelessOnAction,
+                &QAction::setChecked);
+
+        // Real menu
+        auto settings = new QMenu(tr("Settings(&S)"), menuBar);
+        settings->addAction(darkAction);
+        settings->addSeparator();
+        settings->addAction(framelessOnAction);
+
         menuBar->addMenu(file);
         menuBar->addMenu(edit);
+        menuBar->addMenu(settings);
         return menuBar;
     }();
     menuBar->setObjectName(QStringLiteral("win-menu-bar"));
@@ -211,3 +236,15 @@
     connect(windowBar, &QWK::WindowBar::closeRequested, this, &QWidget::close);
 #endif
 }
+
+void MainWindow::loadStyleSheet(Theme theme) {
+    if (!styleSheet().isEmpty() && theme == currentTheme)
+        return;
+    currentTheme = theme;
+    if (QFile qss(theme == Dark ? QStringLiteral(":/dark-style.qss")
+                                : QStringLiteral(":/light-style.qss"));
+        qss.open(QIODevice::ReadOnly | QIODevice::Text)) {
+        setStyleSheet(QString::fromUtf8(qss.readAll()));
+        Q_EMIT themeChanged();
+    }
+}
\ No newline at end of file
diff --git a/examples/mainwindow/mainwindow.h b/examples/mainwindow/mainwindow.h
index cfed3a6..5511105 100644
--- a/examples/mainwindow/mainwindow.h
+++ b/examples/mainwindow/mainwindow.h
@@ -9,11 +9,23 @@
     explicit MainWindow(QWidget *parent = nullptr);
     ~MainWindow() override;
 
+    enum Theme {
+        Dark,
+        Light,
+    };
+    Q_ENUM(Theme)
+
+Q_SIGNALS:
+    void themeChanged();
+
 protected:
     bool event(QEvent *event) override;
 
 private:
     void installWindowAgent();
+    void loadStyleSheet(Theme theme);
+
+    Theme currentTheme{};
 };
 
 #endif // MAINWINDOW_H
diff --git a/examples/mainwindow/mainwindow.qrc b/examples/mainwindow/mainwindow.qrc
index 03d122b..af208ee 100644
--- a/examples/mainwindow/mainwindow.qrc
+++ b/examples/mainwindow/mainwindow.qrc
@@ -1,5 +1,6 @@
 <RCC>
     <qresource prefix="/">
         <file>dark-style.qss</file>
+        <file>light-style.qss</file>
     </qresource>
 </RCC>
\ No newline at end of file
diff --git a/src/core/contexts/cocoawindowcontext.mm b/src/core/contexts/cocoawindowcontext.mm
index 79ad85e..c16c5a2 100644
--- a/src/core/contexts/cocoawindowcontext.mm
+++ b/src/core/contexts/cocoawindowcontext.mm
@@ -404,11 +404,18 @@
     }
 
     void CocoaWindowContext::winIdChanged(QWindow *oldWindow, bool isDestroyed) {
-        releaseWindowProxy(windowId);
+        // If the original window id is valid, remove all resources related
+        if (windowId) {
+            releaseWindowProxy(windowId);
+            windowId = 0;
+            cocoaWindowEventFilter.reset();
+        }
+
         if (!m_windowHandle) {
             return;
         }
 
+        // Allocate new resources
         windowId = m_windowHandle->winId();
         ensureWindowProxy(windowId)->setSystemTitleBarVisible(false);
         cocoaWindowEventFilter = std::make_unique<CocoaWindowEventFilter>(this, this);
diff --git a/src/core/contexts/qtwindowcontext.cpp b/src/core/contexts/qtwindowcontext.cpp
index 7941b56..d0abb6d 100644
--- a/src/core/contexts/qtwindowcontext.cpp
+++ b/src/core/contexts/qtwindowcontext.cpp
@@ -248,7 +248,20 @@
     }
 
     void QtWindowContext::winIdChanged(QWindow *oldWindow, bool isDestroyed) {
-        Q_UNUSED(oldWindow)
+        Q_UNUSED(isDestroyed)
+
+        // If the original window id is valid, remove all resources related
+        if (oldWindow) {
+            qtWindowEventFilter.reset();
+        }
+
+        if (!m_windowHandle) {
+            m_delegate->setWindowFlags(m_host, m_delegate->getWindowFlags(m_host) &
+                                                   ~Qt::FramelessWindowHint);
+            return;
+        }
+
+        // Allocate new resources
         m_delegate->setWindowFlags(m_host,
                                    m_delegate->getWindowFlags(m_host) | Qt::FramelessWindowHint);
         qtWindowEventFilter = std::make_unique<QtWindowEventFilter>(this);
diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
index 3b2d3e8..40fda7c 100644
--- a/src/core/contexts/win32windowcontext.cpp
+++ b/src/core/contexts/win32windowcontext.cpp
@@ -117,7 +117,7 @@
     private:
         DynamicApis() {
 #define DYNAMIC_API_RESOLVE(DLL, NAME)                                                             \
-    p##NAME = reinterpret_cast<decltype(p##NAME)>(DLL.resolve(#NAME))
+  p##NAME = reinterpret_cast<decltype(p##NAME)>(DLL.resolve(#NAME))
 
             QSystemLibrary user32(QStringLiteral("user32"));
             DYNAMIC_API_RESOLVE(user32, GetDpiForWindow);
@@ -985,11 +985,18 @@
     }
 
     void Win32WindowContext::winIdChanged(QWindow *oldWindow, bool isDestroyed) {
-        if (isDestroyed) {
-            removeManagedWindow(reinterpret_cast<HWND>(windowId));
-        } else {
-            removeManagedWindow<false>(reinterpret_cast<HWND>(windowId));
+        Q_UNUSED(isDestroyed)
+
+        // If the original window id is valid, remove all resources related
+        if (windowId) {
+            if (isDestroyed) {
+                removeManagedWindow(reinterpret_cast<HWND>(windowId));
+            } else {
+                removeManagedWindow<false>(reinterpret_cast<HWND>(windowId));
+            }
+            windowId = 0;
         }
+
         if (!m_windowHandle) {
             return;
         }
diff --git a/src/core/windowagentbase.cpp b/src/core/windowagentbase.cpp
index 159d678..28c9945 100644
--- a/src/core/windowagentbase.cpp
+++ b/src/core/windowagentbase.cpp
@@ -67,7 +67,11 @@
 
     void WindowAgentBase::setEnabled(bool enabled) {
         Q_D(WindowAgentBase);
+        if (enabled == d->context->isEnabled()) {
+            return;
+        }
         d->context->setEnabled(enabled);
+        Q_EMIT enabledChanged(enabled);
     }
 
     void WindowAgentBase::showSystemMenu(const QPoint &pos) {
diff --git a/src/core/windowagentbase.h b/src/core/windowagentbase.h
index c252b9b..2524829 100644
--- a/src/core/windowagentbase.h
+++ b/src/core/windowagentbase.h
@@ -39,6 +39,9 @@
         void centralize();
         void raise();
 
+    Q_SIGNALS:
+        void enabledChanged(bool enabled);
+
     protected:
         explicit WindowAgentBase(WindowAgentBasePrivate &d, QObject *parent = nullptr);
 

--
Gitblit v1.9.1