Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/gui/window_wrapper.cpp
9903 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 (screen < 0 || screen >= DisplayServer::get_singleton()->get_screen_count()) {
240
// Fallback to the main window screen if the saved screen is not available.
241
screen = get_window()->get_window_id();
242
}
243
244
Rect2i real_screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(screen);
245
246
if (restored_screen_rect == Rect2i()) {
247
// Fallback to the target screen rect.
248
restored_screen_rect = real_screen_rect;
249
}
250
251
if (window_rect == Rect2i()) {
252
// Fallback to a standard rect.
253
window_rect = Rect2i(restored_screen_rect.position + restored_screen_rect.size / 4, restored_screen_rect.size / 2);
254
}
255
256
// Adjust the window rect size in case the resolution changes.
257
Vector2 screen_ratio = Vector2(real_screen_rect.size) / Vector2(restored_screen_rect.size);
258
259
// The screen positioning may change, so remove the original screen position.
260
window_rect.position -= restored_screen_rect.position;
261
window_rect = Rect2i(window_rect.position * screen_ratio, window_rect.size * screen_ratio);
262
window_rect.position += real_screen_rect.position;
263
264
// Make sure to restore the window if the user minimized it the last time it was displayed.
265
if (window->get_mode() == Window::MODE_MINIMIZED) {
266
window->set_mode(Window::MODE_WINDOWED);
267
}
268
269
// All good, restore the window.
270
window->set_current_screen(p_screen);
271
if (window->is_visible()) {
272
_set_window_rect(window_rect);
273
} else {
274
_set_window_enabled_with_rect(true, window_rect);
275
}
276
}
277
278
void WindowWrapper::enable_window_on_screen(int p_screen, bool p_auto_scale) {
279
int current_screen = Object::cast_to<Window>(get_viewport())->get_current_screen();
280
int screen = p_screen < 0 ? current_screen : p_screen;
281
282
bool auto_scale = p_auto_scale && !EDITOR_GET("interface/multi_window/maximize_window");
283
284
if (auto_scale && current_screen != screen) {
285
Rect2 control_rect = _get_default_window_rect();
286
287
Rect2i source_screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(current_screen);
288
Rect2i dest_screen_rect = DisplayServer::get_singleton()->screen_get_usable_rect(screen);
289
290
// Adjust the window rect size in case the resolution changes.
291
Vector2 screen_ratio = Vector2(source_screen_rect.size) / Vector2(dest_screen_rect.size);
292
293
// The screen positioning may change, so remove the original screen position.
294
control_rect.position -= source_screen_rect.position;
295
control_rect = Rect2i(control_rect.position * screen_ratio, control_rect.size * screen_ratio);
296
control_rect.position += dest_screen_rect.position;
297
298
restore_window(control_rect, p_screen);
299
} else {
300
window->set_current_screen(p_screen);
301
set_window_enabled(true);
302
}
303
}
304
305
void WindowWrapper::set_window_title(const String &p_title) {
306
if (!is_window_available()) {
307
return;
308
}
309
window->set_title(p_title);
310
}
311
312
void WindowWrapper::set_margins_enabled(bool p_enabled) {
313
if (!is_window_available()) {
314
return;
315
}
316
317
if (!p_enabled && margins) {
318
margins->queue_free();
319
margins = nullptr;
320
} else if (p_enabled && !margins) {
321
Size2 borders = Size2(4, 4) * EDSCALE;
322
margins = memnew(MarginContainer);
323
margins->add_theme_constant_override("margin_right", borders.width);
324
margins->add_theme_constant_override("margin_top", borders.height);
325
margins->add_theme_constant_override("margin_left", borders.width);
326
margins->add_theme_constant_override("margin_bottom", borders.height);
327
328
window->add_child(margins);
329
margins->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
330
}
331
}
332
333
Size2 WindowWrapper::get_margins_size() {
334
if (!margins) {
335
return Size2();
336
}
337
338
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));
339
}
340
341
Size2 WindowWrapper::get_margins_top_left() {
342
if (!margins) {
343
return Size2();
344
}
345
346
return Size2(margins->get_margin_size(SIDE_LEFT), margins->get_margin_size(SIDE_TOP));
347
}
348
349
void WindowWrapper::grab_window_focus() {
350
if (get_window_enabled() && is_visible()) {
351
window->grab_focus();
352
}
353
}
354
355
void WindowWrapper::set_override_close_request(bool p_enabled) {
356
override_close_request = p_enabled;
357
}
358
359
WindowWrapper::WindowWrapper() {
360
if (!EditorNode::get_singleton()->is_multi_window_enabled()) {
361
return;
362
}
363
364
window = memnew(Window);
365
window_id = window->get_instance_id();
366
window->set_wrap_controls(true);
367
368
add_child(window);
369
window->hide();
370
371
window->connect("close_requested", callable_mp(this, &WindowWrapper::_window_close_request));
372
window->connect("size_changed", callable_mp(this, &WindowWrapper::_window_size_changed));
373
374
ShortcutBin *capturer = memnew(ShortcutBin);
375
window->add_child(capturer);
376
377
window_background = memnew(Panel);
378
window_background->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
379
window->add_child(window_background);
380
381
ProgressDialog::get_singleton()->add_host_window(window);
382
}
383
384
WindowWrapper::~WindowWrapper() {
385
if (ObjectDB::get_instance(window_id)) {
386
ProgressDialog::get_singleton()->remove_host_window(window);
387
}
388
}
389
390
// ScreenSelect
391
392
void ScreenSelect::_build_advanced_menu() {
393
// Clear old screen list.
394
while (screen_list->get_child_count(false) > 0) {
395
Node *child = screen_list->get_child(0);
396
screen_list->remove_child(child);
397
child->queue_free();
398
}
399
400
// Populate screen list.
401
const real_t height = real_t(get_theme_font_size(SceneStringName(font_size))) * 1.5;
402
403
int current_screen = get_window()->get_current_screen();
404
for (int i = 0; i < DisplayServer::get_singleton()->get_screen_count(); i++) {
405
Button *button = memnew(Button);
406
407
Size2 screen_size = Size2(DisplayServer::get_singleton()->screen_get_size(i));
408
Size2 button_size = Size2(height * (screen_size.x / screen_size.y), height);
409
button->set_custom_minimum_size(button_size);
410
screen_list->add_child(button);
411
412
button->set_text(itos(i));
413
button->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER);
414
button->set_tooltip_text(vformat(TTR("Make this panel floating in the screen %d."), i));
415
416
if (i == current_screen) {
417
Color accent_color = get_theme_color("accent_color", EditorStringName(Editor));
418
button->add_theme_color_override(SceneStringName(font_color), accent_color);
419
}
420
421
button->connect(SceneStringName(pressed), callable_mp(this, &ScreenSelect::_emit_screen_signal).bind(i));
422
button->connect(SceneStringName(pressed), callable_mp(static_cast<BaseButton *>(this), &ScreenSelect::set_pressed).bind(false));
423
button->connect(SceneStringName(pressed), callable_mp(static_cast<Window *>(popup), &Popup::hide));
424
}
425
}
426
427
void ScreenSelect::_emit_screen_signal(int p_screen_idx) {
428
if (!is_disabled()) {
429
emit_signal("request_open_in_screen", p_screen_idx);
430
}
431
}
432
433
void ScreenSelect::_bind_methods() {
434
ADD_SIGNAL(MethodInfo("request_open_in_screen", PropertyInfo(Variant::INT, "screen")));
435
}
436
437
void ScreenSelect::_notification(int p_what) {
438
switch (p_what) {
439
case NOTIFICATION_READY: {
440
connect(SceneStringName(gui_input), callable_mp(this, &ScreenSelect::_handle_mouse_shortcut));
441
} break;
442
case NOTIFICATION_THEME_CHANGED: {
443
set_button_icon(get_editor_theme_icon("MakeFloating"));
444
445
const real_t popup_height = real_t(get_theme_font_size(SceneStringName(font_size))) * 2.0;
446
popup->set_min_size(Size2(0, popup_height * 3));
447
} break;
448
}
449
}
450
451
void ScreenSelect::_handle_mouse_shortcut(const Ref<InputEvent> &p_event) {
452
const Ref<InputEventMouseButton> mouse_button = p_event;
453
if (mouse_button.is_valid()) {
454
if (mouse_button->is_pressed() && mouse_button->get_button_index() == MouseButton::LEFT) {
455
_emit_screen_signal(get_window()->get_current_screen());
456
accept_event();
457
}
458
}
459
}
460
461
void ScreenSelect::_show_popup() {
462
// Adapted from /scene/gui/menu_button.cpp::show_popup
463
if (!get_viewport()) {
464
return;
465
}
466
467
Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale();
468
469
popup->set_size(Size2(size.width, 0));
470
Point2 gp = get_screen_position();
471
gp.y += size.y;
472
if (is_layout_rtl()) {
473
gp.x += size.width - popup->get_size().width;
474
}
475
popup->set_position(gp);
476
popup->popup();
477
}
478
479
void ScreenSelect::pressed() {
480
if (popup->is_visible()) {
481
popup->hide();
482
return;
483
}
484
485
_build_advanced_menu();
486
_show_popup();
487
}
488
489
ScreenSelect::ScreenSelect() {
490
set_button_mask(MouseButtonMask::RIGHT);
491
set_theme_type_variation(SceneStringName(FlatButton));
492
set_toggle_mode(true);
493
set_focus_mode(FOCUS_NONE);
494
set_action_mode(ACTION_MODE_BUTTON_PRESS);
495
496
if (!EditorNode::get_singleton()->is_multi_window_enabled()) {
497
set_disabled(true);
498
set_tooltip_text(EditorNode::get_singleton()->get_multiwindow_support_tooltip_text());
499
} else {
500
set_tooltip_text(TTR("Make this panel floating.") + "\n" + TTR("Right-click to open the screen selector."));
501
}
502
503
// Create the popup.
504
const Size2 borders = Size2(4, 4) * EDSCALE;
505
506
popup = memnew(PopupPanel);
507
popup->connect("popup_hide", callable_mp(static_cast<BaseButton *>(this), &ScreenSelect::set_pressed).bind(false));
508
add_child(popup);
509
510
MarginContainer *popup_root = memnew(MarginContainer);
511
popup_root->add_theme_constant_override("margin_right", borders.width);
512
popup_root->add_theme_constant_override("margin_top", borders.height);
513
popup_root->add_theme_constant_override("margin_left", borders.width);
514
popup_root->add_theme_constant_override("margin_bottom", borders.height);
515
popup->add_child(popup_root);
516
517
VBoxContainer *vb = memnew(VBoxContainer);
518
vb->set_alignment(BoxContainer::ALIGNMENT_CENTER);
519
popup_root->add_child(vb);
520
521
Label *description = memnew(Label(TTR("Select Screen")));
522
description->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
523
vb->add_child(description);
524
525
screen_list = memnew(HBoxContainer);
526
screen_list->set_alignment(BoxContainer::ALIGNMENT_CENTER);
527
vb->add_child(screen_list);
528
529
popup_root->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
530
}
531
532