Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DisplayServer] Add method to estimate window title bar size. #80409

Merged
merged 1 commit into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/classes/AcceptDialog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
The text displayed by the dialog.
</member>
<member name="exclusive" type="bool" setter="set_exclusive" getter="is_exclusive" overrides="Window" default="true" />
<member name="keep_title_visible" type="bool" setter="set_keep_title_visible" getter="get_keep_title_visible" overrides="Window" default="true" />
<member name="ok_button_text" type="String" setter="set_ok_button_text" getter="get_ok_button_text" default="&quot;OK&quot;">
The text displayed by the OK button (see [method get_ok_button]).
</member>
Expand Down
9 changes: 9 additions & 0 deletions doc/classes/DisplayServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,15 @@
Returns the size of the window specified by [param window_id] (in pixels), including the borders drawn by the operating system. See also [method window_get_size].
</description>
</method>
<method name="window_get_title_size" qualifiers="const">
<return type="Vector2i" />
<param index="0" name="title" type="String" />
<param index="1" name="window_id" type="int" default="0" />
<description>
Returns the estimated window title bar size (including text and window buttons) for the window specified by [param window_id] (in pixels). This method does not change the window title.
[b]Note:[/b] This method is implemented on macOS and Windows.
</description>
</method>
<method name="window_get_vsync_mode" qualifiers="const">
<return type="int" enum="DisplayServer.VSyncMode" />
<param index="0" name="window_id" type="int" default="0" />
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/Window.xml
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,9 @@
<member name="initial_position" type="int" setter="set_initial_position" getter="get_initial_position" enum="Window.WindowInitialPosition" default="0">
Specifies the initial type of position for the [Window]. See [enum WindowInitialPosition] constants.
</member>
<member name="keep_title_visible" type="bool" setter="set_keep_title_visible" getter="get_keep_title_visible" default="false">
If [code]true[/code], the [Window] width is expanded to keep the title bar text fully visible.
</member>
bruvzg marked this conversation as resolved.
Show resolved Hide resolved
<member name="max_size" type="Vector2i" setter="set_max_size" getter="get_max_size" default="Vector2i(0, 0)">
If non-zero, the [Window] can't be resized to be bigger than this size.
[b]Note:[/b] This property will be ignored if the value is lower than [member min_size].
Expand Down
1 change: 1 addition & 0 deletions platform/macos/display_server_macos.h
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ class DisplayServerMacOS : public DisplayServer {
virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;

virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
virtual Size2i window_get_title_size(const String &p_title, WindowID p_window) const override;
virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID) override;

virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override;
Expand Down
41 changes: 41 additions & 0 deletions platform/macos/display_server_macos.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2626,6 +2626,47 @@
[wd.window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]];
}

Size2i DisplayServerMacOS::window_get_title_size(const String &p_title, WindowID p_window) const {
_THREAD_SAFE_METHOD_

Size2i size;
ERR_FAIL_COND_V(!windows.has(p_window), size);

const WindowData &wd = windows[p_window];
if (wd.fullscreen || wd.borderless) {
return size;
}
if ([wd.window_object respondsToSelector:@selector(isMiniaturized)]) {
if ([wd.window_object isMiniaturized]) {
return size;
}
}

float scale = screen_get_max_scale();

if (wd.window_button_view) {
size.x = ([wd.window_button_view getOffset].x + [wd.window_button_view frame].size.width);
size.y = ([wd.window_button_view getOffset].y + [wd.window_button_view frame].size.height);
} else {
NSButton *cb = [wd.window_object standardWindowButton:NSWindowCloseButton];
NSButton *mb = [wd.window_object standardWindowButton:NSWindowMiniaturizeButton];
float cb_frame = NSMinX([cb frame]);
float mb_frame = NSMinX([mb frame]);
bool is_rtl = ([wd.window_object windowTitlebarLayoutDirection] == NSUserInterfaceLayoutDirectionRightToLeft);

float window_buttons_spacing = (is_rtl) ? (cb_frame - mb_frame) : (mb_frame - cb_frame);
size.x = window_buttons_spacing * 4;
size.y = [cb frame].origin.y + [cb frame].size.height;
}

NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont titleBarFontOfSize:0], NSFontAttributeName, nil];
NSSize text_size = [[[NSAttributedString alloc] initWithString:[NSString stringWithUTF8String:p_title.utf8().get_data()] attributes:attributes] size];
size.x += text_size.width;
size.y = MAX(size.y, text_size.height);

