[macOS] Add an option to align window buttons in "extend to title" mode.

This commit is contained in:
bruvzg 2022-09-20 11:12:49 +03:00
parent aa553f4030
commit 0ed4cc6287
No known key found for this signature in database
GPG key ID: 7960FCF39844EC38
10 changed files with 215 additions and 1 deletions

View file

@ -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" />

View file

@ -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);
}

View file

@ -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",

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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));

View file

@ -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;