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