return size * scale;
}

void DisplayServerMacOS::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {
_THREAD_SAFE_METHOD_

Expand Down
47 changes: 47 additions & 0 deletions platform/windows/display_server_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1191,6 +1191,51 @@ void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_wi
SetWindowTextW(windows[p_window].hWnd, (LPCWSTR)(p_title.utf16().get_data()));
}

Size2i DisplayServerWindows::window_get_title_size(const String &p_title, WindowID p_window) const {
_THREAD_SAFE_METHOD_

Size2i size;
ERR_FAIL_COND_V(!windows.has(p_window), size);

const WindowData &wd = windows[p_window];
if (wd.fullscreen || wd.minimized || wd.borderless) {
return size;
}

HDC hdc = GetDCEx(wd.hWnd, NULL, DCX_WINDOW);
if (hdc) {
Char16String s = p_title.utf16();
SIZE text_size;
if (GetTextExtentPoint32W(hdc, (LPCWSTR)(s.get_data()), s.length(), &text_size)) {
size.x = text_size.cx;
size.y = text_size.cy;
}

ReleaseDC(wd.hWnd, hdc);
}
RECT rect;
if (DwmGetWindowAttribute(wd.hWnd, DWMWA_CAPTION_BUTTON_BOUNDS, &rect, sizeof(RECT)) == S_OK) {
if (rect.right - rect.left > 0) {
ClientToScreen(wd.hWnd, (POINT *)&rect.left);
ClientToScreen(wd.hWnd, (POINT *)&rect.right);

if (win81p_PhysicalToLogicalPointForPerMonitorDPI) {
win81p_PhysicalToLogicalPointForPerMonitorDPI(0, (POINT *)&rect.left);
win81p_PhysicalToLogicalPointForPerMonitorDPI(0, (POINT *)&rect.right);
}

size.x += (rect.right - rect.left);
size.y = MAX(size.y, rect.bottom - rect.top);
}
}
if (icon.is_valid()) {
size.x += 32;
} else {
size.x += 16;
}
return size;
}

