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;