Browse Source

Merge pull request #66152 from bruvzg/mac_title_button_pos

Rémi Verschelde 2 years ago
parent
commit
34e28f2fb5

+ 9 - 0
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.
 			</description>
 		</method>
+		<method name="window_set_window_buttons_offset">
+			<return type="void" />
+			<param index="0" name="offset" type="Vector2i" />
+			<param index="1" name="window_id" type="int" default="0" />
+			<description>
+				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.
+			</description>
+		</method>
 		<method name="window_set_window_event_callback">
 			<return type="void" />
 			<param index="0" name="callback" type="Callable" />

+ 1 - 0
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);
 	}

+ 1 - 0
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",

+ 3 - 0
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<Vector2> 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;

+ 26 - 0
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;

+ 51 - 0
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 <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+
+@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

+ 112 - 0
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

+ 9 - 0
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);
 }

+ 1 - 0
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));

+ 2 - 1
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;