void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {
_THREAD_SAFE_METHOD_

Expand Down Expand Up @@ -4385,6 +4430,7 @@ bool DisplayServerWindows::winink_available = false;
GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr;
GetPointerPenInfoPtr DisplayServerWindows::win8p_GetPointerPenInfo = nullptr;
LogicalToPhysicalPointForPerMonitorDPIPtr DisplayServerWindows::win81p_LogicalToPhysicalPointForPerMonitorDPI = nullptr;
PhysicalToLogicalPointForPerMonitorDPIPtr DisplayServerWindows::win81p_PhysicalToLogicalPointForPerMonitorDPI = nullptr;

typedef enum _SHC_PROCESS_DPI_AWARENESS {
SHC_PROCESS_DPI_UNAWARE = 0,
Expand Down Expand Up @@ -4522,6 +4568,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo");
win81p_LogicalToPhysicalPointForPerMonitorDPI = (LogicalToPhysicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "LogicalToPhysicalPointForPerMonitorDPI");
win81p_PhysicalToLogicalPointForPerMonitorDPI = (PhysicalToLogicalPointForPerMonitorDPIPtr)GetProcAddress(user32_lib, "PhysicalToLogicalPointForPerMonitorDPI");

winink_available = win8p_GetPointerType && win8p_GetPointerPenInfo;
}
Expand Down
3 changes: 3 additions & 0 deletions platform/windows/display_server_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ typedef struct tagPOINTER_PEN_INFO {
typedef BOOL(WINAPI *GetPointerTypePtr)(uint32_t p_id, POINTER_INPUT_TYPE *p_type);
typedef BOOL(WINAPI *GetPointerPenInfoPtr)(uint32_t p_id, POINTER_PEN_INFO *p_pen_info);
typedef BOOL(WINAPI *LogicalToPhysicalPointForPerMonitorDPIPtr)(HWND hwnd, LPPOINT lpPoint);
typedef BOOL(WINAPI *PhysicalToLogicalPointForPerMonitorDPIPtr)(HWND hwnd, LPPOINT lpPoint);

typedef struct {
BYTE bWidth; // Width, in pixels, of the image
Expand Down Expand Up @@ -309,6 +310,7 @@ class DisplayServerWindows : public DisplayServer {

// DPI conversion API
static LogicalToPhysicalPointForPerMonitorDPIPtr win81p_LogicalToPhysicalPointForPerMonitorDPI;
static PhysicalToLogicalPointForPerMonitorDPIPtr win81p_PhysicalToLogicalPointForPerMonitorDPI;

void _update_tablet_ctx(const String &p_old_driver, const String &p_new_driver);
String tablet_driver;
Expand Down Expand Up @@ -569,6 +571,7 @@ class DisplayServerWindows : public DisplayServer {
virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;

virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
virtual Size2i window_get_title_size(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID) override;

virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override;
Expand Down
1 change: 1 addition & 0 deletions scene/gui/dialogs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ AcceptDialog::AcceptDialog() {
set_transient(true);
set_exclusive(true);
set_clamp_to_embedder(true);
set_keep_title_visible(true);

bg_panel = memnew(Panel);
add_child(bg_panel, false, INTERNAL_MODE_FRONT);
Expand Down
70 changes: 44 additions & 26 deletions scene/main/window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,20 +270,21 @@ void Window::set_title(const String &p_title) {
ERR_MAIN_THREAD_GUARD;

title = p_title;
tr_title = atr(p_title);
#ifdef DEBUG_ENABLED
if (window_id == DisplayServer::MAIN_WINDOW_ID) {
// Append a suffix to the window title to denote that the project is running
// from a debug build (including the editor). Since this results in lower performance,
// this should be clearly presented to the user.
tr_title = vformat("%s (DEBUG)", tr_title);
}
#endif

if (embedder) {
embedder->_sub_window_update(this);
} else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
String tr_title = atr(p_title);
#ifdef DEBUG_ENABLED
if (window_id == DisplayServer::MAIN_WINDOW_ID) {
// Append a suffix to the window title to denote that the project is running
// from a debug build (including the editor). Since this results in lower performance,
// this should be clearly presented to the user.
tr_title = vformat("%s (DEBUG)", tr_title);
}
#endif
DisplayServer::get_singleton()->window_set_title(tr_title, window_id);
_update_window_size();
}
}

Expand Down Expand Up @@ -586,15 +587,6 @@ void Window::_make_window() {
DisplayServer::get_singleton()->window_set_max_size(Size2i(), window_id);
DisplayServer::get_singleton()->window_set_min_size(Size2i(), window_id);
DisplayServer::get_singleton()->window_set_mouse_passthrough(mpath, window_id);
String tr_title = atr(title);
#ifdef DEBUG_ENABLED
if (window_id == DisplayServer::MAIN_WINDOW_ID) {
// Append a suffix to the window title to denote that the project is running
// from a debug build (including the editor). Since this results in lower performance,
// this should be clearly presented to the user.
tr_title = vformat("%s (DEBUG)", tr_title);
}
#endif
DisplayServer::get_singleton()->window_set_title(tr_title, window_id);
DisplayServer::get_singleton()->window_attach_instance_id(get_instance_id(), window_id);

Expand Down Expand Up @@ -994,6 +986,12 @@ void Window::_update_window_size() {
}

DisplayServer::get_singleton()->window_set_max_size(max_size_used, window_id);

if (keep_title_visible) {
Size2i title_size = DisplayServer::get_singleton()->window_get_title_size(tr_title, window_id);
size_limit = size_limit.max(title_size);
}

DisplayServer::get_singleton()->window_set_min_size(size_limit, window_id);
DisplayServer::get_singleton()->window_set_size(size, window_id);
}
Expand Down Expand Up @@ -1281,17 +1279,19 @@ void Window::_notification(int p_what) {
_invalidate_theme_cache();
_update_theme_item_cache();

if (!embedder && window_id != DisplayServer::INVALID_WINDOW_ID) {
String tr_title = atr(title);
tr_title = atr(title);
#ifdef DEBUG_ENABLED
if (window_id == DisplayServer::MAIN_WINDOW_ID) {
// Append a suffix to the window title to denote that the project is running
// from a debug build (including the editor). Since this results in lower performance,
// this should be clearly presented to the user.
tr_title = vformat("%s (DEBUG)", tr_title);
}
if (window_id == DisplayServer::MAIN_WINDOW_ID) {
// Append a suffix to the window title to denote that the project is running
// from a debug build (including the editor). Since this results in lower performance,
// this should be clearly presented to the user.
tr_title = vformat("%s (DEBUG)", tr_title);
}
#endif

if (!embedder && window_id != DisplayServer::INVALID_WINDOW_ID) {
DisplayServer::get_singleton()->window_set_title(tr_title, window_id);
_update_window_size();
}
} break;

Expand Down Expand Up @@ -1384,6 +1384,20 @@ Window::ContentScaleStretch Window::get_content_scale_stretch() const {
return content_scale_stretch;
}

void Window::set_keep_title_visible(bool p_title_visible) {
if (keep_title_visible == p_title_visible) {
return;
}
keep_title_visible = p_title_visible;
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
_update_window_size();
}
}

bool Window::get_keep_title_visible() const {
return keep_title_visible;
}

void Window::set_content_scale_factor(real_t p_factor) {
ERR_MAIN_THREAD_GUARD;
ERR_FAIL_COND(p_factor <= 0);
Expand Down Expand Up @@ -2713,6 +2727,9 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_content_scale_stretch", "stretch"), &Window::set_content_scale_stretch);
ClassDB::bind_method(D_METHOD("get_content_scale_stretch"), &Window::get_content_scale_stretch);

ClassDB::bind_method(D_METHOD("set_keep_title_visible", "title_visible"), &Window::set_keep_title_visible);
ClassDB::bind_method(D_METHOD("get_keep_title_visible"), &Window::get_keep_title_visible);

ClassDB::bind_method(D_METHOD("set_content_scale_factor", "factor"), &Window::set_content_scale_factor);
ClassDB::bind_method(D_METHOD("get_content_scale_factor"), &Window::get_content_scale_factor);

Expand Down Expand Up @@ -2826,6 +2843,7 @@ void Window::_bind_methods() {
ADD_GROUP("Limits", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "min_size", PROPERTY_HINT_NONE, "suffix:px"), "set_min_size", "get_min_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "max_size", PROPERTY_HINT_NONE, "suffix:px"), "set_max_size", "get_max_size");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_title_visible"), "set_keep_title_visible", "get_keep_title_visible");

ADD_GROUP("Content Scale", "content_scale_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "content_scale_size"), "set_content_scale_size", "get_content_scale_size");
Expand Down
5 changes: 5 additions & 0 deletions scene/main/window.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class Window : public Viewport {
bool initialized = false;

String title;
String tr_title;
mutable int current_screen = 0;
mutable Vector2i position;
mutable Size2i size = Size2i(DEFAULT_WINDOW_SIZE, DEFAULT_WINDOW_SIZE);
Expand All @@ -131,6 +132,7 @@ class Window : public Viewport {
bool updating_embedded_window = false;
bool clamp_to_embedder = false;
bool unparent_when_invisible = false;
bool keep_title_visible = false;

LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED;

Expand Down Expand Up @@ -336,6 +338,9 @@ class Window : public Viewport {
void set_content_scale_stretch(ContentScaleStretch p_stretch);
ContentScaleStretch get_content_scale_stretch() const;

void set_keep_title_visible(bool p_title_visible);
bool get_keep_title_visible() const;

void set_content_scale_factor(real_t p_factor);
real_t get_content_scale_factor() const;

Expand Down
1 change: 1 addition & 0 deletions servers/display_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,7 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("window_get_popup_safe_rect", "window"), &DisplayServer::window_get_popup_safe_rect);

ClassDB::bind_method(D_METHOD("window_set_title", "title", "window_id"), &DisplayServer::window_set_title, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_get_title_size", "title", "window_id"), &DisplayServer::window_get_title_size, DEFVAL(MAIN_WINDOW_ID));
ClassDB::bind_method(D_METHOD("window_set_mouse_passthrough", "region", "window_id"), &DisplayServer::window_set_mouse_passthrough, DEFVAL(MAIN_WINDOW_ID));

ClassDB::bind_method(D_METHOD("window_get_current_screen", "window_id"), &DisplayServer::window_get_current_screen, DEFVAL(MAIN_WINDOW_ID));
Expand Down
1 change: 1 addition & 0 deletions servers/display_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ class DisplayServer : public Object {
virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) = 0;

virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) = 0;
virtual Size2i window_get_title_size(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) const { return Size2i(); }

virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID);

Expand Down