Skip to content

Commit 76924a3

Browse files
committed
Optimize system button area APIs
1 parent f963da1 commit 76924a3

File tree

12 files changed

+167
-60
lines changed

12 files changed

+167
-60
lines changed

examples/mainwindow/mainwindow.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,13 @@ void MainWindow::installWindowAgent() {
289289
#endif
290290
windowAgent->setHitTestVisible(menuBar, true);
291291

292+
#ifdef Q_OS_MAC
293+
windowAgent->setSystemButtonAreaCallback([](const QSize &size) {
294+
static constexpr const int width = 75;
295+
return QRect(QPoint(size.width() - width, 0), QSize(width, size.height())); //
296+
});
297+
#endif
298+
292299
setMenuWidget(windowBar);
293300

294301
// 3. Adds simulated mouse events to the title bar buttons

src/core/contexts/abstractwindowcontext.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,9 @@ namespace QWK {
120120
}
121121

122122
#ifdef Q_OS_MAC
123-
void AbstractWindowContext::setSystemButtonArea(const QRect &rect) {
124-
m_systemButtonArea = rect;
123+
void
124+
AbstractWindowContext::setSystemButtonAreaCallback(const ScreenRectCallback &callback) {
125+
m_systemButtonAreaCallback = callback;
125126
virtual_hook(SystemButtonAreaChangedHook, nullptr);
126127
}
127128
#endif

src/core/contexts/abstractwindowcontext_p.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ namespace QWK {
5050
bool setTitleBar(QObject *obj);
5151

5252
#ifdef Q_OS_MAC
53-
inline QRect systemButtonArea() const;
54-
void setSystemButtonArea(const QRect &rect);
53+
inline ScreenRectCallback systemButtonAreaCallback() const;
54+
void setSystemButtonAreaCallback(const ScreenRectCallback &callback);
5555
#endif
5656

5757
bool isInSystemButtons(const QPoint &pos, WindowAgentBase::SystemButton *button) const;
@@ -88,7 +88,7 @@ namespace QWK {
8888

8989
QSet<const QObject *> m_hitTestVisibleItems;
9090
#ifdef Q_OS_MAC
91-
QRect m_systemButtonArea;
91+
ScreenRectCallback m_systemButtonAreaCallback;
9292
#endif
9393

9494
QObject *m_titleBar{};
@@ -126,8 +126,8 @@ namespace QWK {
126126
}
127127

128128
#ifdef Q_OS_MAC
129-
inline QRect AbstractWindowContext::systemButtonArea() const {
130-
return m_systemButtonArea;
129+
inline ScreenRectCallback AbstractWindowContext::systemButtonAreaCallback() const {
130+
return m_systemButtonAreaCallback;
131131
}
132132
#endif
133133

src/core/contexts/cocoawindowcontext.mm

Lines changed: 85 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
#include "qwkglobal_p.h"
1111
#include "systemwindow_p.h"
1212

13+
// https://forgetsou.github.io/2020/11/06/macos%E5%BC%80%E5%8F%91-%E5%85%B3%E9%97%AD-%E6%9C%80%E5%B0%8F%E5%8C%96-%E5%85%A8%E5%B1%8F%E5%B1%85%E4%B8%AD%E5%A4%84%E7%90%86(%E4%BB%BFMac%20QQ)/
14+
// https://nyrra33.com/2019/03/26/changing-titlebars-height/
15+
1316
namespace QWK {
1417

1518
struct NSWindowProxy;
@@ -24,12 +27,16 @@
2427

2528
struct QWK_NSWindowDelegate {
2629
public:
30+
enum NSEventType {
31+
WillEnterFullScreen,
32+
DidEnterFullScreen,
33+
WillExitFullScreen,
34+
DidExitFullScreen,
35+
DidResize,
36+
};
37+
2738
virtual ~QWK_NSWindowDelegate() = default;
28-
virtual void windowWillEnterFullScreen(){};
29-
virtual void windowDidEnterFullScreen(){};
30-
virtual void windowWillExitFullScreen(){};
31-
virtual void windowDidExitFullScreen(){};
32-
virtual void windowDidResize(){};
39+
virtual void windowEvent(NSEventType eventType) = 0;
3340
};
3441

3542
//
@@ -77,35 +84,40 @@ - (void)dealloc {
7784
- (void)windowWillEnterFullScreen:(NSNotification *)notification {
7885
auto nswindow = reinterpret_cast<NSWindow *>(notification.object);
7986
if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) {
80-
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowWillEnterFullScreen();
87+
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent(
88+
QWK_NSWindowDelegate::WillEnterFullScreen);
8189
}
8290
}
8391

8492
- (void)windowDidEnterFullScreen:(NSNotification *)notification {
8593
auto nswindow = reinterpret_cast<NSWindow *>(notification.object);
8694
if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) {
87-
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowDidEnterFullScreen();
95+
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent(
96+
QWK_NSWindowDelegate::DidEnterFullScreen);
8897
}
8998
}
9099

91100
- (void)windowWillExitFullScreen:(NSNotification *)notification {
92101
auto nswindow = reinterpret_cast<NSWindow *>(notification.object);
93102
if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) {
94-
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowWillExitFullScreen();
103+
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent(
104+
QWK_NSWindowDelegate::WillExitFullScreen);
95105
}
96106
}
97107

98108
- (void)windowDidExitFullScreen:(NSNotification *)notification {
99109
auto nswindow = reinterpret_cast<NSWindow *>(notification.object);
100110
if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) {
101-
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowDidExitFullScreen();
111+
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent(
112+
QWK_NSWindowDelegate::DidExitFullScreen);
102113
}
103114
}
104115

105116
- (void)windowDidResize:(NSNotification *)notification {
106117
auto nswindow = reinterpret_cast<NSWindow *>(notification.object);
107118
if (auto proxy = QWK::g_proxyIndexes->value(nswindow)) {
108-
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowDidResize();
119+
reinterpret_cast<QWK_NSWindowDelegate *>(proxy)->windowEvent(
120+
QWK_NSWindowDelegate::DidResize);
109121
}
110122
}
111123

@@ -134,38 +146,43 @@ - (void)windowDidResize:(NSNotification *)notification {
134146
}
135147

136148
// Delegate
137-
void windowWillEnterFullScreen() override {
138-
}
139-
140-
void windowDidEnterFullScreen() override {
141-
}
142-
143-
void windowWillExitFullScreen() override {
144-
if (systemButtonRect.isEmpty() || !systemButtonVisible)
145-
return;
149+
void windowEvent(NSEventType eventType) override {
150+
switch (eventType) {
151+
case WillExitFullScreen: {
152+
if (!screenRectCallback || !systemButtonVisible)
153+
return;
154+
155+
// The system buttons will stuck at their default positions when the
156+
// exit-fullscreen animation is running, we need to hide them until the
157+
// animation finishes
158+
for (const auto &button : systemButtons()) {
159+
button.hidden = true;
160+
}
161+
break;
162+
}
146163

147-
// The system buttons will stuck at their default positions when the exit-fullscreen
148-
// animation is running, we need to hide them until the animation finishes
149-
for (const auto &button : systemButtons()) {
150-
button.hidden = true;
151-
}
152-
}
164+
case DidExitFullScreen: {
165+
if (!screenRectCallback || !systemButtonVisible)
166+
return;
153167

154-
void windowDidExitFullScreen() override {
155-
if (systemButtonRect.isEmpty() || !systemButtonVisible)
156-
return;
168+
for (const auto &button : systemButtons()) {
169+
button.hidden = false;
170+
}
171+
updateSystemButtonRect();
172+
break;
173+
}
157174

158-
for (const auto &button : systemButtons()) {
159-
button.hidden = false;
160-
}
161-
updateSystemButtonRect();
162-
}
175+
case DidResize: {
176+
if (!screenRectCallback || !systemButtonVisible) {
177+
return;
178+
}
179+
updateSystemButtonRect();
180+
break;
181+
}
163182

164-
void windowDidResize() override {
165-
if (systemButtonRect.isEmpty() || !systemButtonVisible) {
166-
return;
183+
default:
184+
break;
167185
}
168-
updateSystemButtonRect();
169186
}
170187

171188
// System buttons visibility
@@ -175,35 +192,42 @@ void setSystemButtonVisible(bool visible) {
175192
button.hidden = !visible;
176193
}
177194

178-
if (systemButtonRect.isEmpty() || !visible) {
195+
if (!screenRectCallback || !visible) {
179196
return;
180197
}
181198
updateSystemButtonRect();
182199
}
183200

184201
// System buttons area
185-
void setSystemButtonRect(const QRect &rect) {
186-
systemButtonRect = rect;
202+
void setScreenRectCallback(const ScreenRectCallback &callback) {
203+
screenRectCallback = callback;
187204

188-
if (rect.isEmpty() || !systemButtonVisible) {
205+
if (!callback || !systemButtonVisible) {
189206
return;
190207
}
191208
updateSystemButtonRect();
192209
}
193210

194211
void updateSystemButtonRect() {
195-
// https://forgetsou.github.io/2020/11/06/macos%E5%BC%80%E5%8F%91-%E5%85%B3%E9%97%AD-%E6%9C%80%E5%B0%8F%E5%8C%96-%E5%85%A8%E5%B1%8F%E5%B1%85%E4%B8%AD%E5%A4%84%E7%90%86(%E4%BB%BFMac%20QQ)/
196-
197212
const auto &buttons = systemButtons();
198213
const auto &leftButton = buttons[0];
199214
const auto &midButton = buttons[1];
200215
const auto &rightButton = buttons[2];
201216

217+
auto titlebar = rightButton.superview;
218+
int titlebarHeight = titlebar.frame.size.height;
219+
202220
auto spacing = midButton.frame.origin.x - leftButton.frame.origin.x;
203221
auto width = midButton.frame.size.width;
204222
auto height = midButton.frame.size.height;
205223

206-
QPoint center = systemButtonRect.center();
224+
auto viewSize =
225+
nswindow.contentView ? nswindow.contentView.frame.size : nswindow.frame.size;
226+
QPoint center = screenRectCallback(QSize(viewSize.width, titlebarHeight)).center();
227+
228+
// The origin of the NSWindow coordinate system is in the lower left corner, we
229+
// do the necessary transformations
230+
center.ry() = titlebarHeight - center.y();
207231

208232
// Mid button
209233
NSPoint centerOrigin = {
@@ -234,6 +258,11 @@ void updateSystemButtonRect() {
234258
return {closeBtn, minimizeBtn, zoomBtn};
235259
}
236260

261+
inline int titleBarHeight() const {
262+
NSButton *closeBtn = [nswindow standardWindowButton:NSWindowCloseButton];
263+
return closeBtn.superview.frame.size.height;
264+
}
265+
237266
// Blur effect
238267
bool setBlurEffect(BlurMode mode) {
239268
static Class visualEffectViewClass = NSClassFromString(@"NSVisualEffectView");
@@ -293,7 +322,7 @@ void setSystemTitleBarVisible(const bool visible) {
293322
nswindow.titlebarAppearsTransparent = (visible ? NO : YES);
294323
nswindow.titleVisibility = (visible ? NSWindowTitleVisible : NSWindowTitleHidden);
295324
nswindow.hasShadow = YES;
296-
nswindow.showsToolbarButton = NO;
325+
// nswindow.showsToolbarButton = NO;
297326
nswindow.movableByWindowBackground = NO;
298327
nswindow.movable = NO; // This line causes the window in the wrong position when
299328
// become fullscreen.
@@ -437,7 +466,7 @@ static void sendEvent(id obj, SEL sel, NSEvent *event) {
437466
NSWindow *nswindow = nil;
438467

439468
bool systemButtonVisible = true;
440-
QRect systemButtonRect;
469+
ScreenRectCallback screenRectCallback;
441470

442471
static inline QWK_NSWindowObserver *windowObserver = nil;
443472

@@ -638,7 +667,7 @@ static inline void releaseWindowProxy(const WId windowId) {
638667
void CocoaWindowContext::virtual_hook(int id, void *data) {
639668
switch (id) {
640669
case SystemButtonAreaChangedHook: {
641-
ensureWindowProxy(windowId)->setSystemButtonRect(m_systemButtonArea);
670+
ensureWindowProxy(windowId)->setScreenRectCallback(m_systemButtonAreaCallback);
642671
return;
643672
}
644673

@@ -648,6 +677,15 @@ static inline void releaseWindowProxy(const WId windowId) {
648677
AbstractWindowContext::virtual_hook(id, data);
649678
}
650679

680+
QVariant CocoaWindowContext::windowAttribute(const QString &key) const {
681+
if (key == QStringLiteral("title-bar-height")) {
682+
if (!m_windowHandle)
683+
return 0;
684+
return ensureWindowProxy(windowId)->titleBarHeight();
685+
}
686+
return AbstractWindowContext::windowAttribute(key);
687+
}
688+
651689
void CocoaWindowContext::winIdChanged() {
652690
// If the original window id is valid, remove all resources related
653691
if (windowId) {

src/core/contexts/cocoawindowcontext_p.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ namespace QWK {
2323
QString key() const override;
2424
void virtual_hook(int id, void *data) override;
2525

26+
QVariant windowAttribute(const QString &key) const override;
27+
2628
protected:
2729
void winIdChanged() override;
2830
bool windowAttributeChanged(const QString &key, const QVariant &attribute,

src/core/qwkglobal.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef QWKGLOBAL_H
22
#define QWKGLOBAL_H
33

4+
#include <functional>
5+
46
#include <QtCore/QEvent>
57
#include <QtGui/QtEvents>
68

@@ -28,4 +30,10 @@ using QT_ENTER_EVENT_TYPE = QEvent;
2830
# define QWINDOWKIT_CONFIG(feature) ((1 / QWINDOWKIT_##feature) == 1)
2931
#endif
3032

33+
namespace QWK {
34+
35+
using ScreenRectCallback = std::function<QRect(const QSize &)>;
36+
37+
}
38+
3139
#endif // QWKGLOBAL_H

src/core/windowagentbase.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ namespace QWK {
102102
\li \c blur-effect: You can specify a string value, "dark" to enable dark mode, "light"
103103
to set enable mode, "none" to disable. You can also specify a boolean value,
104104
\c true to enable current theme mode, \c false to disable.
105+
\li \c title-bar-height: Returns the system title bar height, the system button display
106+
area will be limited to this height. (Readonly)
105107
*/
106108
bool WindowAgentBase::setWindowAttribute(const QString &key, const QVariant &attribute) {
107109
Q_D(WindowAgentBase);

src/quick/quickwindowagent.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ namespace QWK {
5959
if (!d->context->setTitleBar(item)) {
6060
return;
6161
}
62+
#ifdef Q_OS_MAC
63+
setSystemButtonArea(nullptr);
64+
#endif
6265
Q_EMIT titleBarWidgetChanged(item);
6366
}
6467

src/quick/quickwindowagent.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ namespace QWK {
3131
Q_INVOKABLE void setHitTestVisible(const QQuickItem *item, bool visible = true);
3232

3333
#ifdef Q_OS_MAC
34+
// The system button area APIs are experimental, may be changed in the future.
3435
Q_INVOKABLE QQuickItem *systemButtonArea() const;
3536
Q_INVOKABLE void setSystemButtonArea(QQuickItem *item);
37+
38+
Q_INVOKABLE ScreenRectCallback systemButtonAreaCallback() const;
39+
Q_INVOKABLE void setSystemButtonAreaCallback(const ScreenRectCallback &callback);
3640
#endif
3741

3842
Q_SIGNALS:

0 commit comments

Comments
 (0)