1/**************************************************************************/
2/* window.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "window.h"
32
33#include "core/config/project_settings.h"
34#include "core/debugger/engine_debugger.h"
35#include "core/input/shortcut.h"
36#include "core/string/translation.h"
37#include "core/variant/variant_parser.h"
38#include "scene/gui/control.h"
39#include "scene/scene_string_names.h"
40#include "scene/theme/theme_db.h"
41#include "scene/theme/theme_owner.h"
42
43// Dynamic properties.
44
45bool Window::_set(const StringName &p_name, const Variant &p_value) {
46 ERR_MAIN_THREAD_GUARD_V(false);
47
48 String name = p_name;
49 if (!name.begins_with("theme_override")) {
50 return false;
51 }
52
53 if (p_value.get_type() == Variant::NIL || (p_value.get_type() == Variant::OBJECT && (Object *)p_value == nullptr)) {
54 if (name.begins_with("theme_override_icons/")) {
55 String dname = name.get_slicec('/', 1);
56 if (theme_icon_override.has(dname)) {
57 theme_icon_override[dname]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
58 }
59 theme_icon_override.erase(dname);
60 _notify_theme_override_changed();
61 } else if (name.begins_with("theme_override_styles/")) {
62 String dname = name.get_slicec('/', 1);
63 if (theme_style_override.has(dname)) {
64 theme_style_override[dname]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
65 }
66 theme_style_override.erase(dname);
67 _notify_theme_override_changed();
68 } else if (name.begins_with("theme_override_fonts/")) {
69 String dname = name.get_slicec('/', 1);
70 if (theme_font_override.has(dname)) {
71 theme_font_override[dname]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
72 }
73 theme_font_override.erase(dname);
74 _notify_theme_override_changed();
75 } else if (name.begins_with("theme_override_font_sizes/")) {
76 String dname = name.get_slicec('/', 1);
77 theme_font_size_override.erase(dname);
78 _notify_theme_override_changed();
79 } else if (name.begins_with("theme_override_colors/")) {
80 String dname = name.get_slicec('/', 1);
81 theme_color_override.erase(dname);
82 _notify_theme_override_changed();
83 } else if (name.begins_with("theme_override_constants/")) {
84 String dname = name.get_slicec('/', 1);
85 theme_constant_override.erase(dname);
86 _notify_theme_override_changed();
87 } else {
88 return false;
89 }
90
91 } else {
92 if (name.begins_with("theme_override_icons/")) {
93 String dname = name.get_slicec('/', 1);
94 add_theme_icon_override(dname, p_value);
95 } else if (name.begins_with("theme_override_styles/")) {
96 String dname = name.get_slicec('/', 1);
97 add_theme_style_override(dname, p_value);
98 } else if (name.begins_with("theme_override_fonts/")) {
99 String dname = name.get_slicec('/', 1);
100 add_theme_font_override(dname, p_value);
101 } else if (name.begins_with("theme_override_font_sizes/")) {
102 String dname = name.get_slicec('/', 1);
103 add_theme_font_size_override(dname, p_value);
104 } else if (name.begins_with("theme_override_colors/")) {
105 String dname = name.get_slicec('/', 1);
106 add_theme_color_override(dname, p_value);
107 } else if (name.begins_with("theme_override_constants/")) {
108 String dname = name.get_slicec('/', 1);
109 add_theme_constant_override(dname, p_value);
110 } else {
111 return false;
112 }
113 }
114 return true;
115}
116
117bool Window::_get(const StringName &p_name, Variant &r_ret) const {
118 ERR_READ_THREAD_GUARD_V(false);
119
120 String sname = p_name;
121 if (!sname.begins_with("theme_override")) {
122 return false;
123 }
124
125 if (sname.begins_with("theme_override_icons/")) {
126 String name = sname.get_slicec('/', 1);
127 r_ret = theme_icon_override.has(name) ? Variant(theme_icon_override[name]) : Variant();
128 } else if (sname.begins_with("theme_override_styles/")) {
129 String name = sname.get_slicec('/', 1);
130 r_ret = theme_style_override.has(name) ? Variant(theme_style_override[name]) : Variant();
131 } else if (sname.begins_with("theme_override_fonts/")) {
132 String name = sname.get_slicec('/', 1);
133 r_ret = theme_font_override.has(name) ? Variant(theme_font_override[name]) : Variant();
134 } else if (sname.begins_with("theme_override_font_sizes/")) {
135 String name = sname.get_slicec('/', 1);
136 r_ret = theme_font_size_override.has(name) ? Variant(theme_font_size_override[name]) : Variant();
137 } else if (sname.begins_with("theme_override_colors/")) {
138 String name = sname.get_slicec('/', 1);
139 r_ret = theme_color_override.has(name) ? Variant(theme_color_override[name]) : Variant();
140 } else if (sname.begins_with("theme_override_constants/")) {
141 String name = sname.get_slicec('/', 1);
142 r_ret = theme_constant_override.has(name) ? Variant(theme_constant_override[name]) : Variant();
143 } else {
144 return false;
145 }
146
147 return true;
148}
149
150void Window::_get_property_list(List<PropertyInfo> *p_list) const {
151 ERR_READ_THREAD_GUARD;
152
153 Ref<Theme> default_theme = ThemeDB::get_singleton()->get_default_theme();
154
155 p_list->push_back(PropertyInfo(Variant::NIL, GNAME("Theme Overrides", "theme_override_"), PROPERTY_HINT_NONE, "theme_override_", PROPERTY_USAGE_GROUP));
156
157 {
158 List<StringName> names;
159 default_theme->get_color_list(get_class_name(), &names);
160 for (const StringName &E : names) {
161 uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE;
162 if (theme_color_override.has(E)) {
163 usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED;
164 }
165
166 p_list->push_back(PropertyInfo(Variant::COLOR, PNAME("theme_override_colors") + String("/") + E, PROPERTY_HINT_NONE, "", usage));
167 }
168 }
169 {
170 List<StringName> names;
171 default_theme->get_constant_list(get_class_name(), &names);
172 for (const StringName &E : names) {
173 uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE;
174 if (theme_constant_override.has(E)) {
175 usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED;
176 }
177
178 p_list->push_back(PropertyInfo(Variant::INT, PNAME("theme_override_constants") + String("/") + E, PROPERTY_HINT_RANGE, "-16384,16384", usage));
179 }
180 }
181 {
182 List<StringName> names;
183 default_theme->get_font_list(get_class_name(), &names);
184 for (const StringName &E : names) {
185 uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE;
186 if (theme_font_override.has(E)) {
187 usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED;
188 }
189
190 p_list->push_back(PropertyInfo(Variant::OBJECT, PNAME("theme_override_fonts") + String("/") + E, PROPERTY_HINT_RESOURCE_TYPE, "Font", usage));
191 }
192 }
193 {
194 List<StringName> names;
195 default_theme->get_font_size_list(get_class_name(), &names);
196 for (const StringName &E : names) {
197 uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE;
198 if (theme_font_size_override.has(E)) {
199 usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED;
200 }
201
202 p_list->push_back(PropertyInfo(Variant::INT, PNAME("theme_override_font_sizes") + String("/") + E, PROPERTY_HINT_RANGE, "1,256,1,or_greater,suffix:px", usage));
203 }
204 }
205 {
206 List<StringName> names;
207 default_theme->get_icon_list(get_class_name(), &names);
208 for (const StringName &E : names) {
209 uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE;
210 if (theme_icon_override.has(E)) {
211 usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED;
212 }
213
214 p_list->push_back(PropertyInfo(Variant::OBJECT, PNAME("theme_override_icons") + String("/") + E, PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", usage));
215 }
216 }
217 {
218 List<StringName> names;
219 default_theme->get_stylebox_list(get_class_name(), &names);
220 for (const StringName &E : names) {
221 uint32_t usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE;
222 if (theme_style_override.has(E)) {
223 usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED;
224 }
225
226 p_list->push_back(PropertyInfo(Variant::OBJECT, PNAME("theme_override_styles") + String("/") + E, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", usage));
227 }
228 }
229}
230
231void Window::_validate_property(PropertyInfo &p_property) const {
232 if (p_property.name == "position" && initial_position != WINDOW_INITIAL_POSITION_ABSOLUTE) {
233 p_property.usage = PROPERTY_USAGE_NONE;
234 }
235
236 if (p_property.name == "current_screen" && initial_position != WINDOW_INITIAL_POSITION_CENTER_OTHER_SCREEN) {
237 p_property.usage = PROPERTY_USAGE_NONE;
238 }
239
240 if (p_property.name == "theme_type_variation") {
241 List<StringName> names;
242
243 // Only the default theme and the project theme are used for the list of options.
244 // This is an imposed limitation to simplify the logic needed to leverage those options.
245 ThemeDB::get_singleton()->get_default_theme()->get_type_variation_list(get_class_name(), &names);
246 if (ThemeDB::get_singleton()->get_project_theme().is_valid()) {
247 ThemeDB::get_singleton()->get_project_theme()->get_type_variation_list(get_class_name(), &names);
248 }
249 names.sort_custom<StringName::AlphCompare>();
250
251 Vector<StringName> unique_names;
252 String hint_string;
253 for (const StringName &E : names) {
254 // Skip duplicate values.
255 if (unique_names.has(E)) {
256 continue;
257 }
258
259 hint_string += String(E) + ",";
260 unique_names.append(E);
261 }
262
263 p_property.hint_string = hint_string;
264 }
265}
266
267//
268
269void Window::set_title(const String &p_title) {
270 ERR_MAIN_THREAD_GUARD;
271
272 title = p_title;
273
274 if (embedder) {
275 embedder->_sub_window_update(this);
276 } else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
277 String tr_title = atr(p_title);
278#ifdef DEBUG_ENABLED
279 if (window_id == DisplayServer::MAIN_WINDOW_ID) {
280 // Append a suffix to the window title to denote that the project is running
281 // from a debug build (including the editor). Since this results in lower performance,
282 // this should be clearly presented to the user.
283 tr_title = vformat("%s (DEBUG)", tr_title);
284 }
285#endif
286 DisplayServer::get_singleton()->window_set_title(tr_title, window_id);
287 }
288}
289
290String Window::get_title() const {
291 ERR_READ_THREAD_GUARD_V(String());
292 return title;
293}
294
295void Window::set_initial_position(Window::WindowInitialPosition p_initial_position) {
296 ERR_MAIN_THREAD_GUARD;
297
298 initial_position = p_initial_position;
299 notify_property_list_changed();
300}
301
302Window::WindowInitialPosition Window::get_initial_position() const {
303 ERR_READ_THREAD_GUARD_V(WINDOW_INITIAL_POSITION_ABSOLUTE);
304 return initial_position;
305}
306
307void Window::set_current_screen(int p_screen) {
308 ERR_MAIN_THREAD_GUARD;
309
310 current_screen = p_screen;
311 if (window_id == DisplayServer::INVALID_WINDOW_ID) {
312 return;
313 }
314 DisplayServer::get_singleton()->window_set_current_screen(p_screen, window_id);
315}
316
317int Window::get_current_screen() const {
318 ERR_READ_THREAD_GUARD_V(0);
319
320 if (window_id != DisplayServer::INVALID_WINDOW_ID) {
321 current_screen = DisplayServer::get_singleton()->window_get_current_screen(window_id);
322 }
323 return current_screen;
324}
325
326void Window::set_position(const Point2i &p_position) {
327 ERR_MAIN_THREAD_GUARD;
328
329 position = p_position;
330
331 if (embedder) {
332 embedder->_sub_window_update(this);
333
334 } else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
335 DisplayServer::get_singleton()->window_set_position(p_position, window_id);
336 }
337}
338
339Point2i Window::get_position() const {
340 ERR_READ_THREAD_GUARD_V(Point2i());
341
342 return position;
343}
344
345void Window::move_to_center() {
346 ERR_MAIN_THREAD_GUARD;
347 ERR_FAIL_COND(!is_inside_tree());
348
349 Rect2 parent_rect;
350
351 if (is_embedded()) {
352 parent_rect = get_embedder()->get_visible_rect();
353 } else {
354 int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(get_window_id());
355 parent_rect.position = DisplayServer::get_singleton()->screen_get_position(parent_screen);
356 parent_rect.size = DisplayServer::get_singleton()->screen_get_size(parent_screen);
357 }
358
359 if (parent_rect != Rect2()) {
360 set_position(parent_rect.position + (parent_rect.size - get_size()) / 2);
361 }
362}
363
364void Window::set_size(const Size2i &p_size) {
365 ERR_MAIN_THREAD_GUARD;
366
367 size = p_size;
368 _update_window_size();
369}
370
371Size2i Window::get_size() const {
372 ERR_READ_THREAD_GUARD_V(Size2i());
373 return size;
374}
375
376void Window::reset_size() {
377 ERR_MAIN_THREAD_GUARD;
378 set_size(Size2i());
379}
380
381Point2i Window::get_position_with_decorations() const {
382 ERR_READ_THREAD_GUARD_V(Point2i());
383 if (window_id != DisplayServer::INVALID_WINDOW_ID) {
384 return DisplayServer::get_singleton()->window_get_position_with_decorations(window_id);
385 }
386 return position;
387}
388
389Size2i Window::get_size_with_decorations() const {
390 ERR_READ_THREAD_GUARD_V(Size2i());
391 if (window_id != DisplayServer::INVALID_WINDOW_ID) {
392 return DisplayServer::get_singleton()->window_get_size_with_decorations(window_id);
393 }
394 return size;
395}
396
397Size2i Window::_clamp_limit_size(const Size2i &p_limit_size) {
398 // Force window limits to respect size limitations of rendering server.
399 Size2i max_window_size = RS::get_singleton()->get_maximum_viewport_size();
400 if (max_window_size != Size2i()) {
401 return p_limit_size.clamp(Vector2i(), max_window_size);
402 } else {
403 return p_limit_size.max(Vector2i());
404 }
405}
406
407void Window::_validate_limit_size() {
408 // When max_size is invalid, max_size_used falls back to respect size limitations of rendering server.
409 bool max_size_valid = (max_size.x > 0 || max_size.y > 0) && max_size.x >= min_size.x && max_size.y >= min_size.y;
410 max_size_used = max_size_valid ? max_size : RS::get_singleton()->get_maximum_viewport_size();
411}
412
413void Window::set_max_size(const Size2i &p_max_size) {
414 ERR_MAIN_THREAD_GUARD;
415 Size2i max_size_clamped = _clamp_limit_size(p_max_size);
416 if (max_size == max_size_clamped) {
417 return;
418 }
419 max_size = max_size_clamped;
420
421 _validate_limit_size();
422 _update_window_size();
423}
424
425Size2i Window::get_max_size() const {
426 ERR_READ_THREAD_GUARD_V(Size2i());
427 return max_size;
428}
429
430void Window::set_min_size(const Size2i &p_min_size) {
431 ERR_MAIN_THREAD_GUARD;
432 Size2i min_size_clamped = _clamp_limit_size(p_min_size);
433 if (min_size == min_size_clamped) {
434 return;
435 }
436 min_size = min_size_clamped;
437
438 _validate_limit_size();
439 _update_window_size();
440}
441
442Size2i Window::get_min_size() const {
443 ERR_READ_THREAD_GUARD_V(Size2i());
444 return min_size;
445}
446
447void Window::set_mode(Mode p_mode) {
448 ERR_MAIN_THREAD_GUARD;
449 mode = p_mode;
450
451 if (embedder) {
452 embedder->_sub_window_update(this);
453
454 } else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
455 DisplayServer::get_singleton()->window_set_mode(DisplayServer::WindowMode(p_mode), window_id);
456 }
457}
458
459Window::Mode Window::get_mode() const {
460 ERR_READ_THREAD_GUARD_V(MODE_WINDOWED);
461 if (window_id != DisplayServer::INVALID_WINDOW_ID) {
462 mode = (Mode)DisplayServer::get_singleton()->window_get_mode(window_id);
463 }
464 return mode;
465}
466
467void Window::set_flag(Flags p_flag, bool p_enabled) {
468 ERR_MAIN_THREAD_GUARD;
469 ERR_FAIL_INDEX(p_flag, FLAG_MAX);
470 flags[p_flag] = p_enabled;
471
472 if (embedder) {
473 embedder->_sub_window_update(this);
474
475 } else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
476 if (!is_in_edited_scene_root()) {
477 DisplayServer::get_singleton()->window_set_flag(DisplayServer::WindowFlags(p_flag), p_enabled, window_id);
478 }
479 }
480}
481
482bool Window::get_flag(Flags p_flag) const {
483 ERR_READ_THREAD_GUARD_V(false);
484 ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false);
485 if (window_id != DisplayServer::INVALID_WINDOW_ID) {
486 if (!is_in_edited_scene_root()) {
487 flags[p_flag] = DisplayServer::get_singleton()->window_get_flag(DisplayServer::WindowFlags(p_flag), window_id);
488 }
489 }
490 return flags[p_flag];
491}
492
493bool Window::is_maximize_allowed() const {
494 ERR_READ_THREAD_GUARD_V(false);
495 if (window_id != DisplayServer::INVALID_WINDOW_ID) {
496 return DisplayServer::get_singleton()->window_is_maximize_allowed(window_id);
497 }
498 return true;
499}
500
501void Window::request_attention() {
502 ERR_MAIN_THREAD_GUARD;
503 if (window_id != DisplayServer::INVALID_WINDOW_ID) {
504 DisplayServer::get_singleton()->window_request_attention(window_id);
505 }
506}
507
508void Window::move_to_foreground() {
509 ERR_MAIN_THREAD_GUARD;
510 if (embedder) {
511 embedder->_sub_window_grab_focus(this);
512
513 } else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
514 DisplayServer::get_singleton()->window_move_to_foreground(window_id);
515 }
516}
517
518bool Window::can_draw() const {
519 ERR_READ_THREAD_GUARD_V(false);
520 if (!is_inside_tree()) {
521 return false;
522 }
523 if (window_id != DisplayServer::INVALID_WINDOW_ID) {
524 return DisplayServer::get_singleton()->window_can_draw(window_id);
525 }
526
527 return visible;
528}
529
530void Window::set_ime_active(bool p_active) {
531 ERR_MAIN_THREAD_GUARD;
532 if (window_id != DisplayServer::INVALID_WINDOW_ID) {
533 DisplayServer::get_singleton()->window_set_ime_active(p_active, window_id);
534 }
535}
536
537void Window::set_ime_position(const Point2i &p_pos) {
538 ERR_MAIN_THREAD_GUARD;
539 if (window_id != DisplayServer::INVALID_WINDOW_ID) {
540 DisplayServer::get_singleton()->window_set_ime_position(p_pos, window_id);
541 }
542}
543
544bool Window::is_embedded() const {
545 ERR_READ_THREAD_GUARD_V(false);
546 return get_embedder() != nullptr;
547}
548
549bool Window::is_in_edited_scene_root() const {
550 ERR_READ_THREAD_GUARD_V(false);
551#ifdef TOOLS_ENABLED
552 return is_part_of_edited_scene();
553#else
554 return false;
555#endif
556}
557
558void Window::_make_window() {
559 ERR_FAIL_COND(window_id != DisplayServer::INVALID_WINDOW_ID);
560
561 uint32_t f = 0;
562 for (int i = 0; i < FLAG_MAX; i++) {
563 if (flags[i]) {
564 f |= (1 << i);
565 }
566 }
567
568 DisplayServer::VSyncMode vsync_mode = DisplayServer::get_singleton()->window_get_vsync_mode(DisplayServer::MAIN_WINDOW_ID);
569 Rect2i window_rect;
570 if (initial_position == WINDOW_INITIAL_POSITION_ABSOLUTE) {
571 window_rect = Rect2i(position, size);
572 } else if (initial_position == WINDOW_INITIAL_POSITION_CENTER_PRIMARY_SCREEN) {
573 window_rect = Rect2i(DisplayServer::get_singleton()->screen_get_position(DisplayServer::SCREEN_PRIMARY) + (DisplayServer::get_singleton()->screen_get_size(DisplayServer::SCREEN_PRIMARY) - size) / 2, size);
574 } else if (initial_position == WINDOW_INITIAL_POSITION_CENTER_MAIN_WINDOW_SCREEN) {
575 window_rect = Rect2i(DisplayServer::get_singleton()->screen_get_position(DisplayServer::SCREEN_OF_MAIN_WINDOW) + (DisplayServer::get_singleton()->screen_get_size(DisplayServer::SCREEN_OF_MAIN_WINDOW) - size) / 2, size);
576 } else if (initial_position == WINDOW_INITIAL_POSITION_CENTER_OTHER_SCREEN) {
577 window_rect = Rect2i(DisplayServer::get_singleton()->screen_get_position(current_screen) + (DisplayServer::get_singleton()->screen_get_size(current_screen) - size) / 2, size);
578 } else if (initial_position == WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_MOUSE_FOCUS) {
579 window_rect = Rect2i(DisplayServer::get_singleton()->screen_get_position(DisplayServer::SCREEN_WITH_MOUSE_FOCUS) + (DisplayServer::get_singleton()->screen_get_size(DisplayServer::SCREEN_WITH_MOUSE_FOCUS) - size) / 2, size);
580 } else if (initial_position == WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_KEYBOARD_FOCUS) {
581 window_rect = Rect2i(DisplayServer::get_singleton()->screen_get_position(DisplayServer::SCREEN_WITH_KEYBOARD_FOCUS) + (DisplayServer::get_singleton()->screen_get_size(DisplayServer::SCREEN_WITH_KEYBOARD_FOCUS) - size) / 2, size);
582 }
583
584 window_id = DisplayServer::get_singleton()->create_sub_window(DisplayServer::WindowMode(mode), vsync_mode, f, window_rect);
585 ERR_FAIL_COND(window_id == DisplayServer::INVALID_WINDOW_ID);
586 DisplayServer::get_singleton()->window_set_max_size(Size2i(), window_id);
587 DisplayServer::get_singleton()->window_set_min_size(Size2i(), window_id);
588 DisplayServer::get_singleton()->window_set_mouse_passthrough(mpath, window_id);
589 String tr_title = atr(title);
590#ifdef DEBUG_ENABLED
591 if (window_id == DisplayServer::MAIN_WINDOW_ID) {
592 // Append a suffix to the window title to denote that the project is running
593 // from a debug build (including the editor). Since this results in lower performance,
594 // this should be clearly presented to the user.
595 tr_title = vformat("%s (DEBUG)", tr_title);
596 }
597#endif
598 DisplayServer::get_singleton()->window_set_title(tr_title, window_id);
599 DisplayServer::get_singleton()->window_attach_instance_id(get_instance_id(), window_id);
600
601 if (is_in_edited_scene_root()) {
602 DisplayServer::get_singleton()->window_set_exclusive(window_id, false);
603 } else {
604 DisplayServer::get_singleton()->window_set_exclusive(window_id, exclusive);
605 }
606
607 _update_window_size();
608
609 if (transient_parent && transient_parent->window_id != DisplayServer::INVALID_WINDOW_ID) {
610 DisplayServer::get_singleton()->window_set_transient(window_id, transient_parent->window_id);
611 }
612
613 if (transient_parent) {
614 for (const Window *E : transient_children) {
615 if (E->window_id != DisplayServer::INVALID_WINDOW_ID) {
616 DisplayServer::get_singleton()->window_set_transient(E->window_id, transient_parent->window_id);
617 }
618 }
619 }
620
621 _update_window_callbacks();
622
623 RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_VISIBLE);
624 DisplayServer::get_singleton()->show_window(window_id);
625}
626
627void Window::_update_from_window() {
628 ERR_FAIL_COND(window_id == DisplayServer::INVALID_WINDOW_ID);
629 mode = (Mode)DisplayServer::get_singleton()->window_get_mode(window_id);
630 for (int i = 0; i < FLAG_MAX; i++) {
631 flags[i] = DisplayServer::get_singleton()->window_get_flag(DisplayServer::WindowFlags(i), window_id);
632 }
633}
634
635void Window::_clear_window() {
636 ERR_FAIL_COND(window_id == DisplayServer::INVALID_WINDOW_ID);
637
638 bool had_focus = has_focus();
639
640 if (transient_parent && transient_parent->window_id != DisplayServer::INVALID_WINDOW_ID) {
641 DisplayServer::get_singleton()->window_set_transient(window_id, DisplayServer::INVALID_WINDOW_ID);
642 }
643
644 for (const Window *E : transient_children) {
645 if (E->window_id != DisplayServer::INVALID_WINDOW_ID) {
646 DisplayServer::get_singleton()->window_set_transient(E->window_id, DisplayServer::INVALID_WINDOW_ID);
647 }
648 }
649
650 _update_from_window();
651
652 DisplayServer::get_singleton()->delete_sub_window(window_id);
653 window_id = DisplayServer::INVALID_WINDOW_ID;
654
655 // If closing window was focused and has a parent, return focus.
656 if (had_focus && transient_parent) {
657 transient_parent->grab_focus();
658 }
659
660 _update_viewport_size();
661 RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED);
662}
663
664void Window::_rect_changed_callback(const Rect2i &p_callback) {
665 //we must always accept this as the truth
666 if (size == p_callback.size && position == p_callback.position) {
667 return;
668 }
669 position = p_callback.position;
670
671 if (size != p_callback.size) {
672 size = p_callback.size;
673 _update_viewport_size();
674 }
675}
676
677void Window::_propagate_window_notification(Node *p_node, int p_notification) {
678 p_node->notification(p_notification);
679 for (int i = 0; i < p_node->get_child_count(); i++) {
680 Node *child = p_node->get_child(i);
681 Window *window = Object::cast_to<Window>(child);
682 if (window) {
683 continue;
684 }
685 _propagate_window_notification(child, p_notification);
686 }
687}
688
689void Window::_event_callback(DisplayServer::WindowEvent p_event) {
690 switch (p_event) {
691 case DisplayServer::WINDOW_EVENT_MOUSE_ENTER: {
692 Window *root = get_tree()->get_root();
693 if (root->gui.windowmanager_window_over) {
694#ifdef DEV_ENABLED
695 WARN_PRINT_ONCE("Entering a window while a window is hovered should never happen in DisplayServer.");
696#endif // DEV_ENABLED
697 root->gui.windowmanager_window_over->_event_callback(DisplayServer::WINDOW_EVENT_MOUSE_EXIT);
698 }
699 _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER);
700 root->gui.windowmanager_window_over = this;
701 mouse_in_window = true;
702 if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
703 DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_ARROW); //restore cursor shape
704 }
705 } break;
706 case DisplayServer::WINDOW_EVENT_MOUSE_EXIT: {
707 Window *root = get_tree()->get_root();
708 if (!root->gui.windowmanager_window_over) {
709#ifdef DEV_ENABLED
710 WARN_PRINT_ONCE("Exiting a window while no window is hovered should never happen in DisplayServer.");
711#endif // DEV_ENABLED
712 return;
713 }
714 mouse_in_window = false;
715 root->gui.windowmanager_window_over->_mouse_leave_viewport();
716 root->gui.windowmanager_window_over = nullptr;
717 _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT);
718 } break;
719 case DisplayServer::WINDOW_EVENT_FOCUS_IN: {
720 focused = true;
721 _propagate_window_notification(this, NOTIFICATION_WM_WINDOW_FOCUS_IN);
722 emit_signal(SNAME("focus_entered"));
723
724 } break;
725 case DisplayServer::WINDOW_EVENT_FOCUS_OUT: {
726 focused = false;
727 _propagate_window_notification(this, NOTIFICATION_WM_WINDOW_FOCUS_OUT);
728 emit_signal(SNAME("focus_exited"));
729 } break;
730 case DisplayServer::WINDOW_EVENT_CLOSE_REQUEST: {
731 if (exclusive_child != nullptr) {
732 break; //has an exclusive child, can't get events until child is closed
733 }
734 _propagate_window_notification(this, NOTIFICATION_WM_CLOSE_REQUEST);
735 emit_signal(SNAME("close_requested"));
736 } break;
737 case DisplayServer::WINDOW_EVENT_GO_BACK_REQUEST: {
738 _propagate_window_notification(this, NOTIFICATION_WM_GO_BACK_REQUEST);
739 emit_signal(SNAME("go_back_requested"));
740 } break;
741 case DisplayServer::WINDOW_EVENT_DPI_CHANGE: {
742 _update_viewport_size();
743 _propagate_window_notification(this, NOTIFICATION_WM_DPI_CHANGE);
744 emit_signal(SNAME("dpi_changed"));
745 } break;
746 case DisplayServer::WINDOW_EVENT_TITLEBAR_CHANGE: {
747 emit_signal(SNAME("titlebar_changed"));
748 } break;
749 }
750}
751
752void Window::update_mouse_cursor_state() {
753 ERR_MAIN_THREAD_GUARD;
754 // Update states based on mouse cursor position.
755 // This includes updated mouse_enter or mouse_exit signals or the current mouse cursor shape.
756 // These details are set in Viewport::_gui_input_event. To instantly
757 // see the changes in the viewport, we need to trigger a mouse motion event.
758 // This function should be called whenever scene tree changes affect the mouse cursor.
759 Ref<InputEventMouseMotion> mm;
760 Vector2 pos = get_mouse_position();
761 Transform2D xform = get_global_canvas_transform().affine_inverse();
762 mm.instantiate();
763 mm->set_position(pos);
764 mm->set_global_position(xform.xform(pos));
765 mm->set_device(InputEvent::DEVICE_ID_INTERNAL);
766 push_input(mm);
767}
768
769void Window::show() {
770 ERR_MAIN_THREAD_GUARD;
771 set_visible(true);
772}
773
774void Window::hide() {
775 ERR_MAIN_THREAD_GUARD;
776 set_visible(false);
777}
778
779void Window::set_visible(bool p_visible) {
780 ERR_MAIN_THREAD_GUARD;
781 if (visible == p_visible) {
782 return;
783 }
784
785 if (!is_inside_tree()) {
786 visible = p_visible;
787 return;
788 }
789
790 ERR_FAIL_COND_MSG(get_parent() == nullptr, "Can't change visibility of main window.");
791
792 visible = p_visible;
793
794 // Stop any queued resizing, as the window will be resized right now.
795 updating_child_controls = false;
796
797 Viewport *embedder_vp = get_embedder();
798
799 if (!embedder_vp) {
800 if (!p_visible && window_id != DisplayServer::INVALID_WINDOW_ID) {
801 _clear_window();
802 }
803 if (p_visible && window_id == DisplayServer::INVALID_WINDOW_ID) {
804 _make_window();
805 }
806 } else {
807 if (visible) {
808 embedder = embedder_vp;
809 if (initial_position != WINDOW_INITIAL_POSITION_ABSOLUTE) {
810 position = (embedder->get_visible_rect().size - size) / 2;
811 }
812 embedder->_sub_window_register(this);
813 RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE);
814 } else {
815 embedder->_sub_window_remove(this);
816 embedder = nullptr;
817 RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED);
818 }
819 _update_window_size();
820 }
821
822 if (!visible) {
823 focused = false;
824 }
825 notification(NOTIFICATION_VISIBILITY_CHANGED);
826 emit_signal(SceneStringNames::get_singleton()->visibility_changed);
827
828 RS::get_singleton()->viewport_set_active(get_viewport_rid(), visible);
829
830 //update transient exclusive
831 if (transient_parent) {
832 if (exclusive && visible) {
833 if (!is_in_edited_scene_root()) {
834 ERR_FAIL_COND_MSG(transient_parent->exclusive_child && transient_parent->exclusive_child != this, "Transient parent has another exclusive child.");
835 transient_parent->exclusive_child = this;
836 }
837 } else {
838 if (transient_parent->exclusive_child == this) {
839 transient_parent->exclusive_child = nullptr;
840 }
841 }
842 }
843}
844
845void Window::_clear_transient() {
846 if (transient_parent) {
847 if (transient_parent->window_id != DisplayServer::INVALID_WINDOW_ID && window_id != DisplayServer::INVALID_WINDOW_ID) {
848 DisplayServer::get_singleton()->window_set_transient(window_id, DisplayServer::INVALID_WINDOW_ID);
849 }
850 transient_parent->transient_children.erase(this);
851 if (transient_parent->exclusive_child == this) {
852 transient_parent->exclusive_child = nullptr;
853 }
854 transient_parent = nullptr;
855 }
856}
857
858void Window::_make_transient() {
859 if (!get_parent()) {
860 //main window, can't be transient
861 return;
862 }
863 //find transient parent
864 Viewport *vp = get_parent()->get_viewport();
865 Window *window = nullptr;
866 while (vp) {
867 window = Object::cast_to<Window>(vp);
868 if (window) {
869 break;
870 }
871 if (!vp->get_parent()) {
872 break;
873 }
874
875 vp = vp->get_parent()->get_viewport();
876 }
877
878 if (window) {
879 transient_parent = window;
880 window->transient_children.insert(this);
881 if (is_inside_tree() && is_visible() && exclusive) {
882 if (transient_parent->exclusive_child == nullptr) {
883 if (!is_in_edited_scene_root()) {
884 transient_parent->exclusive_child = this;
885 }
886 } else if (transient_parent->exclusive_child != this) {
887 ERR_PRINT("Making child transient exclusive, but parent has another exclusive child");
888 }
889 }
890 }
891
892 //see if we can make transient
893 if (transient_parent->window_id != DisplayServer::INVALID_WINDOW_ID && window_id != DisplayServer::INVALID_WINDOW_ID) {
894 DisplayServer::get_singleton()->window_set_transient(window_id, transient_parent->window_id);
895 }
896}
897
898void Window::set_transient(bool p_transient) {
899 ERR_MAIN_THREAD_GUARD;
900 if (transient == p_transient) {
901 return;
902 }
903
904 transient = p_transient;
905
906 if (!is_inside_tree()) {
907 return;
908 }
909
910 if (transient) {
911 _make_transient();
912 } else {
913 _clear_transient();
914 }
915}
916
917bool Window::is_transient() const {
918 ERR_READ_THREAD_GUARD_V(false);
919 return transient;
920}
921
922void Window::set_exclusive(bool p_exclusive) {
923 ERR_MAIN_THREAD_GUARD;
924 if (exclusive == p_exclusive) {
925 return;
926 }
927
928 exclusive = p_exclusive;
929
930 if (!embedder && window_id != DisplayServer::INVALID_WINDOW_ID) {
931 if (is_in_edited_scene_root()) {
932 DisplayServer::get_singleton()->window_set_exclusive(window_id, false);
933 } else {
934 DisplayServer::get_singleton()->window_set_exclusive(window_id, exclusive);
935 }
936 }
937
938 if (transient_parent) {
939 if (p_exclusive && is_inside_tree() && is_visible()) {
940 ERR_FAIL_COND_MSG(transient_parent->exclusive_child && transient_parent->exclusive_child != this, "Transient parent has another exclusive child.");
941 if (!is_in_edited_scene_root()) {
942 transient_parent->exclusive_child = this;
943 }
944 } else {
945 if (transient_parent->exclusive_child == this) {
946 transient_parent->exclusive_child = nullptr;
947 }
948 }
949 }
950}
951
952bool Window::is_exclusive() const {
953 ERR_READ_THREAD_GUARD_V(false);
954 return exclusive;
955}
956
957bool Window::is_visible() const {
958 ERR_READ_THREAD_GUARD_V(false);
959 return visible;
960}
961
962Size2i Window::_clamp_window_size(const Size2i &p_size) {
963 Size2i window_size_clamped = p_size;
964 Size2 minsize = get_clamped_minimum_size();
965 window_size_clamped = window_size_clamped.max(minsize);
966
967 if (max_size_used != Size2i()) {
968 window_size_clamped = window_size_clamped.min(max_size_used);
969 }
970
971 return window_size_clamped;
972}
973
974void Window::_update_window_size() {
975 Size2i size_limit = get_clamped_minimum_size();
976
977 size = size.max(size_limit);
978
979 bool reset_min_first = false;
980
981 if (max_size_used != Size2i()) {
982 // Force window size to respect size limitations of max_size_used.
983 size = size.min(max_size_used);
984
985 if (size_limit.x > max_size_used.x) {
986 size_limit.x = max_size_used.x;
987 reset_min_first = true;
988 }
989 if (size_limit.y > max_size_used.y) {
990 size_limit.y = max_size_used.y;
991 reset_min_first = true;
992 }
993 }
994
995 if (embedder) {
996 size.x = MAX(size.x, 1);
997 size.y = MAX(size.y, 1);
998
999 embedder->_sub_window_update(this);
1000 } else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
1001 if (reset_min_first && wrap_controls) {
1002 // Avoid an error if setting max_size to a value between min_size and the previous size_limit.
1003 DisplayServer::get_singleton()->window_set_min_size(Size2i(), window_id);
1004 }
1005
1006 DisplayServer::get_singleton()->window_set_max_size(max_size_used, window_id);
1007 DisplayServer::get_singleton()->window_set_min_size(size_limit, window_id);
1008 DisplayServer::get_singleton()->window_set_size(size, window_id);
1009 }
1010
1011 //update the viewport
1012 _update_viewport_size();
1013}
1014
1015void Window::_update_viewport_size() {
1016 //update the viewport part
1017
1018 Size2i final_size;
1019 Size2i final_size_override;
1020 Rect2i attach_to_screen_rect(Point2i(), size);
1021 float font_oversampling = 1.0;
1022 window_transform = Transform2D();
1023
1024 if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_INTEGER) {
1025 // We always want to make sure that the content scale factor is a whole
1026 // number, else there will be pixel wobble no matter what.
1027 content_scale_factor = Math::floor(content_scale_factor);
1028
1029 // A content scale factor of zero is pretty useless.
1030 if (content_scale_factor < 1) {
1031 content_scale_factor = 1;
1032 }
1033 }
1034
1035 if (content_scale_mode == CONTENT_SCALE_MODE_DISABLED || content_scale_size.x == 0 || content_scale_size.y == 0) {
1036 font_oversampling = content_scale_factor;
1037 final_size = size;
1038 final_size_override = Size2(size) / content_scale_factor;
1039 } else {
1040 //actual screen video mode
1041 Size2 video_mode = size;
1042 Size2 desired_res = content_scale_size;
1043
1044 Size2 viewport_size;
1045 Size2 screen_size;
1046
1047 float viewport_aspect = desired_res.aspect();
1048 float video_mode_aspect = video_mode.aspect();
1049
1050 if (content_scale_aspect == CONTENT_SCALE_ASPECT_IGNORE || Math::is_equal_approx(viewport_aspect, video_mode_aspect)) {
1051 //same aspect or ignore aspect
1052 viewport_size = desired_res;
1053 screen_size = video_mode;
1054 } else if (viewport_aspect < video_mode_aspect) {
1055 // screen ratio is smaller vertically
1056
1057 if (content_scale_aspect == CONTENT_SCALE_ASPECT_KEEP_HEIGHT || content_scale_aspect == CONTENT_SCALE_ASPECT_EXPAND) {
1058 //will stretch horizontally
1059 viewport_size.x = desired_res.y * video_mode_aspect;
1060 viewport_size.y = desired_res.y;
1061 screen_size = video_mode;
1062
1063 } else {
1064 //will need black bars
1065 viewport_size = desired_res;
1066 screen_size.x = video_mode.y * viewport_aspect;
1067 screen_size.y = video_mode.y;
1068 }
1069 } else {
1070 //screen ratio is smaller horizontally
1071 if (content_scale_aspect == CONTENT_SCALE_ASPECT_KEEP_WIDTH || content_scale_aspect == CONTENT_SCALE_ASPECT_EXPAND) {
1072 //will stretch horizontally
1073 viewport_size.x = desired_res.x;
1074 viewport_size.y = desired_res.x / video_mode_aspect;
1075 screen_size = video_mode;
1076
1077 } else {
1078 //will need black bars
1079 viewport_size = desired_res;
1080 screen_size.x = video_mode.x;
1081 screen_size.y = video_mode.x / viewport_aspect;
1082 }
1083 }
1084
1085 screen_size = screen_size.floor();
1086 viewport_size = viewport_size.floor();
1087
1088 if (content_scale_stretch == Window::CONTENT_SCALE_STRETCH_INTEGER) {
1089 Size2i screen_scale = (screen_size / viewport_size).floor();
1090 int scale_factor = MIN(screen_scale.x, screen_scale.y);
1091
1092 if (scale_factor < 1) {
1093 scale_factor = 1;
1094 }
1095
1096 screen_size = viewport_size * scale_factor;
1097 }
1098
1099 Size2 margin;
1100 Size2 offset;
1101
1102 if (screen_size.x < video_mode.x) {
1103 margin.x = Math::round((video_mode.x - screen_size.x) / 2.0);
1104 offset.x = Math::round(margin.x * viewport_size.y / screen_size.y);
1105 }
1106
1107 if (screen_size.y < video_mode.y) {
1108 margin.y = Math::round((video_mode.y - screen_size.y) / 2.0);
1109 offset.y = Math::round(margin.y * viewport_size.x / screen_size.x);
1110 }
1111
1112 switch (content_scale_mode) {
1113 case CONTENT_SCALE_MODE_DISABLED: {
1114 // Already handled above
1115 //_update_font_oversampling(1.0);
1116 } break;
1117 case CONTENT_SCALE_MODE_CANVAS_ITEMS: {
1118 final_size = screen_size;
1119 final_size_override = viewport_size / content_scale_factor;
1120 attach_to_screen_rect = Rect2(margin, screen_size);
1121 font_oversampling = (screen_size.x / viewport_size.x) * content_scale_factor;
1122
1123 window_transform.translate_local(margin);
1124 } break;
1125 case CONTENT_SCALE_MODE_VIEWPORT: {
1126 final_size = (viewport_size / content_scale_factor).floor();
1127 attach_to_screen_rect = Rect2(margin, screen_size);
1128
1129 window_transform.translate_local(margin);
1130 if (final_size.x != 0 && final_size.y != 0) {
1131 Transform2D scale_transform;
1132 scale_transform.scale(Vector2(attach_to_screen_rect.size) / Vector2(final_size));
1133 window_transform *= scale_transform;
1134 }
1135 } break;
1136 }
1137 }
1138
1139 bool allocate = is_inside_tree() && visible && (window_id != DisplayServer::INVALID_WINDOW_ID || embedder != nullptr);
1140 _set_size(final_size, final_size_override, allocate);
1141
1142 if (window_id != DisplayServer::INVALID_WINDOW_ID) {
1143 RenderingServer::get_singleton()->viewport_attach_to_screen(get_viewport_rid(), attach_to_screen_rect, window_id);
1144 } else {
1145 RenderingServer::get_singleton()->viewport_attach_to_screen(get_viewport_rid(), Rect2i(), DisplayServer::INVALID_WINDOW_ID);
1146 }
1147
1148 if (window_id == DisplayServer::MAIN_WINDOW_ID) {
1149 if (!use_font_oversampling) {
1150 font_oversampling = 1.0;
1151 }
1152 if (TS->font_get_global_oversampling() != font_oversampling) {
1153 TS->font_set_global_oversampling(font_oversampling);
1154 }
1155 }
1156
1157 notification(NOTIFICATION_WM_SIZE_CHANGED);
1158
1159 if (embedder) {
1160 embedder->_sub_window_update(this);
1161 }
1162}
1163
1164void Window::_update_window_callbacks() {
1165 DisplayServer::get_singleton()->window_set_rect_changed_callback(callable_mp(this, &Window::_rect_changed_callback), window_id);
1166 DisplayServer::get_singleton()->window_set_window_event_callback(callable_mp(this, &Window::_event_callback), window_id);
1167 DisplayServer::get_singleton()->window_set_input_event_callback(callable_mp(this, &Window::_window_input), window_id);
1168 DisplayServer::get_singleton()->window_set_input_text_callback(callable_mp(this, &Window::_window_input_text), window_id);
1169 DisplayServer::get_singleton()->window_set_drop_files_callback(callable_mp(this, &Window::_window_drop_files), window_id);
1170}
1171
1172Viewport *Window::get_embedder() const {
1173 ERR_READ_THREAD_GUARD_V(nullptr);
1174 Viewport *vp = get_parent_viewport();
1175
1176 while (vp) {
1177 if (vp->is_embedding_subwindows()) {
1178 return vp;
1179 }
1180
1181 if (vp->get_parent()) {
1182 vp = vp->get_parent()->get_viewport();
1183 } else {
1184 vp = nullptr;
1185 }
1186 }
1187 return nullptr;
1188}
1189
1190void Window::_notification(int p_what) {
1191 ERR_MAIN_THREAD_GUARD;
1192 switch (p_what) {
1193 case NOTIFICATION_POSTINITIALIZE: {
1194 initialized = true;
1195
1196 _invalidate_theme_cache();
1197 _update_theme_item_cache();
1198 } break;
1199
1200 case NOTIFICATION_PARENTED: {
1201 theme_owner->assign_theme_on_parented(this);
1202 } break;
1203
1204 case NOTIFICATION_UNPARENTED: {
1205 theme_owner->clear_theme_on_unparented(this);
1206 } break;
1207
1208 case NOTIFICATION_ENTER_TREE: {
1209 bool embedded = false;
1210 {
1211 embedder = get_embedder();
1212 if (embedder) {
1213 embedded = true;
1214 if (!visible) {
1215 embedder = nullptr; // Not yet since not visible.
1216 }
1217 }
1218 }
1219
1220 if (embedded) {
1221 // Create as embedded.
1222 if (embedder) {
1223 if (initial_position != WINDOW_INITIAL_POSITION_ABSOLUTE) {
1224 position = (embedder->get_visible_rect().size - size) / 2;
1225 }
1226 embedder->_sub_window_register(this);
1227 RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE);
1228 _update_window_size();
1229 }
1230
1231 } else {
1232 if (!get_parent()) {
1233 // It's the root window!
1234 visible = true; // Always visible.
1235 window_id = DisplayServer::MAIN_WINDOW_ID;
1236 DisplayServer::get_singleton()->window_attach_instance_id(get_instance_id(), window_id);
1237 _update_from_window();
1238 // Since this window already exists (created on start), we must update pos and size from it.
1239 {
1240 position = DisplayServer::get_singleton()->window_get_position(window_id);
1241 size = DisplayServer::get_singleton()->window_get_size(window_id);
1242 focused = DisplayServer::get_singleton()->window_is_focused(window_id);
1243 }
1244 _update_window_size(); // Inform DisplayServer of minimum and maximum size.
1245 _update_viewport_size(); // Then feed back to the viewport.
1246 _update_window_callbacks();
1247 RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_VISIBLE);
1248 } else {
1249 // Create.
1250 if (visible) {
1251 _make_window();
1252 }
1253 }
1254 }
1255
1256 if (transient) {
1257 _make_transient();
1258 }
1259 if (visible) {
1260 notification(NOTIFICATION_VISIBILITY_CHANGED);
1261 emit_signal(SceneStringNames::get_singleton()->visibility_changed);
1262 RS::get_singleton()->viewport_set_active(get_viewport_rid(), true);
1263 }
1264
1265#ifdef TOOLS_ENABLED
1266 if (is_part_of_edited_scene()) {
1267 // Don't translate Windows on scene when inside editor.
1268 set_message_translation(false);
1269 notification(NOTIFICATION_TRANSLATION_CHANGED);
1270 }
1271#endif
1272
1273 // Emits NOTIFICATION_THEME_CHANGED internally.
1274 set_theme_context(ThemeDB::get_singleton()->get_nearest_theme_context(this));
1275 } break;
1276
1277 case NOTIFICATION_READY: {
1278 if (wrap_controls) {
1279 // Finish any resizing immediately so it doesn't interfere on stuff overriding _ready().
1280 _update_child_controls();
1281 }
1282 } break;
1283
1284 case NOTIFICATION_THEME_CHANGED: {
1285 emit_signal(SceneStringNames::get_singleton()->theme_changed);
1286 _invalidate_theme_cache();
1287 _update_theme_item_cache();
1288 } break;
1289
1290 case NOTIFICATION_TRANSLATION_CHANGED: {
1291 _invalidate_theme_cache();
1292 _update_theme_item_cache();
1293
1294 if (!embedder && window_id != DisplayServer::INVALID_WINDOW_ID) {
1295 String tr_title = atr(title);
1296#ifdef DEBUG_ENABLED
1297 if (window_id == DisplayServer::MAIN_WINDOW_ID) {
1298 // Append a suffix to the window title to denote that the project is running
1299 // from a debug build (including the editor). Since this results in lower performance,
1300 // this should be clearly presented to the user.
1301 tr_title = vformat("%s (DEBUG)", tr_title);
1302 }
1303#endif
1304 DisplayServer::get_singleton()->window_set_title(tr_title, window_id);
1305 }
1306 } break;
1307
1308 case NOTIFICATION_VISIBILITY_CHANGED: {
1309 if (unparent_when_invisible && !is_visible()) {
1310 Node *p = get_parent();
1311 if (p) {
1312 p->remove_child(this);
1313 }
1314 }
1315 } break;
1316
1317 case NOTIFICATION_EXIT_TREE: {
1318 set_theme_context(nullptr, false);
1319
1320 if (transient) {
1321 _clear_transient();
1322 }
1323
1324 if (!is_embedded() && window_id != DisplayServer::INVALID_WINDOW_ID) {
1325 if (window_id == DisplayServer::MAIN_WINDOW_ID) {
1326 RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED);
1327 _update_window_callbacks();
1328 } else {
1329 _clear_window();
1330 }
1331 } else {
1332 if (embedder) {
1333 embedder->_sub_window_remove(this);
1334 embedder = nullptr;
1335 RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED);
1336 }
1337 _update_viewport_size(); //called by clear and make, which does not happen here
1338 }
1339
1340 RS::get_singleton()->viewport_set_active(get_viewport_rid(), false);
1341 } break;
1342
1343 case NOTIFICATION_VP_MOUSE_ENTER: {
1344 emit_signal(SceneStringNames::get_singleton()->mouse_entered);
1345 } break;
1346
1347 case NOTIFICATION_VP_MOUSE_EXIT: {
1348 emit_signal(SceneStringNames::get_singleton()->mouse_exited);
1349 } break;
1350 }
1351}
1352
1353void Window::set_content_scale_size(const Size2i &p_size) {
1354 ERR_MAIN_THREAD_GUARD;
1355 ERR_FAIL_COND(p_size.x < 0);
1356 ERR_FAIL_COND(p_size.y < 0);
1357 content_scale_size = p_size;
1358 _update_viewport_size();
1359}
1360
1361Size2i Window::get_content_scale_size() const {
1362 ERR_READ_THREAD_GUARD_V(Size2i());
1363 return content_scale_size;
1364}
1365
1366void Window::set_content_scale_mode(ContentScaleMode p_mode) {
1367 ERR_MAIN_THREAD_GUARD;
1368 content_scale_mode = p_mode;
1369 _update_viewport_size();
1370}
1371
1372Window::ContentScaleMode Window::get_content_scale_mode() const {
1373 ERR_READ_THREAD_GUARD_V(CONTENT_SCALE_MODE_DISABLED);
1374 return content_scale_mode;
1375}
1376
1377void Window::set_content_scale_aspect(ContentScaleAspect p_aspect) {
1378 ERR_MAIN_THREAD_GUARD;
1379 content_scale_aspect = p_aspect;
1380 _update_viewport_size();
1381}
1382
1383Window::ContentScaleAspect Window::get_content_scale_aspect() const {
1384 ERR_READ_THREAD_GUARD_V(CONTENT_SCALE_ASPECT_IGNORE);
1385 return content_scale_aspect;
1386}
1387
1388void Window::set_content_scale_stretch(ContentScaleStretch p_stretch) {
1389 content_scale_stretch = p_stretch;
1390 _update_viewport_size();
1391}
1392
1393Window::ContentScaleStretch Window::get_content_scale_stretch() const {
1394 return content_scale_stretch;
1395}
1396
1397void Window::set_content_scale_factor(real_t p_factor) {
1398 ERR_MAIN_THREAD_GUARD;
1399 ERR_FAIL_COND(p_factor <= 0);
1400 content_scale_factor = p_factor;
1401 _update_viewport_size();
1402}
1403
1404real_t Window::get_content_scale_factor() const {
1405 ERR_READ_THREAD_GUARD_V(0);
1406 return content_scale_factor;
1407}
1408
1409void Window::set_use_font_oversampling(bool p_oversampling) {
1410 ERR_MAIN_THREAD_GUARD;
1411 if (is_inside_tree() && window_id != DisplayServer::MAIN_WINDOW_ID) {
1412 ERR_FAIL_MSG("Only the root window can set and use font oversampling.");
1413 }
1414 use_font_oversampling = p_oversampling;
1415 _update_viewport_size();
1416}
1417
1418bool Window::is_using_font_oversampling() const {
1419 ERR_READ_THREAD_GUARD_V(false);
1420 return use_font_oversampling;
1421}
1422
1423DisplayServer::WindowID Window::get_window_id() const {
1424 ERR_READ_THREAD_GUARD_V(DisplayServer::INVALID_WINDOW_ID);
1425 if (embedder) {
1426 return parent->get_window_id();
1427 }
1428 return window_id;
1429}
1430
1431void Window::set_mouse_passthrough_polygon(const Vector<Vector2> &p_region) {
1432 ERR_MAIN_THREAD_GUARD;
1433 mpath = p_region;
1434 if (window_id == DisplayServer::INVALID_WINDOW_ID) {
1435 return;
1436 }
1437 DisplayServer::get_singleton()->window_set_mouse_passthrough(mpath, window_id);
1438}
1439
1440Vector<Vector2> Window::get_mouse_passthrough_polygon() const {
1441 return mpath;
1442}
1443
1444void Window::set_wrap_controls(bool p_enable) {
1445 ERR_MAIN_THREAD_GUARD;
1446 wrap_controls = p_enable;
1447
1448 if (!is_inside_tree()) {
1449 return;
1450 }
1451
1452 if (updating_child_controls) {
1453 _update_child_controls();
1454 } else {
1455 _update_window_size();
1456 }
1457}
1458
1459bool Window::is_wrapping_controls() const {
1460 ERR_READ_THREAD_GUARD_V(false);
1461 return wrap_controls;
1462}
1463
1464Size2 Window::_get_contents_minimum_size() const {
1465 Size2 max;
1466
1467 for (int i = 0; i < get_child_count(); i++) {
1468 Control *c = Object::cast_to<Control>(get_child(i));
1469 if (c) {
1470 Point2i pos = c->get_position();
1471 Size2i min = c->get_combined_minimum_size();
1472
1473 max.x = MAX(pos.x + min.x, max.x);
1474 max.y = MAX(pos.y + min.y, max.y);
1475 }
1476 }
1477
1478 return max;
1479}
1480
1481void Window::child_controls_changed() {
1482 ERR_MAIN_THREAD_GUARD;
1483 if (!is_inside_tree() || !visible || updating_child_controls) {
1484 return;
1485 }
1486
1487 updating_child_controls = true;
1488 call_deferred(SNAME("_update_child_controls"));
1489}
1490
1491void Window::_update_child_controls() {
1492 if (!updating_child_controls) {
1493 return;
1494 }
1495
1496 _update_window_size();
1497
1498 updating_child_controls = false;
1499}
1500
1501bool Window::_can_consume_input_events() const {
1502 return exclusive_child == nullptr;
1503}
1504
1505void Window::_window_input(const Ref<InputEvent> &p_ev) {
1506 if (EngineDebugger::is_active()) {
1507 // Quit from game window using the stop shortcut (F8 by default).
1508 // The custom shortcut is provided via environment variable when running from the editor.
1509 if (debugger_stop_shortcut.is_null()) {
1510 String shortcut_str = OS::get_singleton()->get_environment("__GODOT_EDITOR_STOP_SHORTCUT__");
1511 if (!shortcut_str.is_empty()) {
1512 Variant shortcut_var;
1513
1514 VariantParser::StreamString ss;
1515 ss.s = shortcut_str;
1516
1517 String errs;
1518 int line;
1519 VariantParser::parse(&ss, shortcut_var, errs, line);
1520 debugger_stop_shortcut = shortcut_var;
1521 }
1522
1523 if (debugger_stop_shortcut.is_null()) {
1524 // Define a default shortcut if it wasn't provided or is invalid.
1525 debugger_stop_shortcut.instantiate();
1526 debugger_stop_shortcut->set_events({ (Variant)InputEventKey::create_reference(Key::F8) });
1527 }
1528 }
1529
1530 Ref<InputEventKey> k = p_ev;
1531 if (k.is_valid() && k->is_pressed() && !k->is_echo() && debugger_stop_shortcut->matches_event(k)) {
1532 EngineDebugger::get_singleton()->send_message("request_quit", Array());
1533 }
1534 }
1535
1536 if (exclusive_child != nullptr) {
1537 if (!is_embedding_subwindows()) { // Not embedding, no need for event.
1538 return;
1539 }
1540 }
1541
1542 if (p_ev->get_device() != InputEvent::DEVICE_ID_INTERNAL) {
1543 emit_signal(SceneStringNames::get_singleton()->window_input, p_ev);
1544 }
1545
1546 if (is_inside_tree()) {
1547 push_input(p_ev);
1548 }
1549}
1550
1551void Window::_window_input_text(const String &p_text) {
1552 push_text_input(p_text);
1553}
1554
1555void Window::_window_drop_files(const Vector<String> &p_files) {
1556 emit_signal(SNAME("files_dropped"), p_files);
1557}
1558
1559Viewport *Window::get_parent_viewport() const {
1560 ERR_READ_THREAD_GUARD_V(nullptr);
1561 if (get_parent()) {
1562 return get_parent()->get_viewport();
1563 } else {
1564 return nullptr;
1565 }
1566}
1567
1568Window *Window::get_parent_visible_window() const {
1569 ERR_READ_THREAD_GUARD_V(nullptr);
1570 Viewport *vp = get_parent_viewport();
1571 Window *window = nullptr;
1572 while (vp) {
1573 window = Object::cast_to<Window>(vp);
1574 if (window && window->visible) {
1575 break;
1576 }
1577 if (!vp->get_parent()) {
1578 break;
1579 }
1580
1581 vp = vp->get_parent()->get_viewport();
1582 }
1583 return window;
1584}
1585
1586void Window::popup_on_parent(const Rect2i &p_parent_rect) {
1587 ERR_MAIN_THREAD_GUARD;
1588 ERR_FAIL_COND(!is_inside_tree());
1589 ERR_FAIL_COND_MSG(window_id == DisplayServer::MAIN_WINDOW_ID, "Can't popup the main window.");
1590
1591 if (!is_embedded()) {
1592 Window *window = get_parent_visible_window();
1593
1594 if (!window) {
1595 popup(p_parent_rect);
1596 } else {
1597 popup(Rect2i(window->get_position() + p_parent_rect.position, p_parent_rect.size));
1598 }
1599 } else {
1600 popup(p_parent_rect);
1601 }
1602}
1603
1604void Window::popup_centered_clamped(const Size2i &p_size, float p_fallback_ratio) {
1605 ERR_MAIN_THREAD_GUARD;
1606 ERR_FAIL_COND(!is_inside_tree());
1607 ERR_FAIL_COND_MSG(window_id == DisplayServer::MAIN_WINDOW_ID, "Can't popup the main window.");
1608
1609 // Consider the current size when calling with the default value.
1610 Size2i expected_size = p_size == Size2i() ? size : p_size;
1611
1612 Rect2 parent_rect;
1613
1614 if (is_embedded()) {
1615 parent_rect = get_embedder()->get_visible_rect();
1616 } else {
1617 DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id();
1618 int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id);
1619 parent_rect.position = DisplayServer::get_singleton()->screen_get_position(parent_screen);
1620 parent_rect.size = DisplayServer::get_singleton()->screen_get_size(parent_screen);
1621 }
1622
1623 Vector2i size_ratio = parent_rect.size * p_fallback_ratio;
1624
1625 Rect2i popup_rect;
1626 popup_rect.size = Vector2i(MIN(size_ratio.x, expected_size.x), MIN(size_ratio.y, expected_size.y));
1627 popup_rect.size = _clamp_window_size(popup_rect.size);
1628
1629 if (parent_rect != Rect2()) {
1630 popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
1631 }
1632
1633 popup(popup_rect);
1634}
1635
1636void Window::popup_centered(const Size2i &p_minsize) {
1637 ERR_MAIN_THREAD_GUARD;
1638 ERR_FAIL_COND(!is_inside_tree());
1639 ERR_FAIL_COND_MSG(window_id == DisplayServer::MAIN_WINDOW_ID, "Can't popup the main window.");
1640
1641 // Consider the current size when calling with the default value.
1642 Size2i expected_size = p_minsize == Size2i() ? size : p_minsize;
1643
1644 Rect2 parent_rect;
1645
1646 if (is_embedded()) {
1647 parent_rect = get_embedder()->get_visible_rect();
1648 } else {
1649 DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id();
1650 int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id);
1651 parent_rect.position = DisplayServer::get_singleton()->screen_get_position(parent_screen);
1652 parent_rect.size = DisplayServer::get_singleton()->screen_get_size(parent_screen);
1653 }
1654
1655 Rect2i popup_rect;
1656 popup_rect.size = _clamp_window_size(expected_size);
1657
1658 if (parent_rect != Rect2()) {
1659 popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
1660 }
1661
1662 popup(popup_rect);
1663}
1664
1665void Window::popup_centered_ratio(float p_ratio) {
1666 ERR_MAIN_THREAD_GUARD;
1667 ERR_FAIL_COND(!is_inside_tree());
1668 ERR_FAIL_COND_MSG(window_id == DisplayServer::MAIN_WINDOW_ID, "Can't popup the main window.");
1669 ERR_FAIL_COND_MSG(p_ratio <= 0.0 || p_ratio > 1.0, "Ratio must be between 0.0 and 1.0!");
1670
1671 Rect2 parent_rect;
1672
1673 if (is_embedded()) {
1674 parent_rect = get_embedder()->get_visible_rect();
1675 } else {
1676 DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id();
1677 int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id);
1678 parent_rect.position = DisplayServer::get_singleton()->screen_get_position(parent_screen);
1679 parent_rect.size = DisplayServer::get_singleton()->screen_get_size(parent_screen);
1680 }
1681
1682 Rect2i popup_rect;
1683 if (parent_rect != Rect2()) {
1684 popup_rect.size = parent_rect.size * p_ratio;
1685 popup_rect.size = _clamp_window_size(popup_rect.size);
1686 popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2;
1687 }
1688
1689 popup(popup_rect);
1690}
1691
1692void Window::popup(const Rect2i &p_screen_rect) {
1693 ERR_MAIN_THREAD_GUARD;
1694 emit_signal(SNAME("about_to_popup"));
1695
1696 if (!get_embedder() && get_flag(FLAG_POPUP)) {
1697 // Send a focus-out notification when opening a Window Manager Popup.
1698 SceneTree *scene_tree = get_tree();
1699 if (scene_tree) {
1700 scene_tree->notify_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_viewports", NOTIFICATION_WM_WINDOW_FOCUS_OUT);
1701 }
1702 }
1703
1704 // Update window size to calculate the actual window size based on contents minimum size and minimum size.
1705 _update_window_size();
1706
1707 if (p_screen_rect != Rect2i()) {
1708 set_position(p_screen_rect.position);
1709 set_size(p_screen_rect.size);
1710 }
1711
1712 Rect2i adjust = _popup_adjust_rect();
1713 if (adjust != Rect2i()) {
1714 set_position(adjust.position);
1715 set_size(adjust.size);
1716 }
1717
1718 int scr = DisplayServer::get_singleton()->get_screen_count();
1719 for (int i = 0; i < scr; i++) {
1720 Rect2i r = DisplayServer::get_singleton()->screen_get_usable_rect(i);
1721 if (r.has_point(position)) {
1722 current_screen = i;
1723 break;
1724 }
1725 }
1726
1727 set_transient(true);
1728 set_visible(true);
1729
1730 Rect2i parent_rect;
1731 if (is_embedded()) {
1732 parent_rect = get_embedder()->get_visible_rect();
1733 } else {
1734 int screen_id = DisplayServer::get_singleton()->window_get_current_screen(get_window_id());
1735 parent_rect = DisplayServer::get_singleton()->screen_get_usable_rect(screen_id);
1736 }
1737 if (parent_rect != Rect2i() && !parent_rect.intersects(Rect2i(position, size))) {
1738 ERR_PRINT(vformat("Window %d spawned at invalid position: %s.", get_window_id(), position));
1739 set_position((parent_rect.size - size) / 2);
1740 }
1741 if (parent_rect != Rect2i() && is_clamped_to_embedder() && is_embedded()) {
1742 Rect2i new_rect = fit_rect_in_parent(Rect2i(position, size), parent_rect);
1743 set_position(new_rect.position);
1744 set_size(new_rect.size);
1745 }
1746
1747 _post_popup();
1748 notification(NOTIFICATION_POST_POPUP);
1749}
1750
1751bool Window::_try_parent_dialog(Node *p_from_node) {
1752 ERR_FAIL_NULL_V(p_from_node, false);
1753 ERR_FAIL_COND_V_MSG(is_inside_tree(), false, "Attempting to parent and popup a dialog that already has a parent.");
1754
1755 Window *w = p_from_node->get_last_exclusive_window();
1756 if (w && w != this) {
1757 w->add_child(this);
1758 return true;
1759 }
1760 return false;
1761}
1762
1763void Window::popup_exclusive(Node *p_from_node, const Rect2i &p_screen_rect) {
1764 if (_try_parent_dialog(p_from_node)) {
1765 popup(p_screen_rect);
1766 }
1767}
1768
1769void Window::popup_exclusive_on_parent(Node *p_from_node, const Rect2i &p_parent_rect) {
1770 if (_try_parent_dialog(p_from_node)) {
1771 popup_on_parent(p_parent_rect);
1772 }
1773}
1774
1775void Window::popup_exclusive_centered(Node *p_from_node, const Size2i &p_minsize) {
1776 if (_try_parent_dialog(p_from_node)) {
1777 popup_centered(p_minsize);
1778 }
1779}
1780
1781void Window::popup_exclusive_centered_ratio(Node *p_from_node, float p_ratio) {
1782 if (_try_parent_dialog(p_from_node)) {
1783 popup_centered_ratio(p_ratio);
1784 }
1785}
1786
1787void Window::popup_exclusive_centered_clamped(Node *p_from_node, const Size2i &p_size, float p_fallback_ratio) {
1788 if (_try_parent_dialog(p_from_node)) {
1789 popup_centered_clamped(p_size, p_fallback_ratio);
1790 }
1791}
1792
1793Rect2i Window::fit_rect_in_parent(Rect2i p_rect, const Rect2i &p_parent_rect) const {
1794 ERR_READ_THREAD_GUARD_V(Rect2i());
1795 Size2i limit = p_parent_rect.size;
1796 if (p_rect.position.x + p_rect.size.x > limit.x) {
1797 p_rect.position.x = limit.x - p_rect.size.x;
1798 }
1799 if (p_rect.position.y + p_rect.size.y > limit.y) {
1800 p_rect.position.y = limit.y - p_rect.size.y;
1801 }
1802
1803 if (p_rect.position.x < 0) {
1804 p_rect.position.x = 0;
1805 }
1806
1807 int title_height = get_flag(Window::FLAG_BORDERLESS) ? 0 : theme_cache.title_height;
1808
1809 if (p_rect.position.y < title_height) {
1810 p_rect.position.y = title_height;
1811 }
1812
1813 return p_rect;
1814}
1815
1816Size2 Window::get_contents_minimum_size() const {
1817 ERR_READ_THREAD_GUARD_V(Size2());
1818 Vector2 ms;
1819 if (GDVIRTUAL_CALL(_get_contents_minimum_size, ms)) {
1820 return ms;
1821 }
1822 return _get_contents_minimum_size();
1823}
1824
1825Size2 Window::get_clamped_minimum_size() const {
1826 ERR_READ_THREAD_GUARD_V(Size2());
1827 if (!wrap_controls) {
1828 return min_size;
1829 }
1830
1831 return min_size.max(get_contents_minimum_size());
1832}
1833
1834void Window::grab_focus() {
1835 ERR_MAIN_THREAD_GUARD;
1836 if (embedder) {
1837 embedder->_sub_window_grab_focus(this);
1838 } else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
1839 DisplayServer::get_singleton()->window_move_to_foreground(window_id);
1840 }
1841}
1842
1843bool Window::has_focus() const {
1844 ERR_READ_THREAD_GUARD_V(false);
1845 if (window_id != DisplayServer::INVALID_WINDOW_ID) {
1846 return DisplayServer::get_singleton()->window_is_focused(window_id);
1847 }
1848 return focused;
1849}
1850
1851Rect2i Window::get_usable_parent_rect() const {
1852 ERR_READ_THREAD_GUARD_V(Rect2i());
1853 ERR_FAIL_COND_V(!is_inside_tree(), Rect2());
1854 Rect2i parent_rect;
1855 if (is_embedded()) {
1856 parent_rect = get_embedder()->get_visible_rect();
1857 } else {
1858 const Window *w = is_visible() ? this : get_parent_visible_window();
1859 //find a parent that can contain us
1860 ERR_FAIL_NULL_V(w, Rect2());
1861
1862 parent_rect = DisplayServer::get_singleton()->screen_get_usable_rect(DisplayServer::get_singleton()->window_get_current_screen(w->get_window_id()));
1863 }
1864 return parent_rect;
1865}
1866
1867void Window::add_child_notify(Node *p_child) {
1868 if (is_inside_tree() && wrap_controls) {
1869 child_controls_changed();
1870 }
1871}
1872
1873void Window::remove_child_notify(Node *p_child) {
1874 if (is_inside_tree() && wrap_controls) {
1875 child_controls_changed();
1876 }
1877}
1878
1879// Theming.
1880
1881void Window::set_theme_owner_node(Node *p_node) {
1882 ERR_MAIN_THREAD_GUARD;
1883 theme_owner->set_owner_node(p_node);
1884}
1885
1886Node *Window::get_theme_owner_node() const {
1887 ERR_READ_THREAD_GUARD_V(nullptr);
1888 return theme_owner->get_owner_node();
1889}
1890
1891bool Window::has_theme_owner_node() const {
1892 ERR_READ_THREAD_GUARD_V(false);
1893 return theme_owner->has_owner_node();
1894}
1895
1896void Window::set_theme_context(ThemeContext *p_context, bool p_propagate) {
1897 ERR_MAIN_THREAD_GUARD;
1898 theme_owner->set_owner_context(p_context, p_propagate);
1899}
1900
1901void Window::set_theme(const Ref<Theme> &p_theme) {
1902 ERR_MAIN_THREAD_GUARD;
1903 if (theme == p_theme) {
1904 return;
1905 }
1906
1907 if (theme.is_valid()) {
1908 theme->disconnect_changed(callable_mp(this, &Window::_theme_changed));
1909 }
1910
1911 theme = p_theme;
1912 if (theme.is_valid()) {
1913 theme_owner->propagate_theme_changed(this, this, is_inside_tree(), true);
1914 theme->connect_changed(callable_mp(this, &Window::_theme_changed), CONNECT_DEFERRED);
1915 return;
1916 }
1917
1918 Control *parent_c = Object::cast_to<Control>(get_parent());
1919 if (parent_c && parent_c->has_theme_owner_node()) {
1920 theme_owner->propagate_theme_changed(this, parent_c->get_theme_owner_node(), is_inside_tree(), true);
1921 return;
1922 }
1923
1924 Window *parent_w = cast_to<Window>(get_parent());
1925 if (parent_w && parent_w->has_theme_owner_node()) {
1926 theme_owner->propagate_theme_changed(this, parent_w->get_theme_owner_node(), is_inside_tree(), true);
1927 return;
1928 }
1929
1930 theme_owner->propagate_theme_changed(this, nullptr, is_inside_tree(), true);
1931}
1932
1933Ref<Theme> Window::get_theme() const {
1934 ERR_READ_THREAD_GUARD_V(Ref<Theme>());
1935 return theme;
1936}
1937
1938void Window::_theme_changed() {
1939 if (is_inside_tree()) {
1940 theme_owner->propagate_theme_changed(this, this, true, false);
1941 }
1942}
1943
1944void Window::_notify_theme_override_changed() {
1945 if (!bulk_theme_override && is_inside_tree()) {
1946 notification(NOTIFICATION_THEME_CHANGED);
1947 }
1948}
1949
1950void Window::_invalidate_theme_cache() {
1951 theme_icon_cache.clear();
1952 theme_style_cache.clear();
1953 theme_font_cache.clear();
1954 theme_font_size_cache.clear();
1955 theme_color_cache.clear();
1956 theme_constant_cache.clear();
1957}
1958
1959void Window::_update_theme_item_cache() {
1960 // Request an update on the next frame to reflect theme changes.
1961 // Updating without a delay can cause a lot of lag.
1962 if (!wrap_controls) {
1963 updating_embedded_window = true;
1964 call_deferred(SNAME("_update_embedded_window"));
1965 } else {
1966 child_controls_changed();
1967 }
1968
1969 ThemeDB::get_singleton()->update_class_instance_items(this);
1970}
1971
1972void Window::_update_embedded_window() {
1973 if (!updating_embedded_window) {
1974 return;
1975 }
1976
1977 if (embedder) {
1978 embedder->_sub_window_update(this);
1979 };
1980
1981 updating_embedded_window = false;
1982}
1983
1984void Window::set_theme_type_variation(const StringName &p_theme_type) {
1985 ERR_MAIN_THREAD_GUARD;
1986 theme_type_variation = p_theme_type;
1987 if (is_inside_tree()) {
1988 notification(NOTIFICATION_THEME_CHANGED);
1989 }
1990}
1991
1992StringName Window::get_theme_type_variation() const {
1993 ERR_READ_THREAD_GUARD_V(StringName());
1994 return theme_type_variation;
1995}
1996
1997/// Theme property lookup.
1998
1999Ref<Texture2D> Window::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const {
2000 ERR_READ_THREAD_GUARD_V(Ref<Texture2D>());
2001 if (!initialized) {
2002 WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED");
2003 }
2004
2005 if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) {
2006 const Ref<Texture2D> *tex = theme_icon_override.getptr(p_name);
2007 if (tex) {
2008 return *tex;
2009 }
2010 }
2011
2012 if (theme_icon_cache.has(p_theme_type) && theme_icon_cache[p_theme_type].has(p_name)) {
2013 return theme_icon_cache[p_theme_type][p_name];
2014 }
2015
2016 List<StringName> theme_types;
2017 theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
2018 Ref<Texture2D> icon = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_ICON, p_name, theme_types);
2019 theme_icon_cache[p_theme_type][p_name] = icon;
2020 return icon;
2021}
2022
2023Ref<StyleBox> Window::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
2024 ERR_READ_THREAD_GUARD_V(Ref<StyleBox>());
2025 if (!initialized) {
2026 WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED");
2027 }
2028
2029 if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) {
2030 const Ref<StyleBox> *style = theme_style_override.getptr(p_name);
2031 if (style) {
2032 return *style;
2033 }
2034 }
2035
2036 if (theme_style_cache.has(p_theme_type) && theme_style_cache[p_theme_type].has(p_name)) {
2037 return theme_style_cache[p_theme_type][p_name];
2038 }
2039
2040 List<StringName> theme_types;
2041 theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
2042 Ref<StyleBox> style = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_STYLEBOX, p_name, theme_types);
2043 theme_style_cache[p_theme_type][p_name] = style;
2044 return style;
2045}
2046
2047Ref<Font> Window::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const {
2048 ERR_READ_THREAD_GUARD_V(Ref<Font>());
2049 if (!initialized) {
2050 WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED");
2051 }
2052
2053 if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) {
2054 const Ref<Font> *font = theme_font_override.getptr(p_name);
2055 if (font) {
2056 return *font;
2057 }
2058 }
2059
2060 if (theme_font_cache.has(p_theme_type) && theme_font_cache[p_theme_type].has(p_name)) {
2061 return theme_font_cache[p_theme_type][p_name];
2062 }
2063
2064 List<StringName> theme_types;
2065 theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
2066 Ref<Font> font = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_FONT, p_name, theme_types);
2067 theme_font_cache[p_theme_type][p_name] = font;
2068 return font;
2069}
2070
2071int Window::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const {
2072 ERR_READ_THREAD_GUARD_V(0);
2073 if (!initialized) {
2074 WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED");
2075 }
2076
2077 if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) {
2078 const int *font_size = theme_font_size_override.getptr(p_name);
2079 if (font_size && (*font_size) > 0) {
2080 return *font_size;
2081 }
2082 }
2083
2084 if (theme_font_size_cache.has(p_theme_type) && theme_font_size_cache[p_theme_type].has(p_name)) {
2085 return theme_font_size_cache[p_theme_type][p_name];
2086 }
2087
2088 List<StringName> theme_types;
2089 theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
2090 int font_size = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types);
2091 theme_font_size_cache[p_theme_type][p_name] = font_size;
2092 return font_size;
2093}
2094
2095Color Window::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const {
2096 ERR_READ_THREAD_GUARD_V(Color());
2097 if (!initialized) {
2098 WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED");
2099 }
2100
2101 if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) {
2102 const Color *color = theme_color_override.getptr(p_name);
2103 if (color) {
2104 return *color;
2105 }
2106 }
2107
2108 if (theme_color_cache.has(p_theme_type) && theme_color_cache[p_theme_type].has(p_name)) {
2109 return theme_color_cache[p_theme_type][p_name];
2110 }
2111
2112 List<StringName> theme_types;
2113 theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
2114 Color color = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_COLOR, p_name, theme_types);
2115 theme_color_cache[p_theme_type][p_name] = color;
2116 return color;
2117}
2118
2119int Window::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const {
2120 ERR_READ_THREAD_GUARD_V(0);
2121 if (!initialized) {
2122 WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED");
2123 }
2124
2125 if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) {
2126 const int *constant = theme_constant_override.getptr(p_name);
2127 if (constant) {
2128 return *constant;
2129 }
2130 }
2131
2132 if (theme_constant_cache.has(p_theme_type) && theme_constant_cache[p_theme_type].has(p_name)) {
2133 return theme_constant_cache[p_theme_type][p_name];
2134 }
2135
2136 List<StringName> theme_types;
2137 theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
2138 int constant = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
2139 theme_constant_cache[p_theme_type][p_name] = constant;
2140 return constant;
2141}
2142
2143Variant Window::get_theme_item(Theme::DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const {
2144 switch (p_data_type) {
2145 case Theme::DATA_TYPE_COLOR:
2146 return get_theme_color(p_name, p_theme_type);
2147 case Theme::DATA_TYPE_CONSTANT:
2148 return get_theme_constant(p_name, p_theme_type);
2149 case Theme::DATA_TYPE_FONT:
2150 return get_theme_font(p_name, p_theme_type);
2151 case Theme::DATA_TYPE_FONT_SIZE:
2152 return get_theme_font_size(p_name, p_theme_type);
2153 case Theme::DATA_TYPE_ICON:
2154 return get_theme_icon(p_name, p_theme_type);
2155 case Theme::DATA_TYPE_STYLEBOX:
2156 return get_theme_stylebox(p_name, p_theme_type);
2157 case Theme::DATA_TYPE_MAX:
2158 break; // Can't happen, but silences warning.
2159 }
2160
2161 return Variant();
2162}
2163
2164#ifdef TOOLS_ENABLED
2165Ref<Texture2D> Window::get_editor_theme_icon(const StringName &p_name) const {
2166 return get_theme_icon(p_name, SNAME("EditorIcons"));
2167}
2168#endif
2169
2170bool Window::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const {
2171 ERR_READ_THREAD_GUARD_V(false);
2172 if (!initialized) {
2173 WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED");
2174 }
2175
2176 if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) {
2177 if (has_theme_icon_override(p_name)) {
2178 return true;
2179 }
2180 }
2181
2182 List<StringName> theme_types;
2183 theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
2184 return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_ICON, p_name, theme_types);
2185}
2186
2187bool Window::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
2188 ERR_READ_THREAD_GUARD_V(false);
2189 if (!initialized) {
2190 WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED");
2191 }
2192
2193 if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) {
2194 if (has_theme_stylebox_override(p_name)) {
2195 return true;
2196 }
2197 }
2198
2199 List<StringName> theme_types;
2200 theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
2201 return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_STYLEBOX, p_name, theme_types);
2202}
2203
2204bool Window::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const {
2205 ERR_READ_THREAD_GUARD_V(false);
2206 if (!initialized) {
2207 WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED");
2208 }
2209
2210 if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) {
2211 if (has_theme_font_override(p_name)) {
2212 return true;
2213 }
2214 }
2215
2216 List<StringName> theme_types;
2217 theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
2218 return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_FONT, p_name, theme_types);
2219}
2220
2221bool Window::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const {
2222 ERR_READ_THREAD_GUARD_V(false);
2223 if (!initialized) {
2224 WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED");
2225 }
2226
2227 if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) {
2228 if (has_theme_font_size_override(p_name)) {
2229 return true;
2230 }
2231 }
2232
2233 List<StringName> theme_types;
2234 theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
2235 return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types);
2236}
2237
2238bool Window::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const {
2239 ERR_READ_THREAD_GUARD_V(false);
2240 if (!initialized) {
2241 WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED");
2242 }
2243
2244 if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) {
2245 if (has_theme_color_override(p_name)) {
2246 return true;
2247 }
2248 }
2249
2250 List<StringName> theme_types;
2251 theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
2252 return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_COLOR, p_name, theme_types);
2253}
2254
2255bool Window::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const {
2256 ERR_READ_THREAD_GUARD_V(false);
2257 if (!initialized) {
2258 WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED");
2259 }
2260
2261 if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) {
2262 if (has_theme_constant_override(p_name)) {
2263 return true;
2264 }
2265 }
2266
2267 List<StringName> theme_types;
2268 theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
2269 return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
2270}
2271
2272/// Local property overrides.
2273
2274void Window::add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon) {
2275 ERR_MAIN_THREAD_GUARD;
2276 ERR_FAIL_COND(!p_icon.is_valid());
2277
2278 if (theme_icon_override.has(p_name)) {
2279 theme_icon_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
2280 }
2281
2282 theme_icon_override[p_name] = p_icon;
2283 theme_icon_override[p_name]->connect_changed(callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
2284 _notify_theme_override_changed();
2285}
2286
2287void Window::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) {
2288 ERR_MAIN_THREAD_GUARD;
2289 ERR_FAIL_COND(!p_style.is_valid());
2290
2291 if (theme_style_override.has(p_name)) {
2292 theme_style_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
2293 }
2294
2295 theme_style_override[p_name] = p_style;
2296 theme_style_override[p_name]->connect_changed(callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
2297 _notify_theme_override_changed();
2298}
2299
2300void Window::add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font) {
2301 ERR_MAIN_THREAD_GUARD;
2302 ERR_FAIL_COND(!p_font.is_valid());
2303
2304 if (theme_font_override.has(p_name)) {
2305 theme_font_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
2306 }
2307
2308 theme_font_override[p_name] = p_font;
2309 theme_font_override[p_name]->connect_changed(callable_mp(this, &Window::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
2310 _notify_theme_override_changed();
2311}
2312
2313void Window::add_theme_font_size_override(const StringName &p_name, int p_font_size) {
2314 ERR_MAIN_THREAD_GUARD;
2315 theme_font_size_override[p_name] = p_font_size;
2316 _notify_theme_override_changed();
2317}
2318
2319void Window::add_theme_color_override(const StringName &p_name, const Color &p_color) {
2320 ERR_MAIN_THREAD_GUARD;
2321 theme_color_override[p_name] = p_color;
2322 _notify_theme_override_changed();
2323}
2324
2325void Window::add_theme_constant_override(const StringName &p_name, int p_constant) {
2326 ERR_MAIN_THREAD_GUARD;
2327 theme_constant_override[p_name] = p_constant;
2328 _notify_theme_override_changed();
2329}
2330
2331void Window::remove_theme_icon_override(const StringName &p_name) {
2332 ERR_MAIN_THREAD_GUARD;
2333 if (theme_icon_override.has(p_name)) {
2334 theme_icon_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
2335 }
2336
2337 theme_icon_override.erase(p_name);
2338 _notify_theme_override_changed();
2339}
2340
2341void Window::remove_theme_style_override(const StringName &p_name) {
2342 ERR_MAIN_THREAD_GUARD;
2343 if (theme_style_override.has(p_name)) {
2344 theme_style_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
2345 }
2346
2347 theme_style_override.erase(p_name);
2348 _notify_theme_override_changed();
2349}
2350
2351void Window::remove_theme_font_override(const StringName &p_name) {
2352 ERR_MAIN_THREAD_GUARD;
2353 if (theme_font_override.has(p_name)) {
2354 theme_font_override[p_name]->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
2355 }
2356
2357 theme_font_override.erase(p_name);
2358 _notify_theme_override_changed();
2359}
2360
2361void Window::remove_theme_font_size_override(const StringName &p_name) {
2362 ERR_MAIN_THREAD_GUARD;
2363 theme_font_size_override.erase(p_name);
2364 _notify_theme_override_changed();
2365}
2366
2367void Window::remove_theme_color_override(const StringName &p_name) {
2368 ERR_MAIN_THREAD_GUARD;
2369 theme_color_override.erase(p_name);
2370 _notify_theme_override_changed();
2371}
2372
2373void Window::remove_theme_constant_override(const StringName &p_name) {
2374 ERR_MAIN_THREAD_GUARD;
2375 theme_constant_override.erase(p_name);
2376 _notify_theme_override_changed();
2377}
2378
2379bool Window::has_theme_icon_override(const StringName &p_name) const {
2380 ERR_READ_THREAD_GUARD_V(false);
2381 const Ref<Texture2D> *tex = theme_icon_override.getptr(p_name);
2382 return tex != nullptr;
2383}
2384
2385bool Window::has_theme_stylebox_override(const StringName &p_name) const {
2386 ERR_READ_THREAD_GUARD_V(false);
2387 const Ref<StyleBox> *style = theme_style_override.getptr(p_name);
2388 return style != nullptr;
2389}
2390
2391bool Window::has_theme_font_override(const StringName &p_name) const {
2392 ERR_READ_THREAD_GUARD_V(false);
2393 const Ref<Font> *font = theme_font_override.getptr(p_name);
2394 return font != nullptr;
2395}
2396
2397bool Window::has_theme_font_size_override(const StringName &p_name) const {
2398 ERR_READ_THREAD_GUARD_V(false);
2399 const int *font_size = theme_font_size_override.getptr(p_name);
2400 return font_size != nullptr;
2401}
2402
2403bool Window::has_theme_color_override(const StringName &p_name) const {
2404 ERR_READ_THREAD_GUARD_V(false);
2405 const Color *color = theme_color_override.getptr(p_name);
2406 return color != nullptr;
2407}
2408
2409bool Window::has_theme_constant_override(const StringName &p_name) const {
2410 ERR_READ_THREAD_GUARD_V(false);
2411 const int *constant = theme_constant_override.getptr(p_name);
2412 return constant != nullptr;
2413}
2414
2415/// Default theme properties.
2416
2417float Window::get_theme_default_base_scale() const {
2418 ERR_READ_THREAD_GUARD_V(0);
2419 return theme_owner->get_theme_default_base_scale();
2420}
2421
2422Ref<Font> Window::get_theme_default_font() const {
2423 ERR_READ_THREAD_GUARD_V(Ref<Font>());
2424 return theme_owner->get_theme_default_font();
2425}
2426
2427int Window::get_theme_default_font_size() const {
2428 ERR_READ_THREAD_GUARD_V(0);
2429 return theme_owner->get_theme_default_font_size();
2430}
2431
2432/// Bulk actions.
2433
2434void Window::begin_bulk_theme_override() {
2435 ERR_MAIN_THREAD_GUARD;
2436 bulk_theme_override = true;
2437}
2438
2439void Window::end_bulk_theme_override() {
2440 ERR_MAIN_THREAD_GUARD;
2441 ERR_FAIL_COND(!bulk_theme_override);
2442
2443 bulk_theme_override = false;
2444 _notify_theme_override_changed();
2445}
2446
2447//
2448
2449Rect2i Window::get_parent_rect() const {
2450 ERR_READ_THREAD_GUARD_V(Rect2i());
2451 ERR_FAIL_COND_V(!is_inside_tree(), Rect2i());
2452 if (is_embedded()) {
2453 //viewport
2454 Node *n = get_parent();
2455 ERR_FAIL_NULL_V(n, Rect2i());
2456 Viewport *p = n->get_viewport();
2457 ERR_FAIL_NULL_V(p, Rect2i());
2458
2459 return p->get_visible_rect();
2460 } else {
2461 int x = get_position().x;
2462 int closest_dist = 0x7FFFFFFF;
2463 Rect2i closest_rect;
2464 for (int i = 0; i < DisplayServer::get_singleton()->get_screen_count(); i++) {
2465 Rect2i s(DisplayServer::get_singleton()->screen_get_position(i), DisplayServer::get_singleton()->screen_get_size(i));
2466 int d;
2467 if (x >= s.position.x && x < s.size.x) {
2468 //contained
2469 closest_rect = s;
2470 break;
2471 } else if (x < s.position.x) {
2472 d = s.position.x - x;
2473 } else {
2474 d = x - (s.position.x + s.size.x);
2475 }
2476
2477 if (d < closest_dist) {
2478 closest_dist = d;
2479 closest_rect = s;
2480 }
2481 }
2482 return closest_rect;
2483 }
2484}
2485
2486void Window::set_clamp_to_embedder(bool p_enable) {
2487 ERR_MAIN_THREAD_GUARD;
2488 clamp_to_embedder = p_enable;
2489}
2490
2491bool Window::is_clamped_to_embedder() const {
2492 ERR_READ_THREAD_GUARD_V(false);
2493 return clamp_to_embedder;
2494}
2495
2496void Window::set_unparent_when_invisible(bool p_unparent) {
2497 unparent_when_invisible = p_unparent;
2498}
2499
2500void Window::set_layout_direction(Window::LayoutDirection p_direction) {
2501 ERR_MAIN_THREAD_GUARD;
2502 ERR_FAIL_INDEX((int)p_direction, 4);
2503
2504 layout_dir = p_direction;
2505 propagate_notification(Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED);
2506}
2507
2508Window::LayoutDirection Window::get_layout_direction() const {
2509 ERR_READ_THREAD_GUARD_V(LAYOUT_DIRECTION_INHERITED);
2510 return layout_dir;
2511}
2512
2513bool Window::is_layout_rtl() const {
2514 ERR_READ_THREAD_GUARD_V(false);
2515 if (layout_dir == LAYOUT_DIRECTION_INHERITED) {
2516 if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
2517 return true;
2518 }
2519 Node *parent_node = get_parent();
2520 while (parent_node) {
2521 Control *parent_control = Object::cast_to<Control>(parent_node);
2522 if (parent_control) {
2523 return parent_control->is_layout_rtl();
2524 }
2525
2526 Window *parent_window = Object::cast_to<Window>(parent_node);
2527 if (parent_window) {
2528 return parent_window->is_layout_rtl();
2529 }
2530 parent_node = parent_node->get_parent();
2531 }
2532
2533 int root_dir = GLOBAL_GET(SNAME("internationalization/rendering/root_node_layout_direction"));
2534 if (root_dir == 1) {
2535 return false;
2536 } else if (root_dir == 2) {
2537 return true;
2538 } else {
2539 String locale = TranslationServer::get_singleton()->get_tool_locale();
2540 return TS->is_locale_right_to_left(locale);
2541 }
2542 } else if (layout_dir == LAYOUT_DIRECTION_LOCALE) {
2543 if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
2544 return true;
2545 } else {
2546 String locale = TranslationServer::get_singleton()->get_tool_locale();
2547 return TS->is_locale_right_to_left(locale);
2548 }
2549 } else {
2550 return (layout_dir == LAYOUT_DIRECTION_RTL);
2551 }
2552}
2553
2554void Window::set_auto_translate(bool p_enable) {
2555 ERR_MAIN_THREAD_GUARD;
2556 if (p_enable == auto_translate) {
2557 return;
2558 }
2559
2560 auto_translate = p_enable;
2561
2562 notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
2563}
2564
2565bool Window::is_auto_translating() const {
2566 ERR_READ_THREAD_GUARD_V(false);
2567 return auto_translate;
2568}
2569
2570Transform2D Window::get_final_transform() const {
2571 ERR_READ_THREAD_GUARD_V(Transform2D());
2572 return window_transform * stretch_transform * global_canvas_transform;
2573}
2574
2575Transform2D Window::get_screen_transform_internal(bool p_absolute_position) const {
2576 ERR_READ_THREAD_GUARD_V(Transform2D());
2577 Transform2D embedder_transform;
2578 if (get_embedder()) {
2579 embedder_transform.translate_local(get_position());
2580 embedder_transform = get_embedder()->get_screen_transform_internal(p_absolute_position) * embedder_transform;
2581 } else if (p_absolute_position) {
2582 embedder_transform.translate_local(get_position());
2583 }
2584 return embedder_transform * get_final_transform();
2585}
2586
2587Transform2D Window::get_popup_base_transform() const {
2588 ERR_READ_THREAD_GUARD_V(Transform2D());
2589 if (is_embedding_subwindows()) {
2590 return Transform2D();
2591 }
2592 Transform2D popup_base_transform;
2593 popup_base_transform.set_origin(get_position());
2594 popup_base_transform *= get_final_transform();
2595 if (get_embedder()) {
2596 return get_embedder()->get_popup_base_transform() * popup_base_transform;
2597 }
2598 return popup_base_transform;
2599}
2600
2601bool Window::is_directly_attached_to_screen() const {
2602 if (get_embedder()) {
2603 return get_embedder()->is_directly_attached_to_screen();
2604 }
2605 // Distinguish between the case that this is a native Window and not inside the tree.
2606 return is_inside_tree();
2607}
2608
2609bool Window::is_attached_in_viewport() const {
2610 return get_embedder();
2611}
2612
2613void Window::_update_mouse_over(Vector2 p_pos) {
2614 if (!mouse_in_window) {
2615 if (is_embedded()) {
2616 mouse_in_window = true;
2617 _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER);
2618 } else {
2619 // Prevent update based on delayed InputEvents from DisplayServer.
2620 return;
2621 }
2622 }
2623
2624 bool new_in = get_visible_rect().has_point(p_pos);
2625 if (new_in == gui.mouse_in_viewport) {
2626 if (new_in) {
2627 Viewport::_update_mouse_over(p_pos);
2628 }
2629 return;
2630 }
2631
2632 if (new_in) {
2633 notification(NOTIFICATION_VP_MOUSE_ENTER);
2634 Viewport::_update_mouse_over(p_pos);
2635 } else {
2636 Viewport::_mouse_leave_viewport();
2637 }
2638}
2639
2640void Window::_mouse_leave_viewport() {
2641 Viewport::_mouse_leave_viewport();
2642 if (is_embedded()) {
2643 mouse_in_window = false;
2644 _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT);
2645 }
2646}
2647
2648void Window::_bind_methods() {
2649 ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title);
2650 ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title);
2651
2652 ClassDB::bind_method(D_METHOD("get_window_id"), &Window::get_window_id);
2653
2654 ClassDB::bind_method(D_METHOD("set_initial_position", "initial_position"), &Window::set_initial_position);
2655 ClassDB::bind_method(D_METHOD("get_initial_position"), &Window::get_initial_position);
2656
2657 ClassDB::bind_method(D_METHOD("set_current_screen", "index"), &Window::set_current_screen);
2658 ClassDB::bind_method(D_METHOD("get_current_screen"), &Window::get_current_screen);
2659
2660 ClassDB::bind_method(D_METHOD("set_position", "position"), &Window::set_position);
2661 ClassDB::bind_method(D_METHOD("get_position"), &Window::get_position);
2662 ClassDB::bind_method(D_METHOD("move_to_center"), &Window::move_to_center);
2663
2664 ClassDB::bind_method(D_METHOD("set_size", "size"), &Window::set_size);
2665 ClassDB::bind_method(D_METHOD("get_size"), &Window::get_size);
2666 ClassDB::bind_method(D_METHOD("reset_size"), &Window::reset_size);
2667
2668 ClassDB::bind_method(D_METHOD("get_position_with_decorations"), &Window::get_position_with_decorations);
2669 ClassDB::bind_method(D_METHOD("get_size_with_decorations"), &Window::get_size_with_decorations);
2670
2671 ClassDB::bind_method(D_METHOD("set_max_size", "max_size"), &Window::set_max_size);
2672 ClassDB::bind_method(D_METHOD("get_max_size"), &Window::get_max_size);
2673
2674 ClassDB::bind_method(D_METHOD("set_min_size", "min_size"), &Window::set_min_size);
2675 ClassDB::bind_method(D_METHOD("get_min_size"), &Window::get_min_size);
2676
2677 ClassDB::bind_method(D_METHOD("set_mode", "mode"), &Window::set_mode);
2678 ClassDB::bind_method(D_METHOD("get_mode"), &Window::get_mode);
2679
2680 ClassDB::bind_method(D_METHOD("set_flag", "flag", "enabled"), &Window::set_flag);
2681 ClassDB::bind_method(D_METHOD("get_flag", "flag"), &Window::get_flag);
2682
2683 ClassDB::bind_method(D_METHOD("is_maximize_allowed"), &Window::is_maximize_allowed);
2684
2685 ClassDB::bind_method(D_METHOD("request_attention"), &Window::request_attention);
2686
2687 ClassDB::bind_method(D_METHOD("move_to_foreground"), &Window::move_to_foreground);
2688
2689 ClassDB::bind_method(D_METHOD("set_visible", "visible"), &Window::set_visible);
2690 ClassDB::bind_method(D_METHOD("is_visible"), &Window::is_visible);
2691
2692 ClassDB::bind_method(D_METHOD("hide"), &Window::hide);
2693 ClassDB::bind_method(D_METHOD("show"), &Window::show);
2694
2695 ClassDB::bind_method(D_METHOD("set_transient", "transient"), &Window::set_transient);
2696 ClassDB::bind_method(D_METHOD("is_transient"), &Window::is_transient);
2697
2698 ClassDB::bind_method(D_METHOD("set_exclusive", "exclusive"), &Window::set_exclusive);
2699 ClassDB::bind_method(D_METHOD("is_exclusive"), &Window::is_exclusive);
2700
2701 ClassDB::bind_method(D_METHOD("set_unparent_when_invisible", "unparent"), &Window::set_unparent_when_invisible);
2702
2703 ClassDB::bind_method(D_METHOD("can_draw"), &Window::can_draw);
2704 ClassDB::bind_method(D_METHOD("has_focus"), &Window::has_focus);
2705 ClassDB::bind_method(D_METHOD("grab_focus"), &Window::grab_focus);
2706
2707 ClassDB::bind_method(D_METHOD("set_ime_active", "active"), &Window::set_ime_active);
2708 ClassDB::bind_method(D_METHOD("set_ime_position", "position"), &Window::set_ime_position);
2709
2710 ClassDB::bind_method(D_METHOD("is_embedded"), &Window::is_embedded);
2711
2712 ClassDB::bind_method(D_METHOD("get_contents_minimum_size"), &Window::get_contents_minimum_size);
2713
2714 ClassDB::bind_method(D_METHOD("set_content_scale_size", "size"), &Window::set_content_scale_size);
2715 ClassDB::bind_method(D_METHOD("get_content_scale_size"), &Window::get_content_scale_size);
2716
2717 ClassDB::bind_method(D_METHOD("set_content_scale_mode", "mode"), &Window::set_content_scale_mode);
2718 ClassDB::bind_method(D_METHOD("get_content_scale_mode"), &Window::get_content_scale_mode);
2719
2720 ClassDB::bind_method(D_METHOD("set_content_scale_aspect", "aspect"), &Window::set_content_scale_aspect);
2721 ClassDB::bind_method(D_METHOD("get_content_scale_aspect"), &Window::get_content_scale_aspect);
2722
2723 ClassDB::bind_method(D_METHOD("set_content_scale_stretch", "stretch"), &Window::set_content_scale_stretch);
2724 ClassDB::bind_method(D_METHOD("get_content_scale_stretch"), &Window::get_content_scale_stretch);
2725
2726 ClassDB::bind_method(D_METHOD("set_content_scale_factor", "factor"), &Window::set_content_scale_factor);
2727 ClassDB::bind_method(D_METHOD("get_content_scale_factor"), &Window::get_content_scale_factor);
2728
2729 ClassDB::bind_method(D_METHOD("set_use_font_oversampling", "enable"), &Window::set_use_font_oversampling);
2730 ClassDB::bind_method(D_METHOD("is_using_font_oversampling"), &Window::is_using_font_oversampling);
2731
2732 ClassDB::bind_method(D_METHOD("set_mouse_passthrough_polygon", "polygon"), &Window::set_mouse_passthrough_polygon);
2733 ClassDB::bind_method(D_METHOD("get_mouse_passthrough_polygon"), &Window::get_mouse_passthrough_polygon);
2734
2735 ClassDB::bind_method(D_METHOD("set_wrap_controls", "enable"), &Window::set_wrap_controls);
2736 ClassDB::bind_method(D_METHOD("is_wrapping_controls"), &Window::is_wrapping_controls);
2737 ClassDB::bind_method(D_METHOD("child_controls_changed"), &Window::child_controls_changed);
2738
2739 ClassDB::bind_method(D_METHOD("_update_child_controls"), &Window::_update_child_controls);
2740 ClassDB::bind_method(D_METHOD("_update_embedded_window"), &Window::_update_embedded_window);
2741
2742 ClassDB::bind_method(D_METHOD("set_theme", "theme"), &Window::set_theme);
2743 ClassDB::bind_method(D_METHOD("get_theme"), &Window::get_theme);
2744
2745 ClassDB::bind_method(D_METHOD("set_theme_type_variation", "theme_type"), &Window::set_theme_type_variation);
2746 ClassDB::bind_method(D_METHOD("get_theme_type_variation"), &Window::get_theme_type_variation);
2747
2748 ClassDB::bind_method(D_METHOD("begin_bulk_theme_override"), &Window::begin_bulk_theme_override);
2749 ClassDB::bind_method(D_METHOD("end_bulk_theme_override"), &Window::end_bulk_theme_override);
2750
2751 ClassDB::bind_method(D_METHOD("add_theme_icon_override", "name", "texture"), &Window::add_theme_icon_override);
2752 ClassDB::bind_method(D_METHOD("add_theme_stylebox_override", "name", "stylebox"), &Window::add_theme_style_override);
2753 ClassDB::bind_method(D_METHOD("add_theme_font_override", "name", "font"), &Window::add_theme_font_override);
2754 ClassDB::bind_method(D_METHOD("add_theme_font_size_override", "name", "font_size"), &Window::add_theme_font_size_override);
2755 ClassDB::bind_method(D_METHOD("add_theme_color_override", "name", "color"), &Window::add_theme_color_override);
2756 ClassDB::bind_method(D_METHOD("add_theme_constant_override", "name", "constant"), &Window::add_theme_constant_override);
2757
2758 ClassDB::bind_method(D_METHOD("remove_theme_icon_override", "name"), &Window::remove_theme_icon_override);
2759 ClassDB::bind_method(D_METHOD("remove_theme_stylebox_override", "name"), &Window::remove_theme_style_override);
2760 ClassDB::bind_method(D_METHOD("remove_theme_font_override", "name"), &Window::remove_theme_font_override);
2761 ClassDB::bind_method(D_METHOD("remove_theme_font_size_override", "name"), &Window::remove_theme_font_size_override);
2762 ClassDB::bind_method(D_METHOD("remove_theme_color_override", "name"), &Window::remove_theme_color_override);
2763 ClassDB::bind_method(D_METHOD("remove_theme_constant_override", "name"), &Window::remove_theme_constant_override);
2764
2765 ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "theme_type"), &Window::get_theme_icon, DEFVAL(""));
2766 ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "theme_type"), &Window::get_theme_stylebox, DEFVAL(""));
2767 ClassDB::bind_method(D_METHOD("get_theme_font", "name", "theme_type"), &Window::get_theme_font, DEFVAL(""));
2768 ClassDB::bind_method(D_METHOD("get_theme_font_size", "name", "theme_type"), &Window::get_theme_font_size, DEFVAL(""));
2769 ClassDB::bind_method(D_METHOD("get_theme_color", "name", "theme_type"), &Window::get_theme_color, DEFVAL(""));
2770 ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "theme_type"), &Window::get_theme_constant, DEFVAL(""));
2771
2772 ClassDB::bind_method(D_METHOD("has_theme_icon_override", "name"), &Window::has_theme_icon_override);
2773 ClassDB::bind_method(D_METHOD("has_theme_stylebox_override", "name"), &Window::has_theme_stylebox_override);
2774 ClassDB::bind_method(D_METHOD("has_theme_font_override", "name"), &Window::has_theme_font_override);
2775 ClassDB::bind_method(D_METHOD("has_theme_font_size_override", "name"), &Window::has_theme_font_size_override);
2776 ClassDB::bind_method(D_METHOD("has_theme_color_override", "name"), &Window::has_theme_color_override);
2777 ClassDB::bind_method(D_METHOD("has_theme_constant_override", "name"), &Window::has_theme_constant_override);
2778
2779 ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "theme_type"), &Window::has_theme_icon, DEFVAL(""));
2780 ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "theme_type"), &Window::has_theme_stylebox, DEFVAL(""));
2781 ClassDB::bind_method(D_METHOD("has_theme_font", "name", "theme_type"), &Window::has_theme_font, DEFVAL(""));
2782 ClassDB::bind_method(D_METHOD("has_theme_font_size", "name", "theme_type"), &Window::has_theme_font_size, DEFVAL(""));
2783 ClassDB::bind_method(D_METHOD("has_theme_color", "name", "theme_type"), &Window::has_theme_color, DEFVAL(""));
2784 ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Window::has_theme_constant, DEFVAL(""));
2785
2786 ClassDB::bind_method(D_METHOD("get_theme_default_base_scale"), &Window::get_theme_default_base_scale);
2787 ClassDB::bind_method(D_METHOD("get_theme_default_font"), &Window::get_theme_default_font);
2788 ClassDB::bind_method(D_METHOD("get_theme_default_font_size"), &Window::get_theme_default_font_size);
2789
2790 ClassDB::bind_method(D_METHOD("set_layout_direction", "direction"), &Window::set_layout_direction);
2791 ClassDB::bind_method(D_METHOD("get_layout_direction"), &Window::get_layout_direction);
2792 ClassDB::bind_method(D_METHOD("is_layout_rtl"), &Window::is_layout_rtl);
2793
2794 ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Window::set_auto_translate);
2795 ClassDB::bind_method(D_METHOD("is_auto_translating"), &Window::is_auto_translating);
2796
2797 ClassDB::bind_method(D_METHOD("popup", "rect"), &Window::popup, DEFVAL(Rect2i()));
2798 ClassDB::bind_method(D_METHOD("popup_on_parent", "parent_rect"), &Window::popup_on_parent);
2799 ClassDB::bind_method(D_METHOD("popup_centered", "minsize"), &Window::popup_centered, DEFVAL(Size2i()));
2800 ClassDB::bind_method(D_METHOD("popup_centered_ratio", "ratio"), &Window::popup_centered_ratio, DEFVAL(0.8));
2801 ClassDB::bind_method(D_METHOD("popup_centered_clamped", "minsize", "fallback_ratio"), &Window::popup_centered_clamped, DEFVAL(Size2i()), DEFVAL(0.75));
2802
2803 ClassDB::bind_method(D_METHOD("popup_exclusive", "from_node", "rect"), &Window::popup_exclusive, DEFVAL(Rect2i()));
2804 ClassDB::bind_method(D_METHOD("popup_exclusive_on_parent", "from_node", "parent_rect"), &Window::popup_exclusive_on_parent);
2805 ClassDB::bind_method(D_METHOD("popup_exclusive_centered", "from_node", "minsize"), &Window::popup_exclusive_centered, DEFVAL(Size2i()));
2806 ClassDB::bind_method(D_METHOD("popup_exclusive_centered_ratio", "from_node", "ratio"), &Window::popup_exclusive_centered_ratio, DEFVAL(0.8));
2807 ClassDB::bind_method(D_METHOD("popup_exclusive_centered_clamped", "from_node", "minsize", "fallback_ratio"), &Window::popup_exclusive_centered_clamped, DEFVAL(Size2i()), DEFVAL(0.75));
2808
2809 // Keep the enum values in sync with the `Mode` enum.
2810 ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Windowed,Minimized,Maximized,Fullscreen,Exclusive Fullscreen"), "set_mode", "get_mode");
2811
2812 ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
2813
2814 // Keep the enum values in sync with the `WindowInitialPosition` enum.
2815 ADD_PROPERTY(PropertyInfo(Variant::INT, "initial_position", PROPERTY_HINT_ENUM, "Absolute,Center of Primary Screen,Center of Main Window Screen,Center of Other Screen,Center of Screen With Mouse Pointer,Center of Screen With Keyboard Focus"), "set_initial_position", "get_initial_position");
2816 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "position", PROPERTY_HINT_NONE, "suffix:px"), "set_position", "get_position");
2817 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size");
2818 ADD_PROPERTY(PropertyInfo(Variant::INT, "current_screen", PROPERTY_HINT_RANGE, "0,64,1,or_greater"), "set_current_screen", "get_current_screen");
2819
2820 ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "mouse_passthrough_polygon"), "set_mouse_passthrough_polygon", "get_mouse_passthrough_polygon");
2821
2822 ADD_GROUP("Flags", "");
2823 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
2824 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_controls"), "set_wrap_controls", "is_wrapping_controls");
2825 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transient"), "set_transient", "is_transient");
2826 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclusive"), "set_exclusive", "is_exclusive");
2827 ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "unresizable"), "set_flag", "get_flag", FLAG_RESIZE_DISABLED);
2828 ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "borderless"), "set_flag", "get_flag", FLAG_BORDERLESS);
2829 ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "always_on_top"), "set_flag", "get_flag", FLAG_ALWAYS_ON_TOP);
2830 ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent"), "set_flag", "get_flag", FLAG_TRANSPARENT);
2831 ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "unfocusable"), "set_flag", "get_flag", FLAG_NO_FOCUS);
2832 ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "popup_window"), "set_flag", "get_flag", FLAG_POPUP);
2833 ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "extend_to_title"), "set_flag", "get_flag", FLAG_EXTEND_TO_TITLE);
2834 ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "mouse_passthrough"), "set_flag", "get_flag", FLAG_MOUSE_PASSTHROUGH);
2835
2836 ADD_GROUP("Limits", "");
2837 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "min_size", PROPERTY_HINT_NONE, "suffix:px"), "set_min_size", "get_min_size");
2838 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "max_size", PROPERTY_HINT_NONE, "suffix:px"), "set_max_size", "get_max_size");
2839
2840 ADD_GROUP("Content Scale", "content_scale_");
2841 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "content_scale_size"), "set_content_scale_size", "get_content_scale_size");
2842 ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_mode", PROPERTY_HINT_ENUM, "Disabled,Canvas Items,Viewport"), "set_content_scale_mode", "get_content_scale_mode");
2843 ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_aspect", PROPERTY_HINT_ENUM, "Ignore,Keep,Keep Width,Keep Height,Expand"), "set_content_scale_aspect", "get_content_scale_aspect");
2844 ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_stretch", PROPERTY_HINT_ENUM, "Fractional,Integer"), "set_content_scale_stretch", "get_content_scale_stretch");
2845 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "content_scale_factor", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), "set_content_scale_factor", "get_content_scale_factor");
2846
2847 ADD_GROUP("Localization", "");
2848 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating");
2849
2850 ADD_GROUP("Theme", "theme_");
2851 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme");
2852 ADD_PROPERTY(PropertyInfo(Variant::STRING, "theme_type_variation", PROPERTY_HINT_ENUM_SUGGESTION), "set_theme_type_variation", "get_theme_type_variation");
2853
2854 ADD_SIGNAL(MethodInfo("window_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")));
2855 ADD_SIGNAL(MethodInfo("files_dropped", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files")));
2856 ADD_SIGNAL(MethodInfo("mouse_entered"));
2857 ADD_SIGNAL(MethodInfo("mouse_exited"));
2858 ADD_SIGNAL(MethodInfo("focus_entered"));
2859 ADD_SIGNAL(MethodInfo("focus_exited"));
2860 ADD_SIGNAL(MethodInfo("close_requested"));
2861 ADD_SIGNAL(MethodInfo("go_back_requested"));
2862 ADD_SIGNAL(MethodInfo("visibility_changed"));
2863 ADD_SIGNAL(MethodInfo("about_to_popup"));
2864 ADD_SIGNAL(MethodInfo("theme_changed"));
2865 ADD_SIGNAL(MethodInfo("dpi_changed"));
2866 ADD_SIGNAL(MethodInfo("titlebar_changed"));
2867
2868 BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED);
2869 BIND_CONSTANT(NOTIFICATION_THEME_CHANGED);
2870
2871 BIND_ENUM_CONSTANT(MODE_WINDOWED);
2872 BIND_ENUM_CONSTANT(MODE_MINIMIZED);
2873 BIND_ENUM_CONSTANT(MODE_MAXIMIZED);
2874 BIND_ENUM_CONSTANT(MODE_FULLSCREEN);
2875 BIND_ENUM_CONSTANT(MODE_EXCLUSIVE_FULLSCREEN);
2876
2877 BIND_ENUM_CONSTANT(FLAG_RESIZE_DISABLED);
2878 BIND_ENUM_CONSTANT(FLAG_BORDERLESS);
2879 BIND_ENUM_CONSTANT(FLAG_ALWAYS_ON_TOP);
2880 BIND_ENUM_CONSTANT(FLAG_TRANSPARENT);
2881 BIND_ENUM_CONSTANT(FLAG_NO_FOCUS);
2882 BIND_ENUM_CONSTANT(FLAG_POPUP);
2883 BIND_ENUM_CONSTANT(FLAG_EXTEND_TO_TITLE);
2884 BIND_ENUM_CONSTANT(FLAG_MOUSE_PASSTHROUGH);
2885 BIND_ENUM_CONSTANT(FLAG_MAX);
2886
2887 BIND_ENUM_CONSTANT(CONTENT_SCALE_MODE_DISABLED);
2888 BIND_ENUM_CONSTANT(CONTENT_SCALE_MODE_CANVAS_ITEMS);
2889 BIND_ENUM_CONSTANT(CONTENT_SCALE_MODE_VIEWPORT);
2890
2891 BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_IGNORE);
2892 BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_KEEP);
2893 BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_KEEP_WIDTH);
2894 BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_KEEP_HEIGHT);
2895 BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_EXPAND);
2896
2897 BIND_ENUM_CONSTANT(CONTENT_SCALE_STRETCH_FRACTIONAL);
2898 BIND_ENUM_CONSTANT(CONTENT_SCALE_STRETCH_INTEGER);
2899
2900 BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_INHERITED);
2901 BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE);
2902 BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LTR);
2903 BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_RTL);
2904
2905 BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_ABSOLUTE);
2906 BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_PRIMARY_SCREEN);
2907 BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_MAIN_WINDOW_SCREEN);
2908 BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_OTHER_SCREEN);
2909 BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_MOUSE_FOCUS);
2910 BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_KEYBOARD_FOCUS);
2911
2912 GDVIRTUAL_BIND(_get_contents_minimum_size);
2913
2914 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Window, embedded_border);
2915 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Window, embedded_unfocused_border);
2916
2917 BIND_THEME_ITEM(Theme::DATA_TYPE_FONT, Window, title_font);
2918 BIND_THEME_ITEM(Theme::DATA_TYPE_FONT_SIZE, Window, title_font_size);
2919 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Window, title_color);
2920 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Window, title_height);
2921 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Window, title_outline_modulate);
2922 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Window, title_outline_size);
2923
2924 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Window, close);
2925 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Window, close_pressed);
2926 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Window, close_h_offset);
2927 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Window, close_v_offset);
2928
2929 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Window, resize_margin);
2930}
2931
2932Window::Window() {
2933 RenderingServer *rendering_server = RenderingServer::get_singleton();
2934 if (rendering_server) {
2935 max_size = rendering_server->get_maximum_viewport_size();
2936 max_size_used = max_size; // Update max_size_used.
2937 }
2938
2939 theme_owner = memnew(ThemeOwner(this));
2940 RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED);
2941}
2942
2943Window::~Window() {
2944 memdelete(theme_owner);
2945
2946 // Resources need to be disconnected.
2947 for (KeyValue<StringName, Ref<Texture2D>> &E : theme_icon_override) {
2948 E.value->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
2949 }
2950 for (KeyValue<StringName, Ref<StyleBox>> &E : theme_style_override) {
2951 E.value->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
2952 }
2953 for (KeyValue<StringName, Ref<Font>> &E : theme_font_override) {
2954 E.value->disconnect_changed(callable_mp(this, &Window::_notify_theme_override_changed));
2955 }
2956
2957 // Then override maps can be simply cleared.
2958 theme_icon_override.clear();
2959 theme_style_override.clear();
2960 theme_font_override.clear();
2961 theme_font_size_override.clear();
2962 theme_color_override.clear();
2963 theme_constant_override.clear();
2964}
2965