Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/gui/window_wrapper.cpp
21135 views
1
/**************************************************************************/
2
/* window_wrapper.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_wrapper.h"
32
33
#include "editor/editor_node.h"
34
#include "editor/editor_string_names.h"
35
#include "editor/gui/progress_dialog.h"
36
#include "editor/settings/editor_settings.h"
37
#include "editor/themes/editor_scale.h"
38
#include "scene/gui/box_container.h"
39
#include "scene/gui/label.h"
40
#include "scene/gui/panel.h"
41
#include "scene/gui/popup.h"
42
#include "scene/main/window.h"
43
44
// WindowWrapper
45
46
// Capture all shortcut events not handled by other nodes.
47
class ShortcutBin : public Node {
48
GDCLASS(ShortcutBin, Node);
49
50
virtual void _notification(int what) {
51
switch (what) {
52
case NOTIFICATION_READY:
53
set_process_shortcut_input(true);
54
break;
55
}
56
}
57
58
virtual void shortcut_input(const Ref<InputEvent> &p_event) override {
59
if (!get_window()->is_visible()) {
60
return;
61
}
62
Window *grandparent_window = get_window()->get_parent_visible_window();
63
ERR_FAIL_NULL(grandparent_window);
64
65
if (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventShortcut>(p_event.ptr())) {
66
// HACK: Propagate the window input to the editor main window to handle global shortcuts.
67
grandparent_window->push_input(p_event);
68
69
if (grandparent_window->is_input_handled()) {
70
get_viewport()->set_input_as_handled();
71
}
72
}
73
}
74
};
75
76
Rect2 WindowWrapper::_get_default_window_rect() const {
77
// Assume that the control rect is the desired one for the window.
78
return wrapped_control->get_screen_rect();
79
}
80
81
Node *WindowWrapper::_get_wrapped_control_parent() const {
82
if (margins) {
83
return margins;
84
}
85
return window;
86
}
87
88
void WindowWrapper::_set_window_enabled_with_rect(bool p_visible, const Rect2 p_rect) {
89
ERR_FAIL_NULL(wrapped_control);
90
91
if (!is_window_available()) {
92
return;
93
}
94
95
if (window->is_visible() == p_visible) {
96
if (p_visible) {
97
window->grab_focus();
98
}
99
return;
100
}
101
102
Node *parent = _get_wrapped_control_parent();
103
104
if (wrapped_control->get_parent() != parent) {
105
// Move the control to the window.
106
wrapped_control->reparent(parent, false);
107
108
_set_window_rect(p_rect);
109
wrapped_control->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
110
111
} else if (!p_visible) {
112
// Remove control from window.
113
wrapped_control->reparent(this, false);
114
}
115
116
window->set_visible(p_visible);
117
if (!p_visible && !override_close_request) {
118
emit_signal("window_close_requested");
119
}
120
emit_signal("window_visibility_changed", p_visible);
121
}
122
123
void WindowWrapper::_set_window_rect(const Rect2 p_rect) {
124
// Set the window rect even when the window is maximized to have a good default size
125
// when the user remove the maximized mode.
126
window->set_position(p_rect.position);
127
window->set_size(p_rect.size);
128
129
if (EDITOR_GET("interface/multi_window/maximize_window")) {
130
window->set_mode(Window::MODE_MAXIMIZED);
131
}
132
}
133
134
void WindowWrapper::_window_size_changed() {
135
emit_signal(SNAME("window_size_changed"));
136
}
137
138
void WindowWrapper::_window_close_request() {
139
if (override_close_request) {
140
emit_signal("window_close_requested");
141
} else {
142
set_window_enabled(false);
143
}
144
}
145
146
void WindowWrapper::_bind_methods() {
147
ADD_SIGNAL(MethodInfo("window_visibility_changed", PropertyInfo(Variant::BOOL, "visible")));
148
ADD_SIGNAL(MethodInfo("window_close_requested"));
149
ADD_SIGNAL(MethodInfo("window_size_changed"));
150
}
151
152
void WindowWrapper::_notification(int p_what) {
153
if (!is_window_available()) {
154
return;
155
}
156
switch (p_what) {
157
case NOTIFICATION_VISIBILITY_CHANGED: {
158
// Grab the focus when WindowWrapper.set_visible(true) is called
159
// and the window is showing.
160
grab_window_focus();
161
} break;
162
case NOTIFICATION_READY: {
163
set_process_shortcut_input(true);
164
} break;
165
case NOTIFICATION_THEME_CHANGED: {
166
window_background->add_theme_style_override(SceneStringName(panel), get_theme_stylebox("PanelForeground", EditorStringName(EditorStyles)));
167
} break;
168
}
169
}
170
171
void WindowWrapper::shortcut_input(const Ref<InputEvent> &p_event) {
172
if (enable_shortcut.is_valid() && enable_shortcut->matches_event(p_event)) {
173
set_window_enabled(true);
174
}
175
}
176
177
void WindowWrapper::set_wrapped_control(Control *p_control, const Ref<Shortcut> &p_enable_shortcut) {
178
ERR_FAIL_NULL(p_control);
179
ERR_FAIL_COND(wrapped_control);
180
181
wrapped_control = p_control;
182
enable_shortcut = p_enable_shortcut;
183
add_child(p_control);
184
}
185
186
Control *WindowWrapper::get_wrapped_control() const {
187
return wrapped_control;
188
}
189
190
Control *WindowWrapper::release_wrapped_control() {
191
set_window_enabled(false);
192
if (wrapped_control) {
193
Control *old_wrapped = wrapped_control;
194
wrapped_control->get_parent()->remove_child(wrapped_control);
195
wrapped_control = nullptr;
196
197
return old_wrapped;
198
}
199
return nullptr;
200
}
201
202
bool WindowWrapper::is_window_available() const {
203
return window != nullptr;
204
}
205
206
bool WindowWrapper::get_window_enabled() const {
207
return is_window_available() ? window->is_visible() : false;
208
}
209
210
void WindowWrapper::set_window_enabled(bool p_enabled) {
211
_set_window_enabled_with_rect(p_enabled, _get_default_window_rect());
212
}
213
214
Rect2i WindowWrapper::get_window_rect() const {
215
ERR_FAIL_COND_V(!get_window_enabled(), Rect2i());
216
return Rect2i(window->get_position(), window->get_size());
217
}
218
219
int WindowWrapper::get_window_screen() const {
220
ERR_FAIL_COND_V(!get_window_enabled(), -1);
221
return window->get_current_screen();
222
}
223
224
void WindowWrapper::restore_window(const Rect2i &p_rect, int p_screen) {
225
ERR_FAIL_COND(!is_window_available());
226
ERR_FAIL_INDEX(p_screen, DisplayServer::get_singleton()->get_screen_count());
227
228
_set_window_enabled_with_rect(true, p_rect);
229
window->set_current_screen(p_screen);
230
}
231
232
void WindowWrapper::restore_window_from_saved_position(const Rect2 p_window_rect, int p_screen, const Rect2 p_screen_rect) {
233
ERR_FAIL_COND(!is_window_available());
234
235
Rect2 window_rect = p_window_rect;
236
int screen = p_screen;
237
Rect2 restored_screen_rect = p_screen_rect;
238
239
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_SELF_FITTING_WINDOWS)) {
240
window_rect = Rect2i();
241
restored_screen_rect = Rect2i();
242
}
243
244
if (screen < 0 || screen >= DisplayServer::get_singleton()->get_screen_count()) {
245
// Fallback to the main window screen if the saved screen is not available.
246
screen = get_window()->get_window_id();
247
}
248
249
Rect2i real_screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(screen);
250
251
if (restored_screen_rect == Rect2i()) {
252
// Fallback to the target screen rect.
253
restored_screen_rect = real_screen_rect;
254
}
255
256
if (window_rect == Rect2i()) {
257
// Fallback to a standard rect.
258
window_rect = Rect2i(restored_screen_rect.position + restored_screen_rect.size / 4, restored_screen_rect.size / 2);
259
}
260
261
// Adjust the window rect size in case the resolution changes.
262
Vector2 screen_ratio = Vector2(real_screen_rect.size) / Vector2(restored_screen_rect.size);
263
264
// The screen positioning may change, so remove the original screen position.
265
window_rect.position -= restored_screen_rect.position;
266
window_rect = Rect2i(window_rect.position * screen_ratio, window_rect.size * screen_ratio);
267
window_rect.position += real_screen_rect.position;
268
269
// Make sure to restore the window if the user minimized it the last time it was displayed.
270
if (window->get_mode() == Window::MODE_MINIMIZED) {
271
window->set_mode(Window::MODE_WINDOWED);
272
}
273
274
// All good, restore the window.
275
window->set_current_screen(p_screen);
276
if (window->is_visible()) {
277
_set_window_rect(window_rect);
278
} else {
279
_set_window_enabled_with_rect(true, window_rect);
280
}
281
}
282
283
void WindowWrapper::enable_window_on_screen(int p_screen, bool p_auto_scale) {
284
int current_screen = Object::cast_to<Window>(get_viewport())->get_current_screen();
285
int screen = p_screen < 0 ? current_screen : p_screen;
286
287
bool auto_scale = p_auto_scale && !EDITOR_GET("interface/multi_window/maximize_window");
288
289
if (auto_scale && current_screen != screen) {
290
Rect2 control_rect = _get_default_window_rect();
291
292
Rect2i source_screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(current_screen);
293
Rect2i dest_screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(screen);
294
295
// Adjust the window rect size in case the resolution changes.
296
Vector2 screen_ratio = Vector2(source_screen_rect.size) / Vector2(dest_screen_rect.size);
297
298
// The screen positioning may change, so remove the original screen position.
299
control_rect.position -= source_screen_rect.position;
300
control_rect = Rect2i(control_rect.position * screen_ratio, control_rect.size * screen_ratio);
301
control_rect.position += dest_screen_rect.position;
302
303
restore_window(control_rect, p_screen);
304
} else {
305
window->set_current_screen(p_screen);
306
set_window_enabled(true);
307
}
308
}
309
310
void WindowWrapper::set_window_title(const String &p_title) {
311
if (!is_window_available()) {
312
return;
313
}
314
window->set_title(p_title);
315
}
316
317
void WindowWrapper::set_margins_enabled(bool p_enabled) {
318
if (!is_window_available()) {
319
return;
320
}
321
322
if (!p_enabled && margins) {
323
margins->queue_free();
324
margins = nullptr;
325
} else if (p_enabled && !margins) {
326
Size2 borders = Size2(4, 4) * EDSCALE;
327
margins = memnew(MarginContainer);
328
margins->add_theme_constant_override("margin_right", borders.width);
329
margins->add_theme_constant_override("margin_top", borders.height);
330
margins->add_theme_constant_override("margin_left", borders.width);
331
margins->add_theme_constant_override("margin_bottom", borders.height);
332
333
window->add_child(margins);
334
margins->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
335
}
336
}
337
338
Size2 WindowWrapper::get_margins_size() {
339
if (!margins) {
340
return Size2();
341
}
342
343
return Size2(margins->get_margin_size(SIDE_LEFT) + margins->get_margin_size(SIDE_RIGHT), margins->get_margin_size(SIDE_TOP) + margins->get_margin_size(SIDE_RIGHT));
344
}
345
346
Size2 WindowWrapper::get_margins_top_left() {
347
if (!margins) {
348
return Size2();
349
}
350
351
return Size2(margins->get_margin_size(SIDE_LEFT), margins->get_margin_size(SIDE_TOP));
352
}
353
354
void WindowWrapper::grab_window_focus() {
355
if (get_window_enabled() && is_visible()) {
356
window->grab_focus();
357
}
358
}
359
360
void WindowWrapper::set_override_close_request(bool p_enabled) {
361
override_close_request = p_enabled;
362
}
363
364
WindowWrapper::WindowWrapper() {
365
if (!EditorNode::get_singleton()->is_multi_window_enabled()) {
366
return;
367
}
368
369
window = memnew(Window);
370
window_id = window->get_instance_id();
371
window->set_wrap_controls(true);
372
373
add_child(window);
374
window->hide();
375
376
window->connect("close_requested", callable_mp(this, &WindowWrapper::_window_close_request));
377
window->connect("size_changed", callable_mp(this, &WindowWrapper::_window_size_changed));
378
379
ShortcutBin *capturer = memnew(ShortcutBin);
380
window->add_child(capturer);
381
382
window_background = memnew(Panel);
383
window_background->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
384
window->add_child(window_background);
385
386
ProgressDialog::get_singleton()->add_host_window(window_id);
387
}
388
389
WindowWrapper::~WindowWrapper() {
390
if (ProgressDialog::get_singleton()) {
391
ProgressDialog::get_singleton()->remove_host_window(window_id);
392
}
393
}
394
395
// ScreenSelect
396
397
void ScreenSelect::_build_advanced_menu() {
398
// Clear old screen list.
399
while (screen_list->get_child_count(false) > 0) {
400
Node *child = screen_list->get_child(0);
401
screen_list->remove_child(child);
402
child->queue_free();
403
}
404
405
// Populate screen list.
406
const real_t height = real_t(get_theme_font_size(SceneStringName(font_size))) * 1.5;
407
408
int current_screen = get_window()->get_current_screen();
409
for (int i = 0; i < DisplayServer::get_singleton()->get_screen_count(); i++) {
410
Button *button = memnew(Button);
411
412
Size2 screen_size = Size2(DisplayServer::get_singleton()->screen_get_size(i));
413
Size2 button_size = Size2(height * (screen_size.x / screen_size.y), height);
414
button->set_custom_minimum_size(button_size);
415
screen_list->add_child(button);
416
417
button->set_text(itos(i));
418
button->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER);
419
button->set_tooltip_text(vformat(TTR("Make this panel floating in the screen %d."), i));
420
421
if (i == current_screen) {
422
Color accent_color = get_theme_color("accent_color", EditorStringName(Editor));
423
button->add_theme_color_override(SceneStringName(font_color), accent_color);
424
}
425
426
button->connect(SceneStringName(pressed), callable_mp(this, &ScreenSelect::_emit_screen_signal).bind(i));
427
button->connect(SceneStringName(pressed), callable_mp(static_cast<BaseButton *>(this), &ScreenSelect::set_pressed).bind(false));
428
button->connect(SceneStringName(pressed), callable_mp(static_cast<Window *>(popup), &Popup::hide));
429
}
430
}
431
432
void ScreenSelect::_emit_screen_signal(int p_screen_idx) {
433
if (!is_disabled()) {
434
emit_signal("request_open_in_screen", p_screen_idx);
435
}
436
}
437
438
void ScreenSelect::_bind_methods() {
439
ADD_SIGNAL(MethodInfo("request_open_in_screen", PropertyInfo(Variant::INT, "screen")));
440
}
441
442
void ScreenSelect::_notification(int p_what) {
443
switch (p_what) {
444
case NOTIFICATION_READY: {
445
connect(SceneStringName(gui_input), callable_mp(this, &ScreenSelect::_handle_mouse_shortcut));
446
} break;
447
case NOTIFICATION_THEME_CHANGED: {
448
set_button_icon(get_editor_theme_icon("MakeFloating"));
449
450
const real_t popup_height = real_t(get_theme_font_size(SceneStringName(font_size))) * 2.0;
451
popup->set_min_size(Size2(0, popup_height * 3));
452
} break;
453
}
454
}
455
456
void ScreenSelect::_handle_mouse_shortcut(const Ref<InputEvent> &p_event) {
457
const Ref<InputEventMouseButton> mouse_button = p_event;
458
if (mouse_button.is_valid()) {
459
if (mouse_button->is_pressed() && mouse_button->get_button_index() == MouseButton::LEFT) {
460
_emit_screen_signal(get_window()->get_current_screen());
461
accept_event();
462
}
463
}
464
}
465
466
void ScreenSelect::_show_popup() {
467
// Adapted from /scene/gui/menu_button.cpp::show_popup
468
if (!get_viewport()) {
469
return;
470
}
471
472
Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale();
473
474
popup->set_size(Size2(size.width, 0));
475
Point2 gp = get_screen_position();
476
gp.y += size.y;
477
if (is_layout_rtl()) {
478
gp.x += size.width - popup->get_size().width;
479
}
480
popup->set_position(gp);
481
popup->popup();
482
}
483
484
void ScreenSelect::pressed() {
485
if (popup->is_visible()) {
486
popup->hide();
487
return;
488
}
489
490
_build_advanced_menu();
491
_show_popup();
492
}
493
494
ScreenSelect::ScreenSelect() {
495
set_button_mask(MouseButtonMask::RIGHT);
496
set_theme_type_variation(SceneStringName(FlatButton));
497
set_toggle_mode(true);
498
set_focus_mode(FOCUS_NONE);
499
set_action_mode(ACTION_MODE_BUTTON_PRESS);
500
501
if (!EditorNode::get_singleton()->is_multi_window_enabled()) {
502
set_disabled(true);
503
set_tooltip_text(EditorNode::get_singleton()->get_multiwindow_support_tooltip_text());
504
} else {
505
set_tooltip_text(TTR("Make this panel floating.") + "\n" + TTR("Right-click to open the screen selector."));
506
}
507
508
// Create the popup.
509
const Size2 borders = Size2(4, 4) * EDSCALE;
510
511
popup = memnew(PopupPanel);
512
popup->connect("popup_hide", callable_mp(static_cast<BaseButton *>(this), &ScreenSelect::set_pressed).bind(false));
513
add_child(popup);
514
515
MarginContainer *popup_root = memnew(MarginContainer);
516
popup_root->add_theme_constant_override("margin_right", borders.width);
517
popup_root->add_theme_constant_override("margin_top", borders.height);
518
popup_root->add_theme_constant_override("margin_left", borders.width);
519
popup_root->add_theme_constant_override("margin_bottom", borders.height);
520
popup->add_child(popup_root);
521
522
VBoxContainer *vb = memnew(VBoxContainer);
523
vb->set_alignment(BoxContainer::ALIGNMENT_CENTER);
524
popup_root->add_child(vb);
525
526
Label *description = memnew(Label(TTR("Select Screen")));
527
description->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
528
vb->add_child(description);
529
530
screen_list = memnew(HBoxContainer);
531
screen_list->set_alignment(BoxContainer::ALIGNMENT_CENTER);
532
vb->add_child(screen_list);
533
534
popup_root->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
535
}
536
537