From 0ed4cc6287fba3b8b42dc97e6ca465f193bdc3e4 Mon Sep 17 00:00:00 2001
From: bruvzg <7645683+bruvzg@users.noreply.github.com>
Date: Tue, 20 Sep 2022 11:12:49 +0300
Subject: [PATCH] [macOS] Add an option to align window buttons in "extend to
title" mode.
---
doc/classes/DisplayServer.xml | 9 ++
editor/editor_node.cpp | 1 +
platform/macos/SCsub | 1 +
platform/macos/display_server_macos.h | 3 +
platform/macos/display_server_macos.mm | 26 ++++++
platform/macos/godot_button_view.h | 51 +++++++++++
platform/macos/godot_button_view.mm | 112 ++++++++++++++++++++++++
platform/macos/godot_window_delegate.mm | 9 ++
servers/display_server.cpp | 1 +
servers/display_server.h | 3 +-
10 files changed, 215 insertions(+), 1 deletion(-)
create mode 100644 platform/macos/godot_button_view.h
create mode 100644 platform/macos/godot_button_view.mm
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index d22d64c2768..a1dee7ecc74 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -1336,6 +1336,15 @@
Depending on the platform and used renderer, the engine will fall back to [constant VSYNC_ENABLED], if the desired mode is not supported.
+
+
+
+
+
+ When [constant WINDOW_FLAG_EXTEND_TO_TITLE] flag is set, set offset to the center of the first titlebar button.
+ [b]Note:[/b] This flag is implemented on macOS.
+
+
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 6701fec5411..a2f2b6c625d 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -7528,6 +7528,7 @@ EditorNode::EditorNode() {
// Extend menu bar to window title.
if (can_expand) {
+ DisplayServer::get_singleton()->window_set_window_buttons_offset(Vector2i(menu_hb->get_minimum_size().y / 2, menu_hb->get_minimum_size().y / 2), DisplayServer::MAIN_WINDOW_ID);
DisplayServer::get_singleton()->window_set_flag(DisplayServer::WINDOW_FLAG_EXTEND_TO_TITLE, true, DisplayServer::MAIN_WINDOW_ID);
menu_hb->set_can_move_window(true);
}
diff --git a/platform/macos/SCsub b/platform/macos/SCsub
index bbd461fba95..7ffb80f70b5 100644
--- a/platform/macos/SCsub
+++ b/platform/macos/SCsub
@@ -12,6 +12,7 @@ files = [
"crash_handler_macos.mm",
"macos_terminal_logger.mm",
"display_server_macos.mm",
+ "godot_button_view.mm",
"godot_content_view.mm",
"godot_window_delegate.mm",
"godot_window.mm",
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index da377c91714..576e7aa9ca8 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -76,6 +76,7 @@ public:
id window_delegate;
id window_object;
id window_view;
+ id window_button_view;
Vector mpath;
@@ -84,6 +85,7 @@ public:
Size2i min_size;
Size2i max_size;
Size2i size;
+ Vector2i wb_offset = Vector2i(16, 16);
NSRect last_frame_rect;
@@ -391,6 +393,7 @@ public:
virtual bool window_maximize_on_title_dbl_click() const override;
virtual bool window_minimize_on_title_dbl_click() const override;
+ virtual void window_set_window_buttons_offset(const Vector2i &p_offset, WindowID p_window = MAIN_WINDOW_ID) override;
virtual Vector2i window_get_safe_title_margins(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual Point2i ime_get_selection() const override;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 05f89c70aab..2d67bcb44f6 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -30,6 +30,7 @@
#include "display_server_macos.h"
+#include "godot_button_view.h"
#include "godot_content_view.h"
#include "godot_menu_delegate.h"
#include "godot_menu_item.h"
@@ -2639,6 +2640,14 @@ bool DisplayServerMacOS::window_minimize_on_title_dbl_click() const {
return false;
}
+void DisplayServerMacOS::window_set_window_buttons_offset(const Vector2i &p_offset, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.wb_offset = p_offset;
+}
+
Vector2i DisplayServerMacOS::window_get_safe_title_margins(WindowID p_window) const {
_THREAD_SAFE_METHOD_
@@ -2682,14 +2691,31 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win
} break;
case WINDOW_FLAG_EXTEND_TO_TITLE: {
NSRect rect = [wd.window_object frame];
+ if (wd.window_button_view) {
+ [wd.window_button_view removeFromSuperview];
+ wd.window_button_view = nil;
+ }
if (p_enabled) {
[wd.window_object setTitlebarAppearsTransparent:YES];
[wd.window_object setTitleVisibility:NSWindowTitleHidden];
[wd.window_object setStyleMask:[wd.window_object styleMask] | NSWindowStyleMaskFullSizeContentView];
+
+ [[wd.window_object standardWindowButton:NSWindowZoomButton] setHidden:YES];
+ [[wd.window_object standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
+ [[wd.window_object standardWindowButton:NSWindowCloseButton] setHidden:YES];
+ float window_buttons_spacing = NSMinX([[wd.window_object standardWindowButton:NSWindowMiniaturizeButton] frame]) - NSMinX([[wd.window_object standardWindowButton:NSWindowCloseButton] frame]);
+
+ wd.window_button_view = [[GodotButtonView alloc] initWithFrame:NSZeroRect];
+ [wd.window_button_view initButtons:window_buttons_spacing offset:NSMakePoint(wd.wb_offset.x, wd.wb_offset.y)];
+ [wd.window_view addSubview:wd.window_button_view];
} else {
[wd.window_object setTitlebarAppearsTransparent:NO];
[wd.window_object setTitleVisibility:NSWindowTitleVisible];
[wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskFullSizeContentView];
+
+ [[wd.window_object standardWindowButton:NSWindowZoomButton] setHidden:NO];
+ [[wd.window_object standardWindowButton:NSWindowMiniaturizeButton] setHidden:NO];
+ [[wd.window_object standardWindowButton:NSWindowCloseButton] setHidden:NO];
}
[wd.window_object setFrame:rect display:YES];
} break;
diff --git a/platform/macos/godot_button_view.h b/platform/macos/godot_button_view.h
new file mode 100644
index 00000000000..e41910878de
--- /dev/null
+++ b/platform/macos/godot_button_view.h
@@ -0,0 +1,51 @@
+/*************************************************************************/
+/* godot_button_view.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOT_BUTTON_VIEW_H
+#define GODOT_BUTTON_VIEW_H
+
+#include "servers/display_server.h"
+
+#import
+#import
+
+@interface GodotButtonView : NSView {
+ NSTrackingArea *tracking_area;
+ NSPoint offset;
+ CGFloat spacing;
+ bool mouse_in_group;
+}
+
+- (void)initButtons:(CGFloat)button_spacing offset:(NSPoint)button_offset;
+- (void)displayButtons;
+
+@end
+
+#endif // GODOT_BUTTON_VIEW_H
diff --git a/platform/macos/godot_button_view.mm b/platform/macos/godot_button_view.mm
new file mode 100644
index 00000000000..ae04c02bd51
--- /dev/null
+++ b/platform/macos/godot_button_view.mm
@@ -0,0 +1,112 @@
+/*************************************************************************/
+/* godot_button_view.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "godot_button_view.h"
+
+@implementation GodotButtonView
+
+- (id)initWithFrame:(NSRect)frame {
+ self = [super initWithFrame:frame];
+
+ tracking_area = nil;
+ offset = NSMakePoint(8, 8);
+ spacing = 20;
+ mouse_in_group = false;
+
+ return self;
+}
+
+- (void)initButtons:(CGFloat)button_spacing offset:(NSPoint)button_offset {
+ spacing = button_spacing;
+
+ NSButton *close_button = [NSWindow standardWindowButton:NSWindowCloseButton forStyleMask:NSWindowStyleMaskTitled];
+ [close_button setFrameOrigin:NSMakePoint(0, 0)];
+ [self addSubview:close_button];
+
+ NSButton *miniaturize_button = [NSWindow standardWindowButton:NSWindowMiniaturizeButton forStyleMask:NSWindowStyleMaskTitled];
+ [miniaturize_button setFrameOrigin:NSMakePoint(spacing, 0)];
+ [self addSubview:miniaturize_button];
+
+ NSButton *zoom_button = [NSWindow standardWindowButton:NSWindowZoomButton forStyleMask:NSWindowStyleMaskTitled];
+ [zoom_button setFrameOrigin:NSMakePoint(spacing * 2, 0)];
+ [self addSubview:zoom_button];
+
+ offset.y = button_offset.y - zoom_button.frame.size.height / 2;
+ offset.x = button_offset.x - zoom_button.frame.size.width / 2;
+
+ [self setFrameSize:NSMakeSize(zoom_button.frame.origin.x + zoom_button.frame.size.width, zoom_button.frame.size.height)];
+ [self displayButtons];
+}
+
+- (void)viewDidMoveToWindow {
+ if (!self.window) {
+ return;
+ }
+
+ [self setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin];
+ [self setFrameOrigin:NSMakePoint(offset.x, self.window.frame.size.height - self.frame.size.height - offset.y)];
+}
+
+- (BOOL)_mouseInGroup:(NSButton *)button {
+ return mouse_in_group;
+}
+
+- (void)updateTrackingAreas {
+ if (tracking_area != nil) {
+ [self removeTrackingArea:tracking_area];
+ }
+
+ NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect;
+ tracking_area = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:options owner:self userInfo:nil];
+
+ [self addTrackingArea:tracking_area];
+}
+
+- (void)mouseEntered:(NSEvent *)event {
+ [super mouseEntered:event];
+
+ mouse_in_group = true;
+ [self displayButtons];
+}
+
+- (void)mouseExited:(NSEvent *)event {
+ [super mouseExited:event];
+
+ mouse_in_group = false;
+ [self displayButtons];
+}
+
+- (void)displayButtons {
+ for (NSView *subview in self.subviews) {
+ [subview setNeedsDisplay:YES];
+ }
+}
+
+@end
diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm
index e1034c99933..f7f16b1e0e5 100644
--- a/platform/macos/godot_window_delegate.mm
+++ b/platform/macos/godot_window_delegate.mm
@@ -31,6 +31,7 @@
#include "godot_window_delegate.h"
#include "display_server_macos.h"
+#include "godot_button_view.h"
@implementation GodotWindowDelegate
@@ -219,6 +220,10 @@
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
+ if (wd.window_button_view) {
+ [(GodotButtonView *)wd.window_button_view displayButtons];
+ }
+
if (ds->mouse_get_mode() == DisplayServer::MOUSE_MODE_CAPTURED) {
const NSRect content_rect = [wd.window_view frame];
NSRect point_in_window_rect = NSMakeRect(content_rect.size.width / 2, content_rect.size.height / 2, 0, 0);
@@ -241,6 +246,10 @@
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
+ if (wd.window_button_view) {
+ [(GodotButtonView *)wd.window_button_view displayButtons];
+ }
+
ds->release_pressed_events();
ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_FOCUS_OUT);
}
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index dda8e29b6a8..e1a1c71420b 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -678,6 +678,7 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("window_set_flag", "flag", "enabled", "window_id"), &DisplayServer::window_set_flag, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_get_flag", "flag", "window_id"), &DisplayServer::window_get_flag, DEFVAL(MAIN_WINDOW_ID));
+ ClassDB::bind_method(D_METHOD("window_set_window_buttons_offset", "offset", "window_id"), &DisplayServer::window_set_window_buttons_offset, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_get_safe_title_margins", "window_id"), &DisplayServer::window_get_safe_title_margins, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_request_attention", "window_id"), &DisplayServer::window_request_attention, DEFVAL(MAIN_WINDOW_ID));
diff --git a/servers/display_server.h b/servers/display_server.h
index ab4f9fc4990..42c254cd2f4 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -380,7 +380,8 @@ public:
virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) = 0;
- virtual Vector2i window_get_safe_title_margins(WindowID p_window = MAIN_WINDOW_ID) const { return Vector2i(); };
+ virtual void window_set_window_buttons_offset(const Vector2i &p_offset, WindowID p_window = MAIN_WINDOW_ID) {}
+ virtual Vector2i window_get_safe_title_margins(WindowID p_window = MAIN_WINDOW_ID) const { return Vector2i(); }
virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const = 0;