Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/main/viewport.cpp
20870 views
1
/**************************************************************************/
2
/* viewport.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 "viewport.h"
32
33
#include "core/config/project_settings.h"
34
#include "core/debugger/engine_debugger.h"
35
#include "core/input/input.h"
36
#include "core/templates/pair.h"
37
#include "core/templates/sort_array.h"
38
#include "scene/gui/control.h"
39
#include "scene/gui/label.h"
40
#include "scene/gui/popup.h"
41
#include "scene/gui/popup_menu.h"
42
#include "scene/gui/subviewport_container.h"
43
#include "scene/main/canvas_layer.h"
44
#include "scene/main/window.h"
45
#include "scene/resources/dpi_texture.h"
46
#include "scene/resources/mesh.h"
47
#include "scene/resources/text_line.h"
48
#include "scene/resources/world_2d.h"
49
#include "servers/audio/audio_server.h"
50
#include "servers/rendering/rendering_server_globals.h"
51
52
// 2D.
53
#include "scene/2d/audio_listener_2d.h"
54
#include "scene/2d/camera_2d.h"
55
56
#ifndef _3D_DISABLED
57
#include "scene/3d/audio_listener_3d.h"
58
#include "scene/3d/camera_3d.h"
59
#include "scene/3d/world_environment.h"
60
#endif // _3D_DISABLED
61
62
#ifndef PHYSICS_2D_DISABLED
63
#include "scene/2d/physics/collision_object_2d.h"
64
#endif // PHYSICS_2D_DISABLED
65
66
#ifndef PHYSICS_3D_DISABLED
67
#include "scene/3d/physics/collision_object_3d.h"
68
#endif // PHYSICS_3D_DISABLED
69
70
void ViewportTexture::setup_local_to_scene() {
71
// For the same target viewport, setup is only allowed once to prevent multiple free or multiple creations.
72
if (!vp_changed) {
73
return;
74
}
75
76
if (vp_pending) {
77
return;
78
}
79
80
Node *loc_scene = get_local_scene();
81
if (!loc_scene) {
82
return;
83
}
84
85
if (vp) {
86
vp->viewport_textures.erase(this);
87
vp = nullptr;
88
}
89
90
if (loc_scene->is_ready()) {
91
_setup_local_to_scene(loc_scene);
92
} else {
93
loc_scene->connect(SceneStringName(ready), callable_mp(this, &ViewportTexture::_setup_local_to_scene).bind(loc_scene), CONNECT_ONE_SHOT);
94
vp_pending = true;
95
}
96
}
97
98
void ViewportTexture::reset_local_to_scene() {
99
vp_changed = true;
100
101
if (vp) {
102
vp->viewport_textures.erase(this);
103
vp = nullptr;
104
}
105
106
if (proxy.is_valid() && proxy_ph.is_null()) {
107
proxy_ph = RS::get_singleton()->texture_2d_placeholder_create();
108
RS::get_singleton()->texture_proxy_update(proxy, proxy_ph);
109
}
110
}
111
112
void ViewportTexture::set_viewport_path_in_scene(const NodePath &p_path) {
113
if (path == p_path) {
114
return;
115
}
116
117
path = p_path;
118
119
reset_local_to_scene();
120
121
if (get_local_scene() && !path.is_empty()) {
122
setup_local_to_scene();
123
} else {
124
if (path.is_empty()) {
125
vp_changed = false;
126
}
127
emit_changed();
128
}
129
}
130
131
NodePath ViewportTexture::get_viewport_path_in_scene() const {
132
return path;
133
}
134
135
int ViewportTexture::get_width() const {
136
if (!vp) {
137
_err_print_viewport_not_set();
138
return 0;
139
}
140
if (vp->is_sub_viewport()) {
141
return vp->size.width;
142
}
143
return vp->size.width * vp->get_stretch_transform().get_scale().width;
144
}
145
146
int ViewportTexture::get_height() const {
147
if (!vp) {
148
_err_print_viewport_not_set();
149
return 0;
150
}
151
if (vp->is_sub_viewport()) {
152
return vp->size.height;
153
}
154
return vp->size.height * vp->get_stretch_transform().get_scale().height;
155
}
156
157
Size2 ViewportTexture::get_size() const {
158
if (!vp) {
159
_err_print_viewport_not_set();
160
return Size2();
161
}
162
if (vp->is_sub_viewport()) {
163
return vp->size;
164
}
165
Size2 scale = vp->get_stretch_transform().get_scale();
166
return Size2(vp->size.width * scale.width, vp->size.height * scale.height).ceil();
167
}
168
169
RID ViewportTexture::get_rid() const {
170
if (proxy.is_null()) {
171
proxy_ph = RS::get_singleton()->texture_2d_placeholder_create();
172
proxy = RS::get_singleton()->texture_proxy_create(proxy_ph);
173
}
174
return proxy;
175
}
176
177
bool ViewportTexture::has_alpha() const {
178
return false;
179
}
180
181
Ref<Image> ViewportTexture::get_image() const {
182
if (!vp) {
183
_err_print_viewport_not_set();
184
return Ref<Image>();
185
}
186
return RS::get_singleton()->texture_2d_get(vp->texture_rid);
187
}
188
189
void ViewportTexture::_err_print_viewport_not_set() const {
190
if (!vp_pending && !vp_changed) {
191
ERR_PRINT("Viewport Texture must be set to use it.");
192
}
193
}
194
195
void ViewportTexture::_setup_local_to_scene(const Node *p_loc_scene) {
196
// Always reset this, even if this call fails with an error.
197
vp_pending = false;
198
199
Node *vpn = p_loc_scene->get_node_or_null(path);
200
ERR_FAIL_NULL_MSG(vpn, "Path to node is invalid: '" + String(path) + "'.");
201
vp = Object::cast_to<Viewport>(vpn);
202
ERR_FAIL_NULL_MSG(vp, "Path to node does not point to a viewport: '" + String(path) + "'.");
203
204
vp->viewport_textures.insert(this);
205
206
ERR_FAIL_NULL(RenderingServer::get_singleton());
207
if (proxy_ph.is_valid()) {
208
RS::get_singleton()->texture_proxy_update(proxy, vp->texture_rid);
209
RS::get_singleton()->free_rid(proxy_ph);
210
proxy_ph = RID();
211
} else {
212
ERR_FAIL_COND(proxy.is_valid()); // Should be invalid.
213
proxy = RS::get_singleton()->texture_proxy_create(vp->texture_rid);
214
}
215
vp_changed = false;
216
217
emit_changed();
218
}
219
220
void ViewportTexture::_bind_methods() {
221
ClassDB::bind_method(D_METHOD("set_viewport_path_in_scene", "path"), &ViewportTexture::set_viewport_path_in_scene);
222
ClassDB::bind_method(D_METHOD("get_viewport_path_in_scene"), &ViewportTexture::get_viewport_path_in_scene);
223
224
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewport_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Viewport", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT), "set_viewport_path_in_scene", "get_viewport_path_in_scene");
225
}
226
227
ViewportTexture::ViewportTexture() {
228
set_local_to_scene(true);
229
}
230
231
ViewportTexture::~ViewportTexture() {
232
if (vp) {
233
vp->viewport_textures.erase(this);
234
}
235
236
ERR_FAIL_NULL(RenderingServer::get_singleton());
237
238
if (proxy_ph.is_valid()) {
239
RS::get_singleton()->free_rid(proxy_ph);
240
}
241
if (proxy.is_valid()) {
242
RS::get_singleton()->free_rid(proxy);
243
}
244
}
245
246
void Viewport::_process_dirty_canvas_parent_orders() {
247
for (const ObjectID &id : gui.canvas_parents_with_dirty_order) {
248
Object *obj = ObjectDB::get_instance(id);
249
if (!obj) {
250
continue; // May have been deleted.
251
}
252
253
Node *n = static_cast<Node *>(obj);
254
for (int i = 0; i < n->get_child_count(); i++) {
255
Node *c = n->get_child(i);
256
CanvasItem *ci = Object::cast_to<CanvasItem>(c);
257
if (ci) {
258
ci->update_draw_order();
259
continue;
260
}
261
CanvasLayer *cl = Object::cast_to<CanvasLayer>(c);
262
if (cl) {
263
cl->update_draw_order();
264
}
265
}
266
}
267
268
gui.canvas_parents_with_dirty_order.clear();
269
}
270
271
void Viewport::_sub_window_update_order() {
272
if (gui.sub_windows.size() < 2) {
273
return;
274
}
275
276
// Reorder 'always on top' windows.
277
int last_index = gui.sub_windows.size() - 1;
278
for (int index = last_index, insert_index = last_index; index >= 0; index--) {
279
SubWindow sw = gui.sub_windows[index];
280
Window *parent_window = sw.window->get_parent_visible_window();
281
bool parent_is_always_on_top = (parent_window != nullptr) && parent_window->get_flag(Window::FLAG_ALWAYS_ON_TOP);
282
if (sw.window->get_flag(Window::FLAG_ALWAYS_ON_TOP) || (parent_is_always_on_top && sw.window->is_exclusive())) {
283
if (index != insert_index) {
284
gui.sub_windows.remove_at(index);
285
gui.sub_windows.insert(insert_index, sw);
286
}
287
insert_index--;
288
}
289
}
290
291
// Reorder exclusive children.
292
for (int parent_index = 0; parent_index < gui.sub_windows.size(); parent_index++) {
293
Window *exclusive_child = gui.sub_windows[parent_index].window->get_exclusive_child();
294
if (exclusive_child != nullptr && exclusive_child->is_visible()) {
295
int child_index = _sub_window_find(exclusive_child);
296
if (child_index < parent_index) {
297
SubWindow sw = gui.sub_windows[child_index];
298
gui.sub_windows.remove_at(child_index);
299
gui.sub_windows.insert(parent_index, sw);
300
}
301
}
302
}
303
304
for (int i = 0; i < gui.sub_windows.size(); i++) {
305
RS::get_singleton()->canvas_item_set_draw_index(gui.sub_windows[i].canvas_item, i);
306
}
307
}
308
309
void Viewport::_sub_window_register(Window *p_window) {
310
ERR_FAIL_COND(!is_inside_tree());
311
for (int i = 0; i < gui.sub_windows.size(); i++) {
312
ERR_FAIL_COND(gui.sub_windows[i].window == p_window);
313
}
314
315
if (gui.sub_windows.is_empty()) {
316
subwindow_canvas = RS::get_singleton()->canvas_create();
317
RS::get_singleton()->viewport_attach_canvas(viewport, subwindow_canvas);
318
RS::get_singleton()->viewport_set_canvas_stacking(viewport, subwindow_canvas, SUBWINDOW_CANVAS_LAYER, 0);
319
}
320
SubWindow sw;
321
sw.canvas_item = RS::get_singleton()->canvas_item_create();
322
RS::get_singleton()->canvas_item_set_parent(sw.canvas_item, subwindow_canvas);
323
sw.window = p_window;
324
gui.sub_windows.push_back(sw);
325
326
if (gui.subwindow_drag == SUB_WINDOW_DRAG_DISABLED) {
327
if (p_window->get_flag(Window::FLAG_NO_FOCUS)) {
328
_sub_window_update_order();
329
} else {
330
_sub_window_grab_focus(p_window);
331
}
332
} else {
333
int index = _sub_window_find(gui.currently_dragged_subwindow);
334
sw = gui.sub_windows[index];
335
gui.sub_windows.remove_at(index);
336
gui.sub_windows.push_back(sw);
337
_sub_window_update_order();
338
}
339
340
RenderingServer::get_singleton()->viewport_set_parent_viewport(p_window->viewport, viewport);
341
}
342
343
void Viewport::_sub_window_update(Window *p_window) {
344
int index = _sub_window_find(p_window);
345
346
// _sub_window_update is sometimes called deferred, and the window may have been closed since then.
347
// For example, when the user resizes the game window.
348
// In that case, _sub_window_find will not find it, which is expected.
349
if (index == -1) {
350
return;
351
}
352
353
SubWindow &sw = gui.sub_windows.write[index];
354
sw.pending_window_update = false;
355
356
RS::get_singleton()->canvas_item_clear(sw.canvas_item);
357
const Rect2i r = Rect2i(p_window->get_position(), p_window->get_size());
358
359
if (!p_window->get_flag(Window::FLAG_BORDERLESS)) {
360
TextServer::set_current_drawn_item_oversampling(get_oversampling());
361
362
Ref<StyleBox> panel = gui.subwindow_focused == p_window ? p_window->theme_cache.embedded_border : p_window->theme_cache.embedded_unfocused_border;
363
panel->draw(sw.canvas_item, r);
364
365
// Draw the title bar text.
366
Ref<Font> title_font = p_window->theme_cache.title_font;
367
int font_size = p_window->theme_cache.title_font_size;
368
Color title_color = p_window->theme_cache.title_color;
369
int title_height = p_window->theme_cache.title_height;
370
int close_h_ofs = p_window->theme_cache.close_h_offset;
371
int close_v_ofs = p_window->theme_cache.close_v_offset;
372
373
const real_t title_space = r.size.width - panel->get_minimum_size().x - close_h_ofs;
374
if (title_space > 0) {
375
TextLine title_text = TextLine(p_window->get_displayed_title(), title_font, font_size);
376
title_text.set_width(title_space);
377
title_text.set_direction(p_window->is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
378
int x = (r.size.width - title_text.get_size().x) / 2;
379
int y = (-title_height - title_text.get_size().y) / 2;
380
381
Color font_outline_color = p_window->theme_cache.title_outline_modulate;
382
int outline_size = p_window->theme_cache.title_outline_size;
383
if (outline_size > 0 && font_outline_color.a > 0) {
384
title_text.draw_outline(sw.canvas_item, r.position + Point2(x, y), outline_size, font_outline_color);
385
}
386
title_text.draw(sw.canvas_item, r.position + Point2(x, y), title_color);
387
}
388
389
bool pressed = gui.subwindow_focused == sw.window && gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE && gui.subwindow_drag_close_inside;
390
Ref<Texture2D> close_icon = pressed ? p_window->theme_cache.close_pressed : p_window->theme_cache.close;
391
close_icon->draw(sw.canvas_item, r.position + Vector2(r.size.width - close_h_ofs, -close_v_ofs));
392
393
TextServer::set_current_drawn_item_oversampling(0.0);
394
}
395
396
const Transform2D xform = sw.window->window_transform * sw.window->stretch_transform;
397
Rect2 vr = xform.xform(sw.window->get_visible_rect());
398
vr.position += p_window->get_position();
399
if (vr != r) {
400
RS::get_singleton()->canvas_item_add_rect(sw.canvas_item, r, Color());
401
}
402
RS::get_singleton()->canvas_item_add_texture_rect(sw.canvas_item, vr, sw.window->get_texture()->get_rid());
403
}
404
405
void Viewport::_sub_window_grab_focus(Window *p_window) {
406
if (p_window == nullptr) {
407
// Release current focus.
408
if (gui.subwindow_focused) {
409
gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
410
gui.subwindow_focused = nullptr;
411
gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
412
}
413
414
Window *this_window = Object::cast_to<Window>(this);
415
if (this_window) {
416
this_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN);
417
}
418
419
return;
420
}
421
422
// The index needs to be update before every usage in case an event callback changed the window list.
423
int index = _sub_window_find(p_window);
424
ERR_FAIL_COND(index == -1);
425
426
if (p_window->get_flag(Window::FLAG_NO_FOCUS)) {
427
// Release current focus.
428
if (gui.subwindow_focused) {
429
gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
430
gui.subwindow_focused = nullptr;
431
gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
432
}
433
// Can only move to foreground, but no focus granted.
434
index = _sub_window_find(p_window);
435
ERR_FAIL_COND(index == -1);
436
SubWindow sw = gui.sub_windows[index];
437
gui.sub_windows.remove_at(index);
438
gui.sub_windows.push_back(sw);
439
_sub_window_update_order();
440
return;
441
}
442
443
if (gui.subwindow_focused) {
444
if (gui.subwindow_focused == p_window) {
445
return; // Nothing to do.
446
}
447
gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
448
gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
449
} else {
450
Window *this_window = Object::cast_to<Window>(this);
451
if (this_window) {
452
this_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
453
}
454
}
455
456
Window *old_focus = gui.subwindow_focused;
457
458
gui.subwindow_focused = p_window;
459
460
gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN);
461
462
{ // Move to foreground.
463
index = _sub_window_find(p_window);
464
ERR_FAIL_COND(index == -1);
465
SubWindow sw = gui.sub_windows[index];
466
gui.sub_windows.remove_at(index);
467
gui.sub_windows.push_back(sw);
468
index = gui.sub_windows.size() - 1;
469
_sub_window_update_order();
470
}
471
472
if (old_focus) {
473
_sub_window_update(old_focus);
474
}
475
476
_sub_window_update(p_window);
477
}
478
479
void Viewport::_sub_window_remove(Window *p_window) {
480
int index = _sub_window_find(p_window);
481
ERR_FAIL_COND(index == -1);
482
483
ERR_FAIL_NULL(RenderingServer::get_singleton());
484
485
SubWindow sw = gui.sub_windows[index];
486
if (gui.subwindow_over == sw.window) {
487
sw.window->_mouse_leave_viewport();
488
gui.subwindow_over = nullptr;
489
}
490
RS::get_singleton()->free_rid(sw.canvas_item);
491
gui.sub_windows.remove_at(index);
492
493
if (gui.sub_windows.is_empty()) {
494
RS::get_singleton()->free_rid(subwindow_canvas);
495
subwindow_canvas = RID();
496
}
497
498
if (gui.currently_dragged_subwindow == p_window) {
499
gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
500
gui.currently_dragged_subwindow = nullptr;
501
}
502
503
if (gui.subwindow_focused == p_window) {
504
Window *new_focused_window;
505
Window *parent_visible = p_window->get_parent_visible_window();
506
507
gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
508
509
if (parent_visible) {
510
new_focused_window = parent_visible;
511
} else {
512
new_focused_window = Object::cast_to<Window>(this);
513
}
514
515
if (new_focused_window) {
516
int new_focused_index = _sub_window_find(new_focused_window);
517
if (new_focused_index != -1) {
518
gui.subwindow_focused = new_focused_window;
519
} else {
520
gui.subwindow_focused = nullptr;
521
}
522
523
new_focused_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN);
524
} else {
525
gui.subwindow_focused = nullptr;
526
}
527
}
528
529
RenderingServer::get_singleton()->viewport_set_parent_viewport(p_window->viewport, p_window->parent ? p_window->parent->viewport : RID());
530
}
531
532
int Viewport::_sub_window_find(Window *p_window) const {
533
for (int i = 0; i < gui.sub_windows.size(); i++) {
534
if (gui.sub_windows[i].window == p_window) {
535
return i;
536
}
537
}
538
539
return -1;
540
}
541
542
void Viewport::_update_viewport_path() {
543
if (!is_inside_tree()) {
544
return;
545
}
546
547
for (ViewportTexture *E : viewport_textures) {
548
Node *loc_scene = E->get_local_scene();
549
if (loc_scene) {
550
E->path = loc_scene->get_path_to(this);
551
}
552
}
553
}
554
555
bool Viewport::_can_hide_focus_state() {
556
return Engine::get_singleton()->is_editor_hint() || GLOBAL_GET_CACHED(int, "gui/common/show_focus_state_on_pointer_event") < 2;
557
}
558
559
void Viewport::_on_settings_changed() {
560
if (!gui.hide_focus || _can_hide_focus_state()) {
561
return;
562
}
563
564
gui.hide_focus = false;
565
// Show previously hidden focus.
566
if (gui.key_focus) {
567
gui.key_focus->queue_redraw();
568
}
569
}
570
571
void Viewport::_notification(int p_what) {
572
ERR_MAIN_THREAD_GUARD;
573
574
switch (p_what) {
575
case NOTIFICATION_ENTER_TREE: {
576
_update_viewport_path();
577
578
if (get_parent()) {
579
parent = get_parent()->get_viewport();
580
RenderingServer::get_singleton()->viewport_set_parent_viewport(viewport, parent->get_viewport_rid());
581
} else {
582
parent = nullptr;
583
}
584
585
current_canvas = find_world_2d()->get_canvas();
586
RenderingServer::get_singleton()->viewport_attach_canvas(viewport, current_canvas);
587
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, current_canvas, canvas_transform);
588
RenderingServer::get_singleton()->viewport_set_canvas_cull_mask(viewport, canvas_cull_mask);
589
_update_audio_listener_2d();
590
#ifndef _3D_DISABLED
591
RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario());
592
_update_audio_listener_3d();
593
#endif // _3D_DISABLED
594
595
add_to_group("_viewports");
596
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
597
if (get_tree()->is_debugging_collisions_hint()) {
598
#ifndef PHYSICS_2D_DISABLED
599
PhysicsServer2D::get_singleton()->space_set_debug_contacts(find_world_2d()->get_space(), get_tree()->get_collision_debug_contact_count());
600
contact_2d_debug = RenderingServer::get_singleton()->canvas_item_create();
601
RenderingServer::get_singleton()->canvas_item_set_parent(contact_2d_debug, current_canvas);
602
#endif // PHYSICS_2D_DISABLED
603
#ifndef PHYSICS_3D_DISABLED
604
PhysicsServer3D::get_singleton()->space_set_debug_contacts(find_world_3d()->get_space(), get_tree()->get_collision_debug_contact_count());
605
contact_3d_debug_multimesh = RenderingServer::get_singleton()->multimesh_create();
606
RenderingServer::get_singleton()->multimesh_allocate_data(contact_3d_debug_multimesh, get_tree()->get_collision_debug_contact_count(), RS::MULTIMESH_TRANSFORM_3D, false);
607
RenderingServer::get_singleton()->multimesh_set_visible_instances(contact_3d_debug_multimesh, 0);
608
RenderingServer::get_singleton()->multimesh_set_mesh(contact_3d_debug_multimesh, get_tree()->get_debug_contact_mesh()->get_rid());
609
contact_3d_debug_instance = RenderingServer::get_singleton()->instance_create();
610
RenderingServer::get_singleton()->instance_set_base(contact_3d_debug_instance, contact_3d_debug_multimesh);
611
RenderingServer::get_singleton()->instance_set_scenario(contact_3d_debug_instance, find_world_3d()->get_scenario());
612
RenderingServer::get_singleton()->instance_geometry_set_flag(contact_3d_debug_instance, RS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, true);
613
#endif // PHYSICS_3D_DISABLED
614
set_physics_process_internal(true);
615
}
616
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
617
} break;
618
619
case NOTIFICATION_READY: {
620
#ifndef _3D_DISABLED
621
if (audio_listener_3d_set.size() && !audio_listener_3d) {
622
AudioListener3D *first = nullptr;
623
for (AudioListener3D *E : audio_listener_3d_set) {
624
if (first == nullptr || first->is_greater_than(E)) {
625
first = E;
626
}
627
}
628
629
if (first) {
630
first->make_current();
631
}
632
}
633
634
if (camera_3d_set.size() && !camera_3d) {
635
// There are cameras but no current camera, pick first in tree and make it current.
636
Camera3D *first = nullptr;
637
for (Camera3D *E : camera_3d_set) {
638
if (first == nullptr || first->is_greater_than(E)) {
639
first = E;
640
}
641
}
642
643
if (first) {
644
first->make_current();
645
}
646
}
647
#endif // _3D_DISABLED
648
} break;
649
650
case NOTIFICATION_EXIT_TREE: {
651
_gui_cancel_tooltip();
652
653
RenderingServer::get_singleton()->viewport_set_scenario(viewport, RID());
654
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, current_canvas);
655
#ifndef PHYSICS_2D_DISABLED
656
if (contact_2d_debug.is_valid()) {
657
RenderingServer::get_singleton()->free_rid(contact_2d_debug);
658
contact_2d_debug = RID();
659
}
660
#endif // PHYSICS_2D_DISABLED
661
662
#ifndef PHYSICS_3D_DISABLED
663
if (contact_3d_debug_multimesh.is_valid()) {
664
RenderingServer::get_singleton()->free_rid(contact_3d_debug_multimesh);
665
RenderingServer::get_singleton()->free_rid(contact_3d_debug_instance);
666
contact_3d_debug_instance = RID();
667
contact_3d_debug_multimesh = RID();
668
}
669
#endif // PHYSICS_3D_DISABLED
670
671
remove_from_group("_viewports");
672
set_physics_process_internal(false);
673
674
RS::get_singleton()->viewport_set_active(viewport, false);
675
RenderingServer::get_singleton()->viewport_set_parent_viewport(viewport, RID());
676
} break;
677
678
case NOTIFICATION_PATH_RENAMED: {
679
_update_viewport_path();
680
} break;
681
682
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
683
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
684
if (!get_tree()) {
685
return;
686
}
687
688
#ifndef PHYSICS_2D_DISABLED
689
if (get_tree()->is_debugging_collisions_hint() && contact_2d_debug.is_valid()) {
690
RenderingServer::get_singleton()->canvas_item_clear(contact_2d_debug);
691
RenderingServer::get_singleton()->canvas_item_set_draw_index(contact_2d_debug, 0xFFFFF); //very high index
692
693
Vector<Vector2> points = PhysicsServer2D::get_singleton()->space_get_contacts(find_world_2d()->get_space());
694
int point_count = PhysicsServer2D::get_singleton()->space_get_contact_count(find_world_2d()->get_space());
695
Color ccol = get_tree()->get_debug_collision_contact_color();
696
697
for (int i = 0; i < point_count; i++) {
698
RenderingServer::get_singleton()->canvas_item_add_rect(contact_2d_debug, Rect2(points[i] - Vector2(2, 2), Vector2(5, 5)), ccol);
699
}
700
}
701
#endif // PHYSICS_2D_DISABLED
702
#ifndef PHYSICS_3D_DISABLED
703
if (get_tree()->is_debugging_collisions_hint() && contact_3d_debug_multimesh.is_valid()) {
704
Vector<Vector3> points = PhysicsServer3D::get_singleton()->space_get_contacts(find_world_3d()->get_space());
705
int point_count = PhysicsServer3D::get_singleton()->space_get_contact_count(find_world_3d()->get_space());
706
707
RS::get_singleton()->multimesh_set_visible_instances(contact_3d_debug_multimesh, point_count);
708
709
for (int i = 0; i < point_count; i++) {
710
Transform3D point_transform;
711
point_transform.origin = points[i];
712
RS::get_singleton()->multimesh_instance_set_transform(contact_3d_debug_multimesh, i, point_transform);
713
}
714
}
715
#endif // PHYSICS_3D_DISABLED
716
} break;
717
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
718
719
case NOTIFICATION_VP_MOUSE_ENTER: {
720
gui.mouse_in_viewport = true;
721
} break;
722
723
case NOTIFICATION_VP_MOUSE_EXIT: {
724
gui.mouse_in_viewport = false;
725
_drop_physics_mouseover();
726
// When the mouse exits the viewport, we don't want to end
727
// mouse_focus, because, for example, we want to continue
728
// dragging a scrollbar even if the mouse has left the viewport.
729
} break;
730
731
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
732
_gui_cancel_tooltip();
733
_drop_physics_mouseover();
734
_drop_mouse_focus();
735
// When the window focus changes, we want to end mouse_focus, but
736
// not the mouse_over. Note: The OS will trigger a separate mouse
737
// exit event if the change in focus results in the mouse exiting
738
// the window.
739
} break;
740
741
case NOTIFICATION_PREDELETE: {
742
if (gui_parent) {
743
gui_parent->gui.tooltip_popup = nullptr;
744
gui_parent->gui.tooltip_label = nullptr;
745
}
746
} break;
747
}
748
}
749
750
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
751
void Viewport::_process_picking() {
752
if (!is_inside_tree()) {
753
return;
754
}
755
if (!physics_object_picking) {
756
return;
757
}
758
if (Object::cast_to<Window>(this) && Input::get_singleton()->get_mouse_mode() == Input::MouseMode::MOUSE_MODE_CAPTURED) {
759
return;
760
}
761
if (!gui.mouse_in_viewport || gui.subwindow_over) {
762
// Clear picking events if the mouse has left the viewport or is over an embedded window.
763
// These are locations, that are expected to not trigger physics picking.
764
physics_picking_events.clear();
765
return;
766
}
767
#ifndef XR_DISABLED
768
if (use_xr) {
769
if (XRServer::get_singleton() != nullptr) {
770
Ref<XRInterface> xr_interface = XRServer::get_singleton()->get_primary_interface();
771
if (xr_interface.is_valid() && xr_interface->is_initialized() && xr_interface->get_view_count() > 1) {
772
WARN_PRINT_ONCE("Object picking can't be used when stereo rendering, this will be turned off!");
773
physics_object_picking = false; // don't try again.
774
return;
775
}
776
}
777
}
778
#endif // XR_DISABLED
779
780
_drop_physics_mouseover(true);
781
782
#ifndef PHYSICS_3D_DISABLED
783
Vector2 last_pos(1e20, 1e20);
784
CollisionObject3D *last_object = nullptr;
785
ObjectID last_id;
786
PhysicsDirectSpaceState3D::RayResult result;
787
#endif // PHYSICS_3D_DISABLED
788
789
#ifndef PHYSICS_2D_DISABLED
790
PhysicsDirectSpaceState2D *ss2d = PhysicsServer2D::get_singleton()->space_get_direct_state(find_world_2d()->get_space());
791
#endif // PHYSICS_2D_DISABLED
792
793
SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
794
bool parent_ignore_mouse = (parent_svc && parent_svc->get_mouse_filter_with_override() == Control::MOUSE_FILTER_IGNORE);
795
bool create_passive_hover_event = true;
796
if (gui.mouse_over.is_valid() || parent_ignore_mouse) {
797
// When the mouse is over a Control node, passive hovering would cause input events for Colliders, that are behind Control nodes.
798
// When parent SubViewportContainer ignores mouse, that setting should be respected.
799
create_passive_hover_event = false;
800
} else {
801
for (const Ref<InputEvent> &e : physics_picking_events) {
802
Ref<InputEventMouse> m = e;
803
if (m.is_valid()) {
804
// A mouse event exists, so passive hovering isn't necessary.
805
create_passive_hover_event = false;
806
break;
807
}
808
}
809
}
810
811
if (create_passive_hover_event) {
812
// Create a mouse motion event. This is necessary because objects or camera may have moved.
813
// While this extra event is sent, it is checked if both camera and last object and last ID did not move.
814
// If nothing changed, the event is discarded to avoid flooding with unnecessary motion events every frame.
815
Ref<InputEventMouseMotion> mm;
816
mm.instantiate();
817
818
mm->set_device(InputEvent::DEVICE_ID_INTERNAL);
819
mm->set_position(get_mouse_position());
820
mm->set_global_position(mm->get_position());
821
mm->set_alt_pressed(Input::get_singleton()->is_key_pressed(Key::ALT));
822
mm->set_shift_pressed(Input::get_singleton()->is_key_pressed(Key::SHIFT));
823
mm->set_ctrl_pressed(Input::get_singleton()->is_key_pressed(Key::CTRL));
824
mm->set_meta_pressed(Input::get_singleton()->is_key_pressed(Key::META));
825
mm->set_button_mask(Input::get_singleton()->get_mouse_button_mask());
826
physics_picking_events.push_back(mm);
827
}
828
829
while (physics_picking_events.size()) {
830
local_input_handled = false;
831
if (!handle_input_locally) {
832
Viewport *vp = this;
833
while (!Object::cast_to<Window>(vp) && vp->get_parent()) {
834
vp = vp->get_parent()->get_viewport();
835
}
836
vp->local_input_handled = false;
837
}
838
839
Ref<InputEvent> ev = physics_picking_events.front()->get();
840
physics_picking_events.pop_front();
841
842
Vector2 pos;
843
bool is_mouse = false;
844
845
Ref<InputEventMouseMotion> mm = ev;
846
847
if (mm.is_valid()) {
848
pos = mm->get_position();
849
is_mouse = true;
850
}
851
852
Ref<InputEventMouseButton> mb = ev;
853
854
if (mb.is_valid()) {
855
pos = mb->get_position();
856
is_mouse = true;
857
}
858
859
Ref<InputEventScreenDrag> sd = ev;
860
861
if (sd.is_valid()) {
862
pos = sd->get_position();
863
}
864
865
Ref<InputEventScreenTouch> st = ev;
866
867
if (st.is_valid()) {
868
pos = st->get_position();
869
}
870
871
#ifndef PHYSICS_2D_DISABLED
872
if (ss2d) {
873
// Send to 2D.
874
875
uint64_t frame = get_tree()->get_frame();
876
877
PhysicsDirectSpaceState2D::ShapeResult res[64];
878
for (const CanvasLayer *E : canvas_layers) {
879
Transform2D canvas_layer_transform;
880
ObjectID canvas_layer_id;
881
if (E) {
882
// A descendant CanvasLayer.
883
canvas_layer_transform = E->get_final_transform();
884
canvas_layer_id = E->get_instance_id();
885
} else {
886
// This Viewport's builtin canvas.
887
canvas_layer_transform = get_canvas_transform();
888
canvas_layer_id = ObjectID();
889
}
890
891
Vector2 point = canvas_layer_transform.affine_inverse().xform(pos);
892
893
PhysicsDirectSpaceState2D::PointParameters point_params;
894
point_params.position = point;
895
point_params.canvas_instance_id = canvas_layer_id;
896
point_params.collide_with_areas = true;
897
point_params.pick_point = true;
898
899
int rc = ss2d->intersect_point(point_params, res, 64);
900
if (physics_object_picking_sort) {
901
struct ComparatorCollisionObjects {
902
bool operator()(const PhysicsDirectSpaceState2D::ShapeResult &p_a, const PhysicsDirectSpaceState2D::ShapeResult &p_b) const {
903
CollisionObject2D *a = Object::cast_to<CollisionObject2D>(p_a.collider);
904
CollisionObject2D *b = Object::cast_to<CollisionObject2D>(p_b.collider);
905
if (!a || !b) {
906
return false;
907
}
908
int za = a->get_effective_z_index();
909
int zb = b->get_effective_z_index();
910
if (za != zb) {
911
return zb < za;
912
}
913
return a->is_greater_than(b);
914
}
915
};
916
SortArray<PhysicsDirectSpaceState2D::ShapeResult, ComparatorCollisionObjects> sorter;
917
sorter.sort(res, rc);
918
}
919
for (int i = 0; i < rc; i++) {
920
if (is_input_handled()) {
921
break;
922
}
923
if (res[i].collider_id.is_valid() && res[i].collider) {
924
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(res[i].collider);
925
if (co && co->can_process()) {
926
bool send_event = true;
927
if (is_mouse) {
928
HashMap<ObjectID, uint64_t>::Iterator F = physics_2d_mouseover.find(res[i].collider_id);
929
if (!F) {
930
physics_2d_mouseover.insert(res[i].collider_id, frame);
931
co->_mouse_enter();
932
} else {
933
F->value = frame;
934
// It was already hovered, so don't send the event if it's faked.
935
if (mm.is_valid() && mm->get_device() == InputEvent::DEVICE_ID_INTERNAL) {
936
send_event = false;
937
}
938
}
939
HashMap<Pair<ObjectID, int>, uint64_t>::Iterator SF = physics_2d_shape_mouseover.find(Pair(res[i].collider_id, res[i].shape));
940
if (!SF) {
941
physics_2d_shape_mouseover.insert(Pair(res[i].collider_id, res[i].shape), frame);
942
co->_mouse_shape_enter(res[i].shape);
943
} else {
944
SF->value = frame;
945
}
946
}
947
948
if (send_event) {
949
co->_input_event_call(this, ev, res[i].shape);
950
}
951
952
if (physics_object_picking_first_only) {
953
break;
954
}
955
}
956
}
957
}
958
}
959
960
if (is_mouse) {
961
_cleanup_mouseover_colliders(false, false, frame);
962
}
963
}
964
#endif // PHYSICS_2D_DISABLED
965
966
#ifndef PHYSICS_3D_DISABLED
967
if (physics_object_picking_first_only && is_input_handled()) {
968
continue;
969
}
970
971
CollisionObject3D *capture_object = nullptr;
972
if (physics_object_capture.is_valid()) {
973
capture_object = ObjectDB::get_instance<CollisionObject3D>(physics_object_capture);
974
if (!capture_object || !camera_3d || (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed())) {
975
physics_object_capture = ObjectID();
976
} else {
977
last_id = physics_object_capture;
978
last_object = capture_object;
979
}
980
}
981
982
if (pos == last_pos) {
983
if (last_id.is_valid()) {
984
if (ObjectDB::get_instance(last_id) && last_object) {
985
// Good, exists.
986
_collision_object_3d_input_event(last_object, camera_3d, ev, result.position, result.normal, result.shape);
987
if (last_object->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
988
physics_object_capture = last_id;
989
}
990
}
991
}
992
} else {
993
if (camera_3d) {
994
Vector3 from = camera_3d->project_ray_origin(pos);
995
Vector3 dir = camera_3d->project_ray_normal(pos);
996
real_t depth_far = camera_3d->get_far();
997
998
PhysicsDirectSpaceState3D *space = PhysicsServer3D::get_singleton()->space_get_direct_state(find_world_3d()->get_space());
999
if (space) {
1000
PhysicsDirectSpaceState3D::RayParameters ray_params;
1001
ray_params.from = from;
1002
ray_params.to = from + dir * depth_far;
1003
ray_params.collide_with_areas = true;
1004
ray_params.pick_ray = true;
1005
1006
bool col = space->intersect_ray(ray_params, result);
1007
ObjectID new_collider;
1008
CollisionObject3D *co = col ? Object::cast_to<CollisionObject3D>(result.collider) : nullptr;
1009
if (co && co->can_process()) {
1010
new_collider = result.collider_id;
1011
if (!capture_object) {
1012
last_object = co;
1013
last_id = result.collider_id;
1014
if (co->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
1015
physics_object_capture = last_id;
1016
}
1017
}
1018
}
1019
1020
if (is_mouse && new_collider != physics_object_over) {
1021
if (physics_object_over.is_valid()) {
1022
CollisionObject3D *previous_co = ObjectDB::get_instance<CollisionObject3D>(physics_object_over);
1023
if (previous_co) {
1024
previous_co->_mouse_exit();
1025
}
1026
}
1027
1028
if (new_collider.is_valid()) {
1029
DEV_ASSERT(co);
1030
co->_mouse_enter();
1031
}
1032
1033
physics_object_over = new_collider;
1034
}
1035
if (capture_object) {
1036
_collision_object_3d_input_event(capture_object, camera_3d, ev, result.position, result.normal, result.shape);
1037
} else if (new_collider.is_valid()) {
1038
_collision_object_3d_input_event(co, camera_3d, ev, result.position, result.normal, result.shape);
1039
}
1040
}
1041
1042
last_pos = pos;
1043
}
1044
}
1045
#endif // PHYSICS_3D_DISABLED
1046
}
1047
}
1048
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
1049
1050
RID Viewport::get_viewport_rid() const {
1051
ERR_READ_THREAD_GUARD_V(RID());
1052
return viewport;
1053
}
1054
1055
void Viewport::update_canvas_items() {
1056
ERR_MAIN_THREAD_GUARD;
1057
if (!is_inside_tree()) {
1058
return;
1059
}
1060
1061
if (is_embedding_subwindows()) {
1062
for (Viewport::SubWindow w : gui.sub_windows) {
1063
if (w.window && !w.pending_window_update) {
1064
w.pending_window_update = true;
1065
w.window->_update_viewport_size();
1066
callable_mp(this, &Viewport::_sub_window_update).call_deferred(w.window);
1067
}
1068
}
1069
}
1070
_update_canvas_items(this);
1071
}
1072
1073
void Viewport::set_use_oversampling(bool p_oversampling) {
1074
ERR_MAIN_THREAD_GUARD;
1075
if (use_font_oversampling == p_oversampling) {
1076
return;
1077
}
1078
use_font_oversampling = p_oversampling;
1079
_set_size(_get_size(), _get_size_2d_override(), _is_size_allocated());
1080
}
1081
1082
bool Viewport::is_using_oversampling() const {
1083
ERR_READ_THREAD_GUARD_V(false);
1084
return use_font_oversampling;
1085
}
1086
1087
void Viewport::set_oversampling_override(float p_oversampling) {
1088
ERR_MAIN_THREAD_GUARD;
1089
if (font_oversampling_override == p_oversampling) {
1090
return;
1091
}
1092
font_oversampling_override = p_oversampling;
1093
_set_size(_get_size(), _get_size_2d_override(), _is_size_allocated());
1094
}
1095
1096
float Viewport::get_oversampling_override() const {
1097
ERR_READ_THREAD_GUARD_V(0.0);
1098
return font_oversampling_override;
1099
}
1100
1101
bool Viewport::_set_size(const Size2i &p_size, const Size2 &p_size_2d_override, bool p_allocated) {
1102
Transform2D stretch_transform_new = Transform2D();
1103
float new_font_oversampling = 1.0;
1104
if (is_size_2d_override_stretch_enabled() && p_size_2d_override.width > 0 && p_size_2d_override.height > 0) {
1105
Size2 scale = Size2(p_size) / p_size_2d_override;
1106
stretch_transform_new.scale(scale);
1107
1108
if (use_font_oversampling) {
1109
if (font_oversampling_override <= 0.0) {
1110
new_font_oversampling = MAX(scale.x, scale.y);
1111
} else {
1112
new_font_oversampling = font_oversampling_override;
1113
}
1114
} else {
1115
new_font_oversampling = 1.0;
1116
}
1117
} else if (use_font_oversampling && font_oversampling_override > 0.0) {
1118
new_font_oversampling = font_oversampling_override;
1119
}
1120
1121
Size2i new_size = p_size.maxi(2);
1122
if (size == new_size && size_allocated == p_allocated && stretch_transform == stretch_transform_new && p_size_2d_override == size_2d_override && new_font_oversampling == font_oversampling) {
1123
return false;
1124
}
1125
1126
if (new_font_oversampling != font_oversampling) {
1127
TS->reference_oversampling_level(new_font_oversampling);
1128
TS->unreference_oversampling_level(font_oversampling);
1129
1130
DPITexture::reference_scaling_level(new_font_oversampling);
1131
DPITexture::unreference_scaling_level(font_oversampling);
1132
}
1133
1134
size = new_size;
1135
size_allocated = p_allocated;
1136
size_2d_override = p_size_2d_override;
1137
stretch_transform = stretch_transform_new;
1138
font_oversampling = new_font_oversampling;
1139
1140
#ifndef XR_DISABLED
1141
if (!use_xr) {
1142
#endif
1143
1144
if (p_allocated) {
1145
RS::get_singleton()->viewport_set_size(viewport, size.width, size.height);
1146
} else {
1147
RS::get_singleton()->viewport_set_size(viewport, 0, 0);
1148
}
1149
1150
#ifndef XR_DISABLED
1151
} // if (!use_xr)
1152
#endif
1153
1154
_update_global_transform();
1155
update_configuration_warnings();
1156
1157
update_canvas_items();
1158
1159
for (ViewportTexture *E : viewport_textures) {
1160
E->emit_changed();
1161
}
1162
1163
emit_signal(SNAME("size_changed"));
1164
1165
Rect2i limit = get_visible_rect();
1166
for (int i = 0; i < gui.sub_windows.size(); ++i) {
1167
Window *sw = gui.sub_windows[i].window;
1168
Rect2i rect = Rect2i(sw->position, sw->size);
1169
Rect2i new_rect = sw->fit_rect_in_parent(rect, limit);
1170
if (new_rect != rect) {
1171
sw->set_position(new_rect.position);
1172
sw->set_size(new_rect.size);
1173
}
1174
}
1175
return true;
1176
}
1177
1178
Size2i Viewport::_get_size() const {
1179
#ifndef XR_DISABLED
1180
if (use_xr) {
1181
if (XRServer::get_singleton() != nullptr) {
1182
Ref<XRInterface> xr_interface = XRServer::get_singleton()->get_primary_interface();
1183
if (xr_interface.is_valid() && xr_interface->is_initialized()) {
1184
Size2 xr_size = xr_interface->get_render_target_size();
1185
return (Size2i)xr_size;
1186
}
1187
}
1188
return Size2i();
1189
}
1190
#endif // XR_DISABLED
1191
1192
return size;
1193
}
1194
1195
Size2 Viewport::_get_size_2d_override() const {
1196
return size_2d_override;
1197
}
1198
1199
bool Viewport::_is_size_allocated() const {
1200
return size_allocated;
1201
}
1202
1203
Rect2 Viewport::get_visible_rect() const {
1204
ERR_READ_THREAD_GUARD_V(Rect2());
1205
Rect2 r;
1206
1207
if (size == Size2()) {
1208
r = Rect2(Point2(), DisplayServer::get_singleton()->window_get_size());
1209
} else {
1210
r = Rect2(Point2(), size);
1211
}
1212
1213
if (size_2d_override != Size2()) {
1214
r.size = size_2d_override;
1215
}
1216
1217
return r;
1218
}
1219
1220
void Viewport::canvas_parent_mark_dirty(Node *p_node) {
1221
ERR_MAIN_THREAD_GUARD;
1222
bool request_update = gui.canvas_parents_with_dirty_order.is_empty();
1223
gui.canvas_parents_with_dirty_order.insert(p_node->get_instance_id());
1224
if (request_update) {
1225
callable_mp(this, &Viewport::_process_dirty_canvas_parent_orders).call_deferred();
1226
}
1227
}
1228
1229
void Viewport::set_canvas_transform(const Transform2D &p_transform) {
1230
ERR_MAIN_THREAD_GUARD;
1231
canvas_transform = p_transform;
1232
1233
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform);
1234
}
1235
1236
Transform2D Viewport::get_canvas_transform() const {
1237
ERR_READ_THREAD_GUARD_V(Transform2D());
1238
return canvas_transform;
1239
}
1240
1241
void Viewport::_update_global_transform() {
1242
Transform2D sxform = stretch_transform * global_canvas_transform;
1243
1244
RenderingServer::get_singleton()->viewport_set_global_canvas_transform(viewport, sxform);
1245
}
1246
1247
void Viewport::set_global_canvas_transform(const Transform2D &p_transform) {
1248
ERR_MAIN_THREAD_GUARD;
1249
global_canvas_transform = p_transform;
1250
1251
_update_global_transform();
1252
}
1253
1254
Transform2D Viewport::get_global_canvas_transform() const {
1255
ERR_READ_THREAD_GUARD_V(Transform2D());
1256
return global_canvas_transform;
1257
}
1258
1259
void Viewport::_canvas_layer_add(CanvasLayer *p_canvas_layer) {
1260
canvas_layers.insert(p_canvas_layer);
1261
}
1262
1263
void Viewport::_canvas_layer_remove(CanvasLayer *p_canvas_layer) {
1264
canvas_layers.erase(p_canvas_layer);
1265
}
1266
1267
void Viewport::set_transparent_background(bool p_enable) {
1268
ERR_MAIN_THREAD_GUARD;
1269
transparent_bg = p_enable;
1270
RS::get_singleton()->viewport_set_transparent_background(viewport, p_enable);
1271
}
1272
1273
bool Viewport::has_transparent_background() const {
1274
ERR_READ_THREAD_GUARD_V(false);
1275
return transparent_bg;
1276
}
1277
1278
void Viewport::set_use_hdr_2d(bool p_enable) {
1279
ERR_MAIN_THREAD_GUARD;
1280
use_hdr_2d = p_enable;
1281
RS::get_singleton()->viewport_set_use_hdr_2d(viewport, p_enable);
1282
}
1283
1284
bool Viewport::is_using_hdr_2d() const {
1285
ERR_READ_THREAD_GUARD_V(false);
1286
return use_hdr_2d;
1287
}
1288
1289
void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) {
1290
ERR_MAIN_THREAD_GUARD;
1291
if (world_2d == p_world_2d) {
1292
return;
1293
}
1294
1295
if (is_inside_tree()) {
1296
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, current_canvas);
1297
}
1298
1299
if (world_2d.is_valid()) {
1300
world_2d->remove_viewport(this);
1301
}
1302
1303
if (p_world_2d.is_valid()) {
1304
bool do_propagate = world_2d.is_valid() && is_inside_tree();
1305
world_2d = p_world_2d;
1306
if (do_propagate) {
1307
_propagate_world_2d_changed(this);
1308
}
1309
} else {
1310
WARN_PRINT("Invalid world_2d");
1311
world_2d.instantiate();
1312
}
1313
1314
world_2d->register_viewport(this);
1315
_update_audio_listener_2d();
1316
1317
if (is_inside_tree()) {
1318
current_canvas = find_world_2d()->get_canvas();
1319
RenderingServer::get_singleton()->viewport_attach_canvas(viewport, current_canvas);
1320
}
1321
}
1322
1323
Ref<World2D> Viewport::find_world_2d() const {
1324
ERR_READ_THREAD_GUARD_V(Ref<World2D>());
1325
if (world_2d.is_valid()) {
1326
return world_2d;
1327
} else if (parent) {
1328
return parent->find_world_2d();
1329
} else {
1330
return Ref<World2D>();
1331
}
1332
}
1333
1334
void Viewport::_propagate_drag_notification(Node *p_node, int p_what) {
1335
// Send notification to p_node and all children and descendant nodes of p_node, except to SubViewports which are not children of a SubViewportContainer.
1336
p_node->notification(p_what);
1337
bool is_svc = Object::cast_to<SubViewportContainer>(p_node);
1338
for (int i = 0; i < p_node->get_child_count(); i++) {
1339
Node *c = p_node->get_child(i);
1340
if (!is_svc && Object::cast_to<SubViewport>(c)) {
1341
continue;
1342
}
1343
Viewport::_propagate_drag_notification(c, p_what);
1344
}
1345
}
1346
1347
Ref<World2D> Viewport::get_world_2d() const {
1348
ERR_READ_THREAD_GUARD_V(Ref<World2D>());
1349
return world_2d;
1350
}
1351
1352
Transform2D Viewport::get_stretch_transform() const {
1353
return stretch_transform;
1354
}
1355
1356
Transform2D Viewport::get_final_transform() const {
1357
ERR_READ_THREAD_GUARD_V(Transform2D());
1358
return stretch_transform * global_canvas_transform;
1359
}
1360
1361
void Viewport::_update_canvas_items(Node *p_node) {
1362
if (p_node != this) {
1363
Window *w = Object::cast_to<Window>(p_node);
1364
if (w && (!w->is_inside_tree() || !w->is_embedded())) {
1365
return;
1366
}
1367
1368
CanvasItem *ci = Object::cast_to<CanvasItem>(p_node);
1369
if (ci) {
1370
ci->queue_redraw();
1371
}
1372
}
1373
1374
int cc = p_node->get_child_count();
1375
1376
for (int i = 0; i < cc; i++) {
1377
_update_canvas_items(p_node->get_child(i));
1378
}
1379
}
1380
1381
Ref<ViewportTexture> Viewport::get_texture() const {
1382
ERR_READ_THREAD_GUARD_V(Ref<ViewportTexture>());
1383
return default_texture;
1384
}
1385
1386
void Viewport::set_positional_shadow_atlas_size(int p_size) {
1387
ERR_MAIN_THREAD_GUARD;
1388
positional_shadow_atlas_size = p_size;
1389
RS::get_singleton()->viewport_set_positional_shadow_atlas_size(viewport, p_size, positional_shadow_atlas_16_bits);
1390
}
1391
1392
int Viewport::get_positional_shadow_atlas_size() const {
1393
ERR_READ_THREAD_GUARD_V(0);
1394
return positional_shadow_atlas_size;
1395
}
1396
1397
void Viewport::set_positional_shadow_atlas_16_bits(bool p_16_bits) {
1398
ERR_MAIN_THREAD_GUARD;
1399
if (positional_shadow_atlas_16_bits == p_16_bits) {
1400
return;
1401
}
1402
1403
positional_shadow_atlas_16_bits = p_16_bits;
1404
RS::get_singleton()->viewport_set_positional_shadow_atlas_size(viewport, positional_shadow_atlas_size, positional_shadow_atlas_16_bits);
1405
}
1406
1407
bool Viewport::get_positional_shadow_atlas_16_bits() const {
1408
ERR_READ_THREAD_GUARD_V(false);
1409
return positional_shadow_atlas_16_bits;
1410
}
1411
void Viewport::set_positional_shadow_atlas_quadrant_subdiv(int p_quadrant, PositionalShadowAtlasQuadrantSubdiv p_subdiv) {
1412
ERR_MAIN_THREAD_GUARD;
1413
ERR_FAIL_INDEX(p_quadrant, 4);
1414
ERR_FAIL_INDEX(p_subdiv, SHADOW_ATLAS_QUADRANT_SUBDIV_MAX);
1415
1416
if (positional_shadow_atlas_quadrant_subdiv[p_quadrant] == p_subdiv) {
1417
return;
1418
}
1419
1420
positional_shadow_atlas_quadrant_subdiv[p_quadrant] = p_subdiv;
1421
static const int subdiv[SHADOW_ATLAS_QUADRANT_SUBDIV_MAX] = { 0, 1, 4, 16, 64, 256, 1024 };
1422
1423
RS::get_singleton()->viewport_set_positional_shadow_atlas_quadrant_subdivision(viewport, p_quadrant, subdiv[p_subdiv]);
1424
}
1425
1426
Viewport::PositionalShadowAtlasQuadrantSubdiv Viewport::get_positional_shadow_atlas_quadrant_subdiv(int p_quadrant) const {
1427
ERR_READ_THREAD_GUARD_V(SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED);
1428
ERR_FAIL_INDEX_V(p_quadrant, 4, SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED);
1429
return positional_shadow_atlas_quadrant_subdiv[p_quadrant];
1430
}
1431
1432
Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) {
1433
if (ev.is_null()) {
1434
return ev; // No transformation defined for null event
1435
}
1436
1437
Transform2D ai = get_final_transform().affine_inverse();
1438
Ref<InputEventMouse> me = ev;
1439
if (me.is_valid()) {
1440
me = me->xformed_by(ai);
1441
// For InputEventMouse, the global position is not adjusted by ev->xformed_by() and needs to be set separately.
1442
me->set_global_position(me->get_position());
1443
return me;
1444
}
1445
return ev->xformed_by(ai);
1446
}
1447
1448
Vector2 Viewport::get_mouse_position() const {
1449
ERR_READ_THREAD_GUARD_V(Vector2());
1450
if (get_section_root_viewport() != SceneTree::get_singleton()->get_root()) {
1451
// Rely on the most recent mouse coordinate from an InputEventMouse in push_input.
1452
// In this case get_screen_transform is not applicable, because it is ambiguous.
1453
return gui.last_mouse_pos;
1454
} else if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_MOUSE)) {
1455
Transform2D xform = get_screen_transform_internal(true);
1456
if (xform.determinant() == 0) {
1457
// Screen transform can be non-invertible when the Window is minimized.
1458
return Vector2();
1459
}
1460
return xform.affine_inverse().xform(DisplayServer::get_singleton()->mouse_get_position());
1461
} else {
1462
// Fallback to Input for getting mouse position in case of emulated mouse.
1463
return get_screen_transform_internal().affine_inverse().xform(Input::get_singleton()->get_mouse_position());
1464
}
1465
}
1466
1467
void Viewport::warp_mouse(const Vector2 &p_position) {
1468
ERR_MAIN_THREAD_GUARD;
1469
Transform2D xform = get_screen_transform_internal();
1470
Vector2 gpos = xform.xform(p_position);
1471
Input::get_singleton()->warp_mouse(gpos);
1472
}
1473
1474
Point2 Viewport::wrap_mouse_in_rect(const Vector2 &p_relative, const Rect2 &p_rect) {
1475
// Move the mouse cursor from its current position to a location bounded by `p_rect`
1476
// in accordance with a heuristic that takes the traveled distance `p_relative` of the mouse
1477
// into account.
1478
1479
// All parameters are in viewport coordinates.
1480
// p_relative denotes the distance to the previous mouse position.
1481
// p_rect denotes the area, in which the mouse should be confined in.
1482
1483
// The relative distance reported for the next event after a warp is in the boundaries of the
1484
// size of the rect on that axis, but it may be greater, in which case there's no problem as
1485
// fmod() will warp it, but if the pointer has moved in the opposite direction between the
1486
// pointer relocation and the subsequent event, the reported relative distance will be less
1487
// than the size of the rect and thus fmod() will be disabled for handling the situation.
1488
// And due to this mouse warping mechanism being stateless, we need to apply some heuristics
1489
// to detect the warp: if the relative distance is greater than the half of the size of the
1490
// relevant rect (checked per each axis), it will be considered as the consequence of a former
1491
// pointer warp.
1492
1493
const Point2 rel_sign(p_relative.x >= 0.0f ? 1 : -1, p_relative.y >= 0.0 ? 1 : -1);
1494
const Size2 warp_margin = p_rect.size * 0.5f;
1495
const Point2 rel_warped(
1496
Math::fmod(p_relative.x + rel_sign.x * warp_margin.x, p_rect.size.x) - rel_sign.x * warp_margin.x,
1497
Math::fmod(p_relative.y + rel_sign.y * warp_margin.y, p_rect.size.y) - rel_sign.y * warp_margin.y);
1498
1499
const Point2 pos_local = get_mouse_position() - p_rect.position;
1500
const Point2 pos_warped(Math::fposmod(pos_local.x, p_rect.size.x), Math::fposmod(pos_local.y, p_rect.size.y));
1501
if (pos_warped != pos_local) {
1502
warp_mouse(pos_warped + p_rect.position);
1503
}
1504
1505
return rel_warped;
1506
}
1507
1508
void Viewport::_gui_sort_roots() {
1509
if (!gui.roots_order_dirty) {
1510
return;
1511
}
1512
1513
gui.roots.sort_custom<Control::CComparator>();
1514
1515
gui.roots_order_dirty = false;
1516
}
1517
1518
void Viewport::_gui_cancel_tooltip() {
1519
gui.tooltip_control = nullptr;
1520
gui.tooltip_text = "";
1521
1522
if (gui.tooltip_timer.is_valid()) {
1523
gui.tooltip_timer->release_connections();
1524
gui.tooltip_timer = Ref<SceneTreeTimer>();
1525
}
1526
if (gui.tooltip_popup) {
1527
gui.tooltip_popup->queue_free();
1528
}
1529
}
1530
1531
String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_tooltip_owner) {
1532
Vector2 pos = p_pos;
1533
String tooltip;
1534
1535
while (p_control) {
1536
tooltip = p_control->get_tooltip(pos);
1537
1538
// Temporary solution for PopupMenus.
1539
PopupMenu *menu = Object::cast_to<PopupMenu>(this);
1540
if (menu) {
1541
Ref<StyleBox> sb = menu->get_theme_stylebox(SceneStringName(panel));
1542
if (sb.is_valid()) {
1543
pos.y += sb->get_margin(SIDE_TOP);
1544
}
1545
1546
tooltip = menu->get_tooltip(pos);
1547
}
1548
1549
if (r_tooltip_owner) {
1550
*r_tooltip_owner = p_control;
1551
}
1552
1553
// If we found a tooltip, we stop here.
1554
if (!tooltip.is_empty()) {
1555
break;
1556
}
1557
1558
// Otherwise, we check parent controls unless some conditions prevent it.
1559
1560
if (p_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
1561
break;
1562
}
1563
if (p_control->is_set_as_top_level()) {
1564
break;
1565
}
1566
1567
// Transform cursor pos for parent control.
1568
pos = p_control->get_transform().xform(pos);
1569
1570
p_control = p_control->get_parent_control();
1571
}
1572
1573
return tooltip;
1574
}
1575
1576
void Viewport::cancel_tooltip() {
1577
_gui_cancel_tooltip();
1578
}
1579
1580
void Viewport::show_tooltip(Control *p_control) {
1581
if (!p_control) {
1582
return;
1583
}
1584
1585
if (gui.tooltip_timer.is_valid()) {
1586
gui.tooltip_timer->release_connections();
1587
gui.tooltip_timer = Ref<SceneTreeTimer>();
1588
}
1589
gui.tooltip_control = p_control;
1590
_gui_show_tooltip_at(p_control->get_size() / 2);
1591
}
1592
1593
void Viewport::_gui_show_tooltip() {
1594
_gui_show_tooltip_at(gui.last_mouse_pos);
1595
}
1596
1597
void Viewport::_gui_show_tooltip_at(const Point2i &p_pos) {
1598
if (!gui.tooltip_control) {
1599
return;
1600
}
1601
1602
// Get the Control under cursor and the relevant tooltip text, if any.
1603
Control *tooltip_owner = nullptr;
1604
gui.tooltip_text = _gui_get_tooltip(
1605
gui.tooltip_control,
1606
gui.tooltip_control->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos),
1607
&tooltip_owner);
1608
gui.tooltip_text = gui.tooltip_text.strip_edges();
1609
1610
// Controls can implement `make_custom_tooltip` to provide their own tooltip.
1611
// This should be a Control node which will be added as child to a TooltipPanel.
1612
Control *base_tooltip = tooltip_owner->make_custom_tooltip(gui.tooltip_text);
1613
1614
// When the custom control is not visible, don't show any tooltip.
1615
// This way, the custom tooltip from `ConnectionsDockTree` can create
1616
// its own tooltip without conflicting with the default one, even an empty tooltip.
1617
if (base_tooltip && !base_tooltip->is_visible()) {
1618
memdelete(base_tooltip);
1619
return;
1620
}
1621
1622
if (gui.tooltip_text.is_empty() && !base_tooltip) {
1623
return; // Nothing to show.
1624
}
1625
1626
// Remove previous popup if we change something.
1627
if (gui.tooltip_popup) {
1628
memdelete(gui.tooltip_popup);
1629
gui.tooltip_popup = nullptr;
1630
}
1631
1632
if (!tooltip_owner) {
1633
return;
1634
}
1635
1636
// Popup window which houses the tooltip content.
1637
PopupPanel *panel = memnew(PopupPanel);
1638
panel->set_theme_type_variation(SNAME("TooltipPanel"));
1639
1640
// If no custom tooltip is given, use a default implementation.
1641
if (!base_tooltip) {
1642
gui.tooltip_label = memnew(Label);
1643
gui.tooltip_label->set_theme_type_variation(SNAME("TooltipLabel"));
1644
gui.tooltip_label->set_text(gui.tooltip_text);
1645
gui.tooltip_label->set_auto_translate_mode(tooltip_owner->get_tooltip_auto_translate_mode());
1646
base_tooltip = gui.tooltip_label;
1647
panel->connect(SceneStringName(mouse_entered), callable_mp(this, &Viewport::_gui_cancel_tooltip));
1648
}
1649
1650
base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
1651
1652
panel->set_flag(Window::FLAG_NO_FOCUS, true);
1653
panel->set_flag(Window::FLAG_POPUP, false);
1654
panel->set_flag(Window::FLAG_MOUSE_PASSTHROUGH, true);
1655
panel->set_wrap_controls(true);
1656
panel->set_default_canvas_item_texture_filter(get_default_canvas_item_texture_filter());
1657
panel->set_default_canvas_item_texture_repeat(get_default_canvas_item_texture_repeat());
1658
panel->add_child(base_tooltip);
1659
panel->gui_parent = this;
1660
1661
gui.tooltip_popup = panel;
1662
1663
tooltip_owner->add_child(gui.tooltip_popup);
1664
1665
Window *window = Object::cast_to<Window>(gui.tooltip_popup->get_embedder());
1666
if (!window) { // Not embedded.
1667
window = gui.tooltip_popup->get_parent_visible_window();
1668
}
1669
Size2 scale = get_popup_base_transform().get_scale();
1670
real_t popup_scale = MIN(scale.x, scale.y);
1671
Point2 tooltip_offset = GLOBAL_GET_CACHED(Point2, "display/mouse_cursor/tooltip_position_offset");
1672
tooltip_offset *= popup_scale;
1673
Rect2 r(gui.tooltip_pos + tooltip_offset, gui.tooltip_popup->get_contents_minimum_size());
1674
Rect2i vr;
1675
if (gui.tooltip_popup->is_embedded()) {
1676
vr = gui.tooltip_popup->get_embedder()->get_visible_rect();
1677
} else {
1678
vr = window->get_usable_parent_rect();
1679
}
1680
panel->content_scale_factor = popup_scale;
1681
r.size *= popup_scale;
1682
r.size = r.size.ceil();
1683
r.size = r.size.min(panel->get_max_size());
1684
1685
if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_SELF_FITTING_WINDOWS) || gui.tooltip_popup->is_embedded()) {
1686
if (r.size.x + r.position.x > vr.size.x + vr.position.x) {
1687
// Place it in the opposite direction. If it fails, just hug the border.
1688
r.position.x = gui.tooltip_pos.x - r.size.x - tooltip_offset.x;
1689
1690
if (r.position.x < vr.position.x) {
1691
r.position.x = vr.position.x + vr.size.x - r.size.x;
1692
}
1693
} else if (r.position.x < vr.position.x) {
1694
r.position.x = vr.position.x;
1695
}
1696
1697
if (r.size.y + r.position.y > vr.size.y + vr.position.y) {
1698
// Same as above.
1699
r.position.y = gui.tooltip_pos.y - r.size.y - tooltip_offset.y;
1700
1701
if (r.position.y < vr.position.y) {
1702
r.position.y = vr.position.y + vr.size.y - r.size.y;
1703
}
1704
} else if (r.position.y < vr.position.y) {
1705
r.position.y = vr.position.y;
1706
}
1707
}
1708
1709
DisplayServer::WindowID active_popup = DisplayServer::get_singleton()->window_get_active_popup();
1710
if (active_popup == DisplayServer::INVALID_WINDOW_ID || active_popup == window->get_window_id()) {
1711
gui.tooltip_popup->popup(r);
1712
}
1713
gui.tooltip_popup->child_controls_changed();
1714
}
1715
1716
void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_input) {
1717
Ref<InputEvent> ev = p_input;
1718
1719
// Returns true if an event should be impacted by a control's mouse filter.
1720
bool is_pointer_event = Ref<InputEventMouse>(p_input).is_valid() || Ref<InputEventScreenDrag>(p_input).is_valid() || Ref<InputEventScreenTouch>(p_input).is_valid();
1721
1722
Ref<InputEventMouseButton> mb = p_input;
1723
bool is_scroll_event = mb.is_valid() &&
1724
(mb->get_button_index() == MouseButton::WHEEL_DOWN ||
1725
mb->get_button_index() == MouseButton::WHEEL_UP ||
1726
mb->get_button_index() == MouseButton::WHEEL_LEFT ||
1727
mb->get_button_index() == MouseButton::WHEEL_RIGHT);
1728
1729
CanvasItem *ci = p_control;
1730
while (ci) {
1731
Control *control = Object::cast_to<Control>(ci);
1732
if (control) {
1733
if (control->get_mouse_filter_with_override() != Control::MOUSE_FILTER_IGNORE) {
1734
control->_call_gui_input(ev);
1735
}
1736
1737
if (!control->is_inside_tree() || control->is_set_as_top_level()) {
1738
break;
1739
}
1740
if (control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP && is_pointer_event && !(is_scroll_event && control->data.force_pass_scroll_events)) {
1741
// Mouse, ScreenDrag and ScreenTouch events are stopped by default with MOUSE_FILTER_STOP, unless we have a scroll event and force_pass_scroll_events set to true
1742
set_input_as_handled();
1743
break;
1744
}
1745
}
1746
1747
if (is_input_handled()) {
1748
// Break when the event is set to handled in a child Control node or after physics picking in SubViewport.
1749
break;
1750
}
1751
1752
if (ci->is_set_as_top_level()) {
1753
break;
1754
}
1755
1756
ev = ev->xformed_by(ci->get_transform()); // Transform event upwards.
1757
ci = ci->get_parent_item();
1758
}
1759
}
1760
1761
void Viewport::_gui_call_notification(Control *p_control, int p_what) {
1762
CanvasItem *ci = p_control;
1763
while (ci) {
1764
Control *control = Object::cast_to<Control>(ci);
1765
if (control) {
1766
if (control->get_mouse_filter_with_override() != Control::MOUSE_FILTER_IGNORE) {
1767
control->notification(p_what);
1768
}
1769
1770
if (!control->is_inside_tree()) {
1771
break;
1772
}
1773
1774
if (!control->is_inside_tree() || control->is_set_as_top_level()) {
1775
break;
1776
}
1777
if (control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
1778
break;
1779
}
1780
}
1781
1782
if (ci->is_set_as_top_level()) {
1783
break;
1784
}
1785
1786
ci = ci->get_parent_item();
1787
}
1788
}
1789
1790
// `gui_find_control` doesn't take embedded windows into account. So the caller of this function
1791
// needs to make sure, that there is no embedded window at the specified position.
1792
Control *Viewport::gui_find_control(const Point2 &p_global) {
1793
ERR_MAIN_THREAD_GUARD_V(nullptr);
1794
1795
_gui_sort_roots();
1796
1797
for (List<Control *>::Element *E = gui.roots.back(); E; E = E->prev()) {
1798
Control *sw = E->get();
1799
if (!sw->is_visible_in_tree()) {
1800
continue;
1801
}
1802
1803
Transform2D xform;
1804
CanvasItem *pci = sw->get_parent_item();
1805
if (pci) {
1806
xform = pci->get_global_transform_with_canvas();
1807
} else {
1808
xform = sw->get_canvas_transform();
1809
}
1810
1811
Control *ret = _gui_find_control_at_pos(sw, p_global, xform);
1812
if (ret) {
1813
return ret;
1814
}
1815
}
1816
1817
return nullptr;
1818
}
1819
1820
Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform) {
1821
if (!p_node->is_visible()) {
1822
return nullptr; // Canvas item hidden, discard.
1823
}
1824
1825
Transform2D matrix = p_xform * p_node->get_transform();
1826
// matrix.determinant() == 0.0f implies that node does not exist on scene
1827
if (matrix.determinant() == 0.0f) {
1828
return nullptr;
1829
}
1830
1831
Control *c = Object::cast_to<Control>(p_node);
1832
1833
if (!c || !c->is_clipping_contents() || c->has_point(matrix.affine_inverse().xform(p_global))) {
1834
for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
1835
CanvasItem *ci = Object::cast_to<CanvasItem>(p_node->get_child(i));
1836
if (!ci || ci->is_set_as_top_level()) {
1837
continue;
1838
}
1839
1840
Control *ret = _gui_find_control_at_pos(ci, p_global, matrix);
1841
if (ret) {
1842
return ret;
1843
}
1844
}
1845
}
1846
1847
if (!c || c->get_mouse_filter_with_override() == Control::MOUSE_FILTER_IGNORE) {
1848
return nullptr;
1849
}
1850
1851
matrix.affine_invert();
1852
if (!c->has_point(matrix.xform(p_global))) {
1853
return nullptr;
1854
}
1855
1856
Control *drag_preview = _gui_get_drag_preview();
1857
if (!drag_preview || (c != drag_preview && !drag_preview->is_ancestor_of(c))) {
1858
return c;
1859
}
1860
1861
return nullptr;
1862
}
1863
1864
bool Viewport::_gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check) {
1865
// Attempt drop, try parent controls too.
1866
CanvasItem *ci = p_at_control;
1867
Viewport *section_root = get_section_root_viewport();
1868
while (ci) {
1869
Control *control = Object::cast_to<Control>(ci);
1870
if (control) {
1871
if (control->can_drop_data(p_at_pos, section_root->gui.drag_data)) {
1872
if (!p_just_check) {
1873
control->drop_data(p_at_pos, section_root->gui.drag_data);
1874
}
1875
1876
return true;
1877
}
1878
1879
if (control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
1880
break;
1881
}
1882
}
1883
1884
p_at_pos = ci->get_transform().xform(p_at_pos);
1885
1886
if (ci->is_set_as_top_level()) {
1887
break;
1888
}
1889
1890
ci = ci->get_parent_item();
1891
}
1892
1893
return false;
1894
}
1895
1896
void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
1897
ERR_FAIL_COND(p_event.is_null());
1898
1899
Ref<InputEventMouseButton> mb = p_event;
1900
if (mb.is_valid()) {
1901
Point2 mpos = mb->get_position();
1902
if (mb->is_pressed()) {
1903
if (gui.dragging && mb->get_button_index() == MouseButton::RIGHT) {
1904
_perform_drop();
1905
set_input_as_handled();
1906
return;
1907
}
1908
MouseButtonMask button_mask = mouse_button_to_mask(mb->get_button_index());
1909
if (!gui.mouse_focus_mask.is_empty() && !gui.mouse_focus_mask.has_flag(button_mask)) {
1910
if (!gui.mouse_focus) {
1911
return;
1912
}
1913
1914
// Do not steal mouse focus and stuff while a focus mask without the current mouse button exists.
1915
gui.mouse_focus_mask.set_flag(button_mask);
1916
} else {
1917
gui.mouse_focus = gui_find_control(mpos);
1918
1919
if (!gui.mouse_focus) {
1920
// Focus should be hidden on click even if the focus holder didn't change.
1921
if (gui.key_focus && mb->get_button_index() == MouseButton::LEFT && _can_hide_focus_state()) {
1922
gui.hide_focus = true;
1923
gui.key_focus->queue_redraw();
1924
}
1925
1926
return;
1927
}
1928
1929
gui.mouse_focus_mask.set_flag(mouse_button_to_mask(mb->get_button_index()));
1930
1931
if (mb->get_button_index() == MouseButton::LEFT) {
1932
gui.drag_accum = Vector2();
1933
gui.drag_attempted = false;
1934
}
1935
}
1936
DEV_ASSERT(gui.mouse_focus);
1937
1938
mb = mb->xformed_by(Transform2D()); // Make a copy of the event.
1939
1940
Point2 pos = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(mpos);
1941
mb->set_position(pos);
1942
1943
#ifdef DEBUG_ENABLED
1944
if (EngineDebugger::get_singleton()) {
1945
Array arr = { gui.mouse_focus->get_path(), gui.mouse_focus->get_class() };
1946
EngineDebugger::get_singleton()->send_message("scene:click_ctrl", arr);
1947
}
1948
#endif
1949
1950
if (mb->get_button_index() == MouseButton::LEFT) { // Assign focus.
1951
CanvasItem *ci = gui.mouse_focus;
1952
while (ci) {
1953
Control *control = Object::cast_to<Control>(ci);
1954
if (control) {
1955
if (control->_is_focusable()) {
1956
// Grabbing unhovered focus can cause issues when mouse is dragged
1957
// with another button held down.
1958
if (gui.mouse_over_hierarchy.has(control->get_instance_id())) {
1959
// Hide the focus when it comes from a click.
1960
control->grab_focus(true);
1961
}
1962
break;
1963
}
1964
1965
if (control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
1966
break;
1967
}
1968
}
1969
1970
if (ci->is_set_as_top_level()) {
1971
break;
1972
}
1973
1974
ci = ci->get_parent_item();
1975
}
1976
}
1977
1978
if (gui.mouse_focus && gui.mouse_focus->can_process()) {
1979
_gui_call_input(gui.mouse_focus, mb);
1980
}
1981
1982
if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) {
1983
// Alternate drop use (when using force_drag(), as proposed by #5342).
1984
_perform_drop(gui.mouse_focus);
1985
}
1986
1987
_gui_cancel_tooltip();
1988
} else {
1989
if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) {
1990
_perform_drop(gui.drag_mouse_over);
1991
}
1992
1993
gui.mouse_focus_mask.clear_flag(mouse_button_to_mask(mb->get_button_index())); // Remove from mask.
1994
1995
if (!gui.mouse_focus) {
1996
// Release event is only sent if a mouse focus (previously pressed button) exists.
1997
return;
1998
}
1999
2000
mb = mb->xformed_by(Transform2D()); // Make a copy.
2001
Point2 pos = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(mpos);
2002
mb->set_position(pos);
2003
2004
Control *mouse_focus = gui.mouse_focus;
2005
2006
// Disable mouse focus if needed before calling input,
2007
// this makes popups on mouse press event work better,
2008
// as the release will never be received otherwise.
2009
if (gui.mouse_focus_mask.is_empty()) {
2010
gui.mouse_focus = nullptr;
2011
}
2012
2013
if (mouse_focus && mouse_focus->can_process()) {
2014
_gui_call_input(mouse_focus, mb);
2015
}
2016
}
2017
}
2018
2019
Ref<InputEventMouseMotion> mm = p_event;
2020
if (mm.is_valid()) {
2021
Point2 mpos = mm->get_position();
2022
2023
// Drag & drop.
2024
Viewport *section_root = get_section_root_viewport();
2025
if (!gui.drag_attempted && gui.mouse_focus && section_root && !section_root->gui.global_dragging && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
2026
gui.drag_accum += mm->get_relative();
2027
float len = gui.drag_accum.length();
2028
if (len > gui.drag_threshold) {
2029
{ // Attempt grab, try parent controls too.
2030
CanvasItem *ci = gui.mouse_focus;
2031
while (ci) {
2032
Control *control = Object::cast_to<Control>(ci);
2033
if (control) {
2034
section_root->gui.global_dragging = true;
2035
section_root->gui.drag_data = control->get_drag_data(control->get_global_transform_with_canvas().affine_inverse().xform(mpos - gui.drag_accum));
2036
if (section_root->gui.drag_data.get_type() != Variant::NIL) {
2037
gui.mouse_focus = nullptr;
2038
gui.mouse_focus_mask.clear();
2039
gui.dragging = true;
2040
break;
2041
} else {
2042
Control *drag_preview = _gui_get_drag_preview();
2043
if (drag_preview) {
2044
ERR_PRINT("Don't set a drag preview and return null data. Preview was deleted and drag request ignored.");
2045
memdelete(drag_preview);
2046
gui.drag_preview_id = ObjectID();
2047
}
2048
section_root->gui.global_dragging = false;
2049
}
2050
2051
if (control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
2052
break;
2053
}
2054
}
2055
2056
if (ci->is_set_as_top_level()) {
2057
break;
2058
}
2059
2060
ci = ci->get_parent_item();
2061
}
2062
}
2063
2064
gui.drag_attempted = true;
2065
if (gui.dragging) {
2066
Viewport::_propagate_drag_notification(section_root, NOTIFICATION_DRAG_BEGIN);
2067
}
2068
}
2069
}
2070
2071
Control *over = nullptr;
2072
if (gui.mouse_focus) {
2073
over = gui.mouse_focus;
2074
} else if (gui.mouse_in_viewport) {
2075
over = gui_find_control(mpos);
2076
}
2077
2078
DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)Input::get_singleton()->get_default_cursor_shape();
2079
2080
if (over) {
2081
Transform2D localizer = over->get_global_transform_with_canvas().affine_inverse();
2082
Size2 pos = localizer.xform(mpos);
2083
Vector2 velocity = localizer.basis_xform(mm->get_velocity());
2084
Vector2 rel = localizer.basis_xform(mm->get_relative());
2085
2086
mm = mm->xformed_by(Transform2D()); // Make a copy.
2087
2088
mm->set_global_position(mpos);
2089
mm->set_velocity(velocity);
2090
mm->set_relative(rel);
2091
2092
// Nothing pressed.
2093
if (mm->get_button_mask().is_empty()) {
2094
bool is_tooltip_shown = false;
2095
2096
if (gui.tooltip_popup) {
2097
if (gui.tooltip_control) {
2098
String tooltip = _gui_get_tooltip(over, gui.tooltip_control->get_global_transform_with_canvas().affine_inverse().xform(mpos));
2099
tooltip = tooltip.strip_edges();
2100
2101
if (tooltip != gui.tooltip_text) {
2102
_gui_cancel_tooltip();
2103
} else {
2104
is_tooltip_shown = true;
2105
}
2106
} else {
2107
_gui_cancel_tooltip();
2108
}
2109
}
2110
2111
// Reset the timer if the mouse has moved more than 5 pixels or has entered a new control.
2112
if (!is_tooltip_shown && over->can_process()) {
2113
Vector2 new_tooltip_pos = over->get_screen_transform().xform(pos);
2114
if (over != gui.tooltip_control || gui.tooltip_pos.distance_squared_to(new_tooltip_pos) > 25) {
2115
if (gui.tooltip_timer.is_valid()) {
2116
gui.tooltip_timer->release_connections();
2117
}
2118
gui.tooltip_control = over;
2119
gui.tooltip_pos = new_tooltip_pos;
2120
gui.tooltip_timer = get_tree()->create_timer(gui.tooltip_delay);
2121
gui.tooltip_timer->set_ignore_time_scale(true);
2122
gui.tooltip_timer->connect("timeout", callable_mp(this, &Viewport::_gui_show_tooltip));
2123
}
2124
}
2125
}
2126
2127
mm->set_position(pos);
2128
2129
Control::CursorShape cursor_shape = Control::CURSOR_ARROW;
2130
{
2131
Control *c = over;
2132
Vector2 cpos = pos;
2133
while (c) {
2134
if (!gui.mouse_focus_mask.is_empty() || c->has_point(cpos)) {
2135
cursor_shape = c->get_cursor_shape(cpos);
2136
} else {
2137
cursor_shape = Control::CURSOR_ARROW;
2138
}
2139
cpos = c->get_transform().xform(cpos);
2140
if (cursor_shape != Control::CURSOR_ARROW) {
2141
break;
2142
}
2143
if (c->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
2144
break;
2145
}
2146
if (c->is_set_as_top_level()) {
2147
break;
2148
}
2149
c = c->get_parent_control();
2150
}
2151
}
2152
2153
ds_cursor_shape = (DisplayServer::CursorShape)cursor_shape;
2154
2155
if (over->can_process()) {
2156
_gui_call_input(over, mm);
2157
}
2158
}
2159
2160
if (gui.dragging) {
2161
// Handle drag & drop. This happens in the viewport where dragging started.
2162
2163
Control *drag_preview = _gui_get_drag_preview();
2164
if (drag_preview) {
2165
Vector2 pos = drag_preview->get_canvas_transform().affine_inverse().xform(mpos);
2166
drag_preview->set_position(pos);
2167
}
2168
2169
gui.drag_mouse_over = section_root->gui.target_control;
2170
if (gui.drag_mouse_over) {
2171
if (!_gui_drop(gui.drag_mouse_over, gui.drag_mouse_over->get_local_mouse_position(), true)) {
2172
gui.drag_mouse_over = nullptr;
2173
}
2174
if (gui.drag_mouse_over) {
2175
ds_cursor_shape = DisplayServer::CURSOR_CAN_DROP;
2176
} else {
2177
ds_cursor_shape = DisplayServer::CURSOR_FORBIDDEN;
2178
}
2179
}
2180
}
2181
2182
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE) && (gui.dragging || (!section_root->gui.global_dragging && !Object::cast_to<SubViewportContainer>(over)))) {
2183
// If dragging is active, then set the cursor shape only from the Viewport where dragging started.
2184
// If dragging is inactive, then set the cursor shape only when not over a SubViewportContainer.
2185
DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape);
2186
}
2187
}
2188
2189
Ref<InputEventScreenTouch> touch_event = p_event;
2190
if (touch_event.is_valid()) {
2191
Size2 pos = touch_event->get_position();
2192
const int touch_index = touch_event->get_index();
2193
if (touch_event->is_pressed()) {
2194
Control *over = gui_find_control(pos);
2195
if (over) {
2196
gui.touch_focus[touch_index] = over->get_instance_id();
2197
if (over->can_process()) {
2198
touch_event = touch_event->xformed_by(Transform2D()); // Make a copy.
2199
pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
2200
touch_event->set_position(pos);
2201
_gui_call_input(over, touch_event);
2202
}
2203
return;
2204
}
2205
} else {
2206
ObjectID control_id = gui.touch_focus[touch_index];
2207
Control *over = control_id.is_valid() ? ObjectDB::get_instance<Control>(control_id) : nullptr;
2208
if (over && over->can_process()) {
2209
touch_event = touch_event->xformed_by(Transform2D()); // Make a copy.
2210
pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
2211
touch_event->set_position(pos);
2212
2213
_gui_call_input(over, touch_event);
2214
}
2215
gui.touch_focus.erase(touch_index);
2216
return;
2217
}
2218
}
2219
2220
Ref<InputEventGesture> gesture_event = p_event;
2221
if (gesture_event.is_valid()) {
2222
_gui_cancel_tooltip();
2223
2224
Size2 pos = gesture_event->get_position();
2225
2226
Control *over = gui_find_control(pos);
2227
if (over) {
2228
if (over->can_process()) {
2229
gesture_event = gesture_event->xformed_by(Transform2D()); // Make a copy.
2230
pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
2231
gesture_event->set_position(pos);
2232
_gui_call_input(over, gesture_event);
2233
}
2234
return;
2235
}
2236
}
2237
2238
Ref<InputEventScreenDrag> drag_event = p_event;
2239
if (drag_event.is_valid()) {
2240
const int drag_event_index = drag_event->get_index();
2241
ObjectID control_id = gui.touch_focus[drag_event_index];
2242
Control *over = control_id.is_valid() ? ObjectDB::get_instance<Control>(control_id) : nullptr;
2243
if (!over) {
2244
over = gui_find_control(drag_event->get_position());
2245
}
2246
if (over) {
2247
if (over->can_process()) {
2248
Transform2D localizer = over->get_global_transform_with_canvas().affine_inverse();
2249
Size2 pos = localizer.xform(drag_event->get_position());
2250
Vector2 velocity = localizer.basis_xform(drag_event->get_velocity());
2251
Vector2 rel = localizer.basis_xform(drag_event->get_relative());
2252
2253
drag_event = drag_event->xformed_by(Transform2D()); // Make a copy.
2254
2255
drag_event->set_velocity(velocity);
2256
drag_event->set_relative(rel);
2257
drag_event->set_position(pos);
2258
2259
_gui_call_input(over, drag_event);
2260
}
2261
2262
return;
2263
}
2264
}
2265
2266
if (mm.is_null() && mb.is_null() && p_event->is_action_type()) {
2267
if (gui.dragging && p_event->is_action_pressed(SNAME("ui_cancel")) && Input::get_singleton()->is_action_just_pressed(SNAME("ui_cancel"))) {
2268
_perform_drop();
2269
set_input_as_handled();
2270
return;
2271
}
2272
2273
if (p_event->is_action_pressed(SNAME("ui_cancel"))) {
2274
// Cancel tooltip timer or hide tooltip when pressing Escape (this is standard behavior in most applications).
2275
_gui_cancel_tooltip();
2276
if (gui.tooltip_popup) {
2277
// If a tooltip was hidden, prevent other actions associated with `ui_cancel` from occurring.
2278
// For instance, this prevents the node from being deselected when pressing Escape
2279
// to hide a documentation tooltip in the inspector.
2280
set_input_as_handled();
2281
return;
2282
}
2283
}
2284
2285
if (gui.key_focus && !gui.key_focus->is_visible_in_tree()) {
2286
gui.key_focus->release_focus();
2287
}
2288
2289
if (gui.key_focus) {
2290
if (gui.key_focus->can_process()) {
2291
gui.key_focus->_call_gui_input(p_event);
2292
}
2293
2294
if (is_input_handled()) {
2295
return;
2296
}
2297
}
2298
2299
Control *from = gui.key_focus ? gui.key_focus : nullptr;
2300
if (!from) {
2301
for (int i = 0; i < get_child_count(true); i++) {
2302
Control *c = Object::cast_to<Control>(get_child(i, true));
2303
if (!c || !c->is_visible_in_tree() || c->is_set_as_top_level()) {
2304
continue;
2305
}
2306
2307
from = c;
2308
break;
2309
}
2310
}
2311
2312
if (from && p_event->is_pressed()) {
2313
Control *next = nullptr;
2314
bool show_focus = false;
2315
2316
Ref<InputEventJoypadMotion> joypadmotion_event = p_event;
2317
if (joypadmotion_event.is_valid()) {
2318
Input *input = Input::get_singleton();
2319
2320
if (p_event->is_action_pressed(SNAME("ui_focus_next")) && input->is_action_just_pressed_by_event(SNAME("ui_focus_next"), p_event)) {
2321
next = from->find_next_valid_focus();
2322
show_focus = true;
2323
}
2324
2325
if (p_event->is_action_pressed(SNAME("ui_focus_prev")) && input->is_action_just_pressed_by_event(SNAME("ui_focus_prev"), p_event)) {
2326
next = from->find_prev_valid_focus();
2327
show_focus = true;
2328
}
2329
2330
if (p_event->is_action_pressed(SNAME("ui_accessibility_drag_and_drop")) && input->is_action_just_pressed_by_event(SNAME("ui_accessibility_drag_and_drop"), p_event)) {
2331
if (gui_is_dragging()) {
2332
from->accessibility_drop();
2333
} else {
2334
from->accessibility_drag();
2335
}
2336
}
2337
2338
if (p_event->is_action_pressed(SNAME("ui_up")) && input->is_action_just_pressed_by_event(SNAME("ui_up"), p_event)) {
2339
next = from->_get_focus_neighbor(SIDE_TOP);
2340
show_focus = true;
2341
}
2342
2343
if (p_event->is_action_pressed(SNAME("ui_left")) && input->is_action_just_pressed_by_event(SNAME("ui_left"), p_event)) {
2344
next = from->_get_focus_neighbor(SIDE_LEFT);
2345
show_focus = true;
2346
}
2347
2348
if (p_event->is_action_pressed(SNAME("ui_right")) && input->is_action_just_pressed_by_event(SNAME("ui_right"), p_event)) {
2349
next = from->_get_focus_neighbor(SIDE_RIGHT);
2350
show_focus = true;
2351
}
2352
2353
if (p_event->is_action_pressed(SNAME("ui_down")) && input->is_action_just_pressed_by_event(SNAME("ui_down"), p_event)) {
2354
next = from->_get_focus_neighbor(SIDE_BOTTOM);
2355
show_focus = true;
2356
}
2357
} else {
2358
if (p_event->is_action_pressed(SNAME("ui_focus_next"), true, true)) {
2359
next = from->find_next_valid_focus();
2360
show_focus = true;
2361
}
2362
2363
if (p_event->is_action_pressed(SNAME("ui_focus_prev"), true, true)) {
2364
next = from->find_prev_valid_focus();
2365
show_focus = true;
2366
}
2367
2368
if (p_event->is_action_pressed(SNAME("ui_accessibility_drag_and_drop"), true, true)) {
2369
if (gui_is_dragging()) {
2370
from->accessibility_drop();
2371
} else {
2372
from->accessibility_drag();
2373
}
2374
}
2375
2376
if (p_event->is_action_pressed(SNAME("ui_up"), true, true)) {
2377
next = from->_get_focus_neighbor(SIDE_TOP);
2378
show_focus = true;
2379
}
2380
2381
if (p_event->is_action_pressed(SNAME("ui_left"), true, true)) {
2382
next = from->_get_focus_neighbor(SIDE_LEFT);
2383
show_focus = true;
2384
}
2385
2386
if (p_event->is_action_pressed(SNAME("ui_right"), true, true)) {
2387
next = from->_get_focus_neighbor(SIDE_RIGHT);
2388
show_focus = true;
2389
}
2390
2391
if (p_event->is_action_pressed(SNAME("ui_down"), true, true)) {
2392
next = from->_get_focus_neighbor(SIDE_BOTTOM);
2393
show_focus = true;
2394
}
2395
}
2396
2397
if (next) {
2398
next->grab_focus();
2399
set_input_as_handled();
2400
} else if (show_focus && gui.hide_focus && gui.key_focus) {
2401
// Show focus even it the holder didn't change, as visual feedback.
2402
gui.hide_focus = false;
2403
gui.key_focus->queue_redraw();
2404
}
2405
}
2406
}
2407
}
2408
2409
void Viewport::_perform_drop(Control *p_control) {
2410
gui_perform_drop_at(p_control ? p_control->get_local_mouse_position() : Vector2(), p_control);
2411
}
2412
2413
void Viewport::gui_perform_drop_at(const Point2 &p_pos, Control *p_control) {
2414
// Without any arguments, simply cancel Drag and Drop.
2415
if (p_control) {
2416
gui.drag_successful = _gui_drop(p_control, p_pos, false);
2417
} else {
2418
gui.drag_successful = false;
2419
}
2420
2421
Control *drag_preview = _gui_get_drag_preview();
2422
if (drag_preview) {
2423
memdelete(drag_preview);
2424
gui.drag_preview_id = ObjectID();
2425
}
2426
2427
Viewport *section_root = get_section_root_viewport();
2428
section_root->gui.drag_data = Variant();
2429
gui.dragging = false;
2430
gui.drag_description = String();
2431
section_root->gui.global_dragging = false;
2432
gui.drag_mouse_over = nullptr;
2433
Viewport::_propagate_drag_notification(section_root, NOTIFICATION_DRAG_END);
2434
// Display the new cursor shape instantly.
2435
update_mouse_cursor_state();
2436
}
2437
2438
void Viewport::_gui_cleanup_internal_state(Ref<InputEvent> p_event) {
2439
ERR_FAIL_COND(p_event.is_null());
2440
2441
Ref<InputEventMouseButton> mb = p_event;
2442
if (mb.is_valid()) {
2443
if (!mb->is_pressed()) {
2444
gui.mouse_focus_mask.clear_flag(mouse_button_to_mask(mb->get_button_index())); // Remove from mask.
2445
}
2446
}
2447
}
2448
2449
List<Control *>::Element *Viewport::_gui_add_root_control(Control *p_control) {
2450
gui.roots_order_dirty = true;
2451
return gui.roots.push_back(p_control);
2452
}
2453
2454
void Viewport::gui_set_root_order_dirty() {
2455
ERR_MAIN_THREAD_GUARD;
2456
gui.roots_order_dirty = true;
2457
}
2458
2459
void Viewport::_gui_force_drag_start() {
2460
Viewport *section_root = get_section_root_viewport();
2461
section_root->gui.global_dragging = true;
2462
}
2463
2464
void Viewport::_gui_force_drag_cancel() {
2465
Viewport *section_root = get_section_root_viewport();
2466
section_root->gui.global_dragging = false;
2467
}
2468
2469
void Viewport::_gui_force_drag(Control *p_base, const Variant &p_data, Control *p_control) {
2470
ERR_FAIL_COND_MSG(p_data.get_type() == Variant::NIL, "Drag data must be a value.");
2471
2472
Viewport *section_root = get_section_root_viewport();
2473
ERR_FAIL_COND(!section_root->gui.global_dragging);
2474
2475
gui.dragging = true;
2476
section_root->gui.drag_data = p_data;
2477
gui.mouse_focus = nullptr;
2478
gui.mouse_focus_mask.clear();
2479
2480
if (p_control) {
2481
_gui_set_drag_preview(p_base, p_control);
2482
}
2483
Viewport::_propagate_drag_notification(section_root, NOTIFICATION_DRAG_BEGIN);
2484
}
2485
2486
void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) {
2487
ERR_FAIL_NULL(p_control);
2488
ERR_FAIL_COND(p_control->is_inside_tree());
2489
ERR_FAIL_COND(p_control->get_parent() != nullptr);
2490
2491
Control *drag_preview = _gui_get_drag_preview();
2492
if (drag_preview) {
2493
memdelete(drag_preview);
2494
}
2495
p_control->set_as_top_level(true);
2496
p_control->set_position(gui.last_mouse_pos);
2497
p_base->get_root_parent_control()->add_child(p_control); // Add as child of viewport.
2498
p_control->move_to_front();
2499
2500
gui.drag_preview_id = p_control->get_instance_id();
2501
}
2502
2503
Control *Viewport::_gui_get_drag_preview() {
2504
if (gui.drag_preview_id.is_null()) {
2505
return nullptr;
2506
} else {
2507
Control *drag_preview = ObjectDB::get_instance<Control>(gui.drag_preview_id);
2508
if (!drag_preview) {
2509
ERR_PRINT("Don't free the control set as drag preview.");
2510
gui.drag_preview_id = ObjectID();
2511
}
2512
return drag_preview;
2513
}
2514
}
2515
2516
void Viewport::_gui_remove_root_control(List<Control *>::Element *RI) {
2517
gui.roots.erase(RI);
2518
}
2519
2520
void Viewport::_gui_unfocus_control(Control *p_control) {
2521
if (gui.key_focus == p_control) {
2522
gui.key_focus->release_focus();
2523
}
2524
}
2525
2526
void Viewport::_gui_hide_control(Control *p_control) {
2527
if (gui.mouse_focus == p_control) {
2528
_drop_mouse_focus();
2529
}
2530
2531
if (gui.key_focus == p_control) {
2532
gui_release_focus();
2533
}
2534
ObjectID over_id = p_control ? p_control->get_instance_id() : ObjectID();
2535
if (gui.mouse_over == over_id || gui.mouse_over_hierarchy.has(over_id)) {
2536
_drop_mouse_over(p_control->get_parent_control());
2537
}
2538
if (gui.drag_mouse_over == p_control) {
2539
gui.drag_mouse_over = nullptr;
2540
}
2541
if (gui.tooltip_control == p_control) {
2542
_gui_cancel_tooltip();
2543
}
2544
}
2545
2546
void Viewport::_gui_remove_control(Control *p_control) {
2547
if (gui.mouse_focus == p_control) {
2548
gui.mouse_focus = nullptr;
2549
gui.mouse_focus_mask.clear();
2550
}
2551
if (gui.key_focus == p_control) {
2552
gui.key_focus = nullptr;
2553
}
2554
ObjectID over_id = p_control ? p_control->get_instance_id() : ObjectID();
2555
if (gui.mouse_over == over_id || gui.mouse_over_hierarchy.has(over_id)) {
2556
_drop_mouse_over(p_control->get_parent_control());
2557
}
2558
if (gui.drag_mouse_over == p_control) {
2559
gui.drag_mouse_over = nullptr;
2560
}
2561
if (gui.tooltip_control == p_control) {
2562
gui.tooltip_control = nullptr;
2563
}
2564
}
2565
2566
void Viewport::canvas_item_top_level_changed() {
2567
_gui_update_mouse_over();
2568
}
2569
2570
void Viewport::_gui_update_mouse_over() {
2571
if (gui.mouse_over.is_null() || gui.mouse_over_hierarchy.is_empty()) {
2572
return;
2573
}
2574
2575
if (gui.sending_mouse_enter_exit_notifications) {
2576
// If notifications are already being sent, delay call to next frame.
2577
if (get_tree() && !get_tree()->is_connected(SNAME("process_frame"), callable_mp(this, &Viewport::_gui_update_mouse_over))) {
2578
get_tree()->connect(SNAME("process_frame"), callable_mp(this, &Viewport::_gui_update_mouse_over), CONNECT_ONE_SHOT);
2579
}
2580
return;
2581
}
2582
2583
// Rebuild the mouse over hierarchy.
2584
LocalVector<ObjectID> new_mouse_over_hierarchy;
2585
LocalVector<ObjectID> needs_enter;
2586
LocalVector<int> needs_exit;
2587
2588
CanvasItem *over = ObjectDB::get_instance<CanvasItem>(gui.mouse_over);
2589
CanvasItem *ancestor = over;
2590
bool removing = false;
2591
bool reached_top = false;
2592
while (ancestor) {
2593
Control *ancestor_control = Object::cast_to<Control>(ancestor);
2594
if (ancestor_control) {
2595
ObjectID ancestor_control_id = ancestor_control->get_instance_id();
2596
int found = gui.mouse_over_hierarchy.find(ancestor_control_id);
2597
if (found >= 0) {
2598
// Remove the node if the propagation chain has been broken or it is now MOUSE_FILTER_IGNORE.
2599
if (removing || ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_IGNORE) {
2600
needs_exit.push_back(found);
2601
}
2602
}
2603
if (found == 0) {
2604
if (removing) {
2605
// Stop if the chain has been broken and the top of the hierarchy has been reached.
2606
break;
2607
}
2608
reached_top = true;
2609
}
2610
if (!removing && ancestor_control->get_mouse_filter_with_override() != Control::MOUSE_FILTER_IGNORE) {
2611
new_mouse_over_hierarchy.push_back(ancestor_control_id);
2612
// Add the node if it was not found and it is now not MOUSE_FILTER_IGNORE.
2613
if (found < 0) {
2614
needs_enter.push_back(ancestor_control_id);
2615
}
2616
}
2617
if (ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
2618
// MOUSE_FILTER_STOP breaks the propagation chain.
2619
if (reached_top) {
2620
break;
2621
}
2622
removing = true;
2623
}
2624
}
2625
if (ancestor->is_set_as_top_level()) {
2626
// Top level breaks the propagation chain.
2627
if (reached_top) {
2628
break;
2629
} else {
2630
removing = true;
2631
ancestor = Object::cast_to<CanvasItem>(ancestor->get_parent());
2632
continue;
2633
}
2634
}
2635
ancestor = ancestor->get_parent_item();
2636
}
2637
if (needs_exit.is_empty() && needs_enter.is_empty()) {
2638
return;
2639
}
2640
2641
gui.sending_mouse_enter_exit_notifications = true;
2642
2643
// Send Mouse Exit Self notification.
2644
if (over && !needs_exit.is_empty() && needs_exit[0] == (int)gui.mouse_over_hierarchy.size() - 1) {
2645
over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF);
2646
gui.mouse_over = ObjectID();
2647
}
2648
2649
// Send Mouse Exit notifications.
2650
for (int exit_control_index : needs_exit) {
2651
Control *ctrl = ObjectDB::get_instance<Control>(gui.mouse_over_hierarchy[exit_control_index]);
2652
if (ctrl) {
2653
ctrl->notification(Control::NOTIFICATION_MOUSE_EXIT);
2654
ctrl->emit_signal(SceneStringName(mouse_exited));
2655
}
2656
}
2657
2658
// Update the mouse over hierarchy.
2659
gui.mouse_over_hierarchy.resize(new_mouse_over_hierarchy.size());
2660
for (int i = 0; i < (int)new_mouse_over_hierarchy.size(); i++) {
2661
gui.mouse_over_hierarchy[i] = new_mouse_over_hierarchy[new_mouse_over_hierarchy.size() - 1 - i];
2662
}
2663
2664
// Send Mouse Enter notifications.
2665
for (int i = needs_enter.size() - 1; i >= 0; i--) {
2666
Control *ctrl = ObjectDB::get_instance<Control>(needs_enter[i]);
2667
if (ctrl) {
2668
ctrl->notification(Control::NOTIFICATION_MOUSE_ENTER);
2669
ctrl->emit_signal(SceneStringName(mouse_entered));
2670
}
2671
}
2672
2673
gui.sending_mouse_enter_exit_notifications = false;
2674
}
2675
2676
Window *Viewport::get_base_window() {
2677
ERR_READ_THREAD_GUARD_V(nullptr);
2678
ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
2679
2680
Viewport *v = this;
2681
Window *w = Object::cast_to<Window>(v);
2682
while (!w) {
2683
v = v->get_parent_viewport();
2684
w = Object::cast_to<Window>(v);
2685
}
2686
2687
return w;
2688
}
2689
2690
void Viewport::_gui_remove_focus_for_window(Node *p_window) {
2691
if (get_base_window() == p_window) {
2692
gui_release_focus();
2693
}
2694
}
2695
2696
bool Viewport::_gui_control_has_focus(const Control *p_control, bool p_ignore_hidden_focus) {
2697
return (!p_ignore_hidden_focus || !gui.hide_focus) && gui.key_focus == p_control;
2698
}
2699
2700
void Viewport::_gui_control_grab_focus(Control *p_control, bool p_hide_focus) {
2701
if (gui.key_focus && gui.key_focus == p_control) {
2702
// Only worry about the focus visibility change.
2703
if (p_hide_focus != gui.hide_focus && _can_hide_focus_state()) {
2704
gui.hide_focus = p_hide_focus;
2705
p_control->queue_redraw();
2706
}
2707
return;
2708
}
2709
2710
get_tree()->call_group("_viewports", "_gui_remove_focus_for_window", get_base_window());
2711
if (p_control->is_inside_tree() && p_control->get_viewport() == this) {
2712
gui.key_focus = p_control;
2713
if (_can_hide_focus_state()) {
2714
gui.hide_focus = p_hide_focus;
2715
}
2716
emit_signal(SNAME("gui_focus_changed"), p_control);
2717
p_control->notification(Control::NOTIFICATION_FOCUS_ENTER);
2718
p_control->queue_redraw();
2719
}
2720
}
2721
2722
void Viewport::_gui_accept_event() {
2723
if (is_inside_tree()) {
2724
set_input_as_handled();
2725
}
2726
}
2727
2728
void Viewport::_drop_mouse_focus() {
2729
Control *c = gui.mouse_focus;
2730
BitField<MouseButtonMask> mask = gui.mouse_focus_mask;
2731
gui.mouse_focus = nullptr;
2732
gui.mouse_focus_mask.clear();
2733
2734
if (!c) {
2735
return;
2736
}
2737
2738
for (int i = 0; i < 3; i++) {
2739
if ((int)mask & (1 << i)) {
2740
Ref<InputEventMouseButton> mb;
2741
mb.instantiate();
2742
mb->set_position(c->get_local_mouse_position());
2743
mb->set_global_position(c->get_local_mouse_position());
2744
mb->set_button_index(MouseButton(i + 1));
2745
mb->set_pressed(false);
2746
mb->set_device(InputEvent::DEVICE_ID_INTERNAL);
2747
c->_call_gui_input(mb);
2748
}
2749
}
2750
}
2751
2752
void Viewport::_drop_physics_mouseover(bool p_paused_only) {
2753
#ifndef PHYSICS_2D_DISABLED
2754
_cleanup_mouseover_colliders(true, p_paused_only);
2755
#endif // PHYSICS_2D_DISABLED
2756
2757
#ifndef PHYSICS_3D_DISABLED
2758
if (physics_object_over.is_valid()) {
2759
CollisionObject3D *co = ObjectDB::get_instance<CollisionObject3D>(physics_object_over);
2760
if (co) {
2761
if (!co->is_inside_tree()) {
2762
physics_object_over = ObjectID();
2763
physics_object_capture = ObjectID();
2764
} else if (!(p_paused_only && co->can_process())) {
2765
co->_mouse_exit();
2766
physics_object_over = ObjectID();
2767
physics_object_capture = ObjectID();
2768
}
2769
}
2770
}
2771
#endif // PHYSICS_3D_DISABLED
2772
}
2773
2774
void Viewport::_gui_grab_click_focus(Control *p_control) {
2775
gui.mouse_click_grabber = p_control;
2776
callable_mp(this, &Viewport::_post_gui_grab_click_focus).call_deferred();
2777
}
2778
2779
void Viewport::_post_gui_grab_click_focus() {
2780
Control *focus_grabber = gui.mouse_click_grabber;
2781
if (!focus_grabber) {
2782
// Redundant grab requests were made.
2783
return;
2784
}
2785
gui.mouse_click_grabber = nullptr;
2786
2787
if (gui.mouse_focus) {
2788
if (gui.mouse_focus == focus_grabber) {
2789
return;
2790
}
2791
2792
BitField<MouseButtonMask> mask = gui.mouse_focus_mask;
2793
Point2 click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
2794
2795
for (int i = 0; i < 3; i++) {
2796
if ((int)mask & (1 << i)) {
2797
Ref<InputEventMouseButton> mb;
2798
mb.instantiate();
2799
2800
// Send unclick.
2801
2802
mb->set_position(click);
2803
mb->set_button_index(MouseButton(i + 1));
2804
mb->set_pressed(false);
2805
mb->set_device(InputEvent::DEVICE_ID_INTERNAL);
2806
gui.mouse_focus->_call_gui_input(mb);
2807
}
2808
}
2809
2810
gui.mouse_focus = focus_grabber;
2811
click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
2812
2813
for (int i = 0; i < 3; i++) {
2814
if ((int)mask & (1 << i)) {
2815
Ref<InputEventMouseButton> mb;
2816
mb.instantiate();
2817
2818
// Send click.
2819
2820
mb->set_position(click);
2821
mb->set_button_index(MouseButton(i + 1));
2822
mb->set_pressed(true);
2823
mb->set_device(InputEvent::DEVICE_ID_INTERNAL);
2824
callable_mp(gui.mouse_focus, &Control::_call_gui_input).call_deferred(mb);
2825
}
2826
}
2827
}
2828
}
2829
2830
///////////////////////////////
2831
2832
void Viewport::_push_text_input(const String &p_text, bool p_emit_signal) {
2833
ERR_MAIN_THREAD_GUARD;
2834
if (gui.subwindow_focused) {
2835
gui.subwindow_focused->push_text_input(p_text);
2836
return;
2837
}
2838
2839
StringName set_text_method = SNAME("_set_text");
2840
if (!gui.key_focus || !gui.key_focus->has_method(set_text_method)) {
2841
return;
2842
}
2843
gui.key_focus->call(set_text_method, p_text, p_emit_signal);
2844
}
2845
2846
void Viewport::push_text_input(const String &p_text) {
2847
_push_text_input(p_text, false);
2848
}
2849
2850
Viewport::SubWindowResize Viewport::_sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point) {
2851
if (p_subwindow->get_flag(Window::FLAG_BORDERLESS) || p_subwindow->get_flag(Window::FLAG_RESIZE_DISABLED)) {
2852
return SUB_WINDOW_RESIZE_DISABLED;
2853
}
2854
2855
Rect2i r = Rect2i(p_subwindow->get_position(), p_subwindow->get_size());
2856
2857
int title_height = p_subwindow->theme_cache.title_height;
2858
2859
r.position.y -= title_height;
2860
r.size.y += title_height;
2861
2862
if (r.has_point(p_point)) {
2863
return SUB_WINDOW_RESIZE_DISABLED; // It's inside, so no resize.
2864
}
2865
2866
int dist_x = p_point.x < r.position.x ? (p_point.x - r.position.x) : (p_point.x > (r.position.x + r.size.x) ? (p_point.x - (r.position.x + r.size.x)) : 0);
2867
int dist_y = p_point.y < r.position.y ? (p_point.y - r.position.y) : (p_point.y > (r.position.y + r.size.y) ? (p_point.y - (r.position.y + r.size.y)) : 0);
2868
2869
int limit = p_subwindow->theme_cache.resize_margin;
2870
2871
if (Math::abs(dist_x) > limit) {
2872
return SUB_WINDOW_RESIZE_DISABLED;
2873
}
2874
2875
if (Math::abs(dist_y) > limit) {
2876
return SUB_WINDOW_RESIZE_DISABLED;
2877
}
2878
2879
if (dist_x < 0 && dist_y < 0) {
2880
return SUB_WINDOW_RESIZE_TOP_LEFT;
2881
}
2882
2883
if (dist_x == 0 && dist_y < 0) {
2884
return SUB_WINDOW_RESIZE_TOP;
2885
}
2886
2887
if (dist_x > 0 && dist_y < 0) {
2888
return SUB_WINDOW_RESIZE_TOP_RIGHT;
2889
}
2890
2891
if (dist_x < 0 && dist_y == 0) {
2892
return SUB_WINDOW_RESIZE_LEFT;
2893
}
2894
2895
if (dist_x > 0 && dist_y == 0) {
2896
return SUB_WINDOW_RESIZE_RIGHT;
2897
}
2898
2899
if (dist_x < 0 && dist_y > 0) {
2900
return SUB_WINDOW_RESIZE_BOTTOM_LEFT;
2901
}
2902
2903
if (dist_x == 0 && dist_y > 0) {
2904
return SUB_WINDOW_RESIZE_BOTTOM;
2905
}
2906
2907
if (dist_x > 0 && dist_y > 0) {
2908
return SUB_WINDOW_RESIZE_BOTTOM_RIGHT;
2909
}
2910
2911
return SUB_WINDOW_RESIZE_DISABLED;
2912
}
2913
2914
bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
2915
if (gui.subwindow_drag != SUB_WINDOW_DRAG_DISABLED) {
2916
ERR_FAIL_NULL_V(gui.currently_dragged_subwindow, false);
2917
2918
Ref<InputEventMouseButton> mb = p_event;
2919
if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
2920
if (gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE) {
2921
if (gui.subwindow_drag_close_rect.has_point(mb->get_position())) {
2922
// Close window.
2923
gui.currently_dragged_subwindow->_event_callback(DisplayServer::WINDOW_EVENT_CLOSE_REQUEST);
2924
}
2925
}
2926
gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
2927
if (gui.currently_dragged_subwindow != nullptr) { // May have been erased.
2928
_sub_window_update(gui.currently_dragged_subwindow);
2929
gui.currently_dragged_subwindow = nullptr;
2930
}
2931
}
2932
2933
Ref<InputEventMouseMotion> mm = p_event;
2934
if (mm.is_valid()) {
2935
if (gui.subwindow_drag == SUB_WINDOW_DRAG_MOVE) {
2936
Vector2 diff = mm->get_position() - gui.subwindow_drag_from;
2937
Rect2i new_rect(gui.subwindow_drag_pos + diff, gui.currently_dragged_subwindow->get_size());
2938
2939
if (gui.currently_dragged_subwindow->is_clamped_to_embedder()) {
2940
new_rect = gui.currently_dragged_subwindow->fit_rect_in_parent(new_rect, get_visible_rect());
2941
}
2942
2943
gui.currently_dragged_subwindow->_rect_changed_callback(new_rect);
2944
2945
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
2946
DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_MOVE);
2947
}
2948
}
2949
if (gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE) {
2950
gui.subwindow_drag_close_inside = gui.subwindow_drag_close_rect.has_point(mm->get_position());
2951
}
2952
if (gui.subwindow_drag == SUB_WINDOW_DRAG_RESIZE) {
2953
Vector2i diff = mm->get_position() - gui.subwindow_drag_from;
2954
Size2i min_size = gui.currently_dragged_subwindow->get_min_size();
2955
Size2i min_size_clamped = gui.currently_dragged_subwindow->get_clamped_minimum_size();
2956
2957
min_size_clamped = min_size_clamped.maxi(1);
2958
2959
Rect2i r = gui.subwindow_resize_from_rect;
2960
2961
Size2i limit = r.size - min_size_clamped;
2962
2963
switch (gui.subwindow_resize_mode) {
2964
case SUB_WINDOW_RESIZE_TOP_LEFT: {
2965
diff.x = MIN(diff.x, limit.x);
2966
diff.y = MIN(diff.y, limit.y);
2967
r.position += diff;
2968
r.size -= diff;
2969
} break;
2970
case SUB_WINDOW_RESIZE_TOP: {
2971
diff.x = 0;
2972
diff.y = MIN(diff.y, limit.y);
2973
r.position += diff;
2974
r.size -= diff;
2975
} break;
2976
case SUB_WINDOW_RESIZE_TOP_RIGHT: {
2977
diff.x = MAX(diff.x, -limit.x);
2978
diff.y = MIN(diff.y, limit.y);
2979
r.position.y += diff.y;
2980
r.size.y -= diff.y;
2981
r.size.x += diff.x;
2982
} break;
2983
case SUB_WINDOW_RESIZE_LEFT: {
2984
diff.x = MIN(diff.x, limit.x);
2985
diff.y = 0;
2986
r.position += diff;
2987
r.size -= diff;
2988
2989
} break;
2990
case SUB_WINDOW_RESIZE_RIGHT: {
2991
diff.x = MAX(diff.x, -limit.x);
2992
r.size.x += diff.x;
2993
} break;
2994
case SUB_WINDOW_RESIZE_BOTTOM_LEFT: {
2995
diff.x = MIN(diff.x, limit.x);
2996
diff.y = MAX(diff.y, -limit.y);
2997
r.position.x += diff.x;
2998
r.size.x -= diff.x;
2999
r.size.y += diff.y;
3000
3001
} break;
3002
case SUB_WINDOW_RESIZE_BOTTOM: {
3003
diff.y = MAX(diff.y, -limit.y);
3004
r.size.y += diff.y;
3005
} break;
3006
case SUB_WINDOW_RESIZE_BOTTOM_RIGHT: {
3007
diff.x = MAX(diff.x, -limit.x);
3008
diff.y = MAX(diff.y, -limit.y);
3009
r.size += diff;
3010
3011
} break;
3012
default: {
3013
}
3014
}
3015
3016
Size2i max_size = gui.currently_dragged_subwindow->get_max_size();
3017
if ((max_size.x > 0 || max_size.y > 0) && (max_size.x >= min_size.x && max_size.y >= min_size.y)) {
3018
max_size = max_size.maxi(1);
3019
3020
if (r.size.x > max_size.x) {
3021
r.size.x = max_size.x;
3022
}
3023
if (r.size.y > max_size.y) {
3024
r.size.y = max_size.y;
3025
}
3026
}
3027
3028
gui.currently_dragged_subwindow->_rect_changed_callback(r);
3029
}
3030
3031
if (gui.currently_dragged_subwindow) { // May have been erased.
3032
_sub_window_update(gui.currently_dragged_subwindow);
3033
}
3034
}
3035
3036
return true; // Handled.
3037
}
3038
Ref<InputEventMouseButton> mb = p_event;
3039
// If the event is a mouse button, we need to check whether another window was clicked.
3040
3041
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
3042
Window *click_on_window = nullptr;
3043
for (int i = gui.sub_windows.size() - 1; i >= 0; i--) {
3044
SubWindow sw = gui.sub_windows.write[i];
3045
3046
// Clicked inside window?
3047
3048
Rect2i r = Rect2i(sw.window->get_position(), sw.window->get_size());
3049
3050
if (!sw.window->get_flag(Window::FLAG_BORDERLESS)) {
3051
// Check top bar.
3052
int title_height = sw.window->theme_cache.title_height;
3053
Rect2i title_bar = r;
3054
title_bar.position.y -= title_height;
3055
title_bar.size.y = title_height;
3056
3057
if (title_bar.size.y > 0 && title_bar.has_point(mb->get_position())) {
3058
click_on_window = sw.window;
3059
3060
int close_h_ofs = sw.window->theme_cache.close_h_offset;
3061
int close_v_ofs = sw.window->theme_cache.close_v_offset;
3062
bool pressed = gui.subwindow_focused == sw.window && gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE && gui.subwindow_drag_close_inside;
3063
Ref<Texture2D> close_icon = pressed ? sw.window->theme_cache.close_pressed : sw.window->theme_cache.close;
3064
3065
Rect2 close_rect;
3066
close_rect.position = Vector2(r.position.x + r.size.x - close_h_ofs, r.position.y - close_v_ofs);
3067
close_rect.size = close_icon->get_size();
3068
3069
if (gui.subwindow_focused != sw.window) {
3070
// Refocus.
3071
_sub_window_grab_focus(sw.window);
3072
}
3073
3074
if (close_rect.has_point(mb->get_position())) {
3075
gui.subwindow_drag = SUB_WINDOW_DRAG_CLOSE;
3076
gui.subwindow_drag_close_inside = true; // Starts inside.
3077
gui.subwindow_drag_close_rect = close_rect;
3078
} else {
3079
gui.subwindow_drag = SUB_WINDOW_DRAG_MOVE;
3080
}
3081
3082
gui.subwindow_drag_from = mb->get_position();
3083
gui.subwindow_drag_pos = sw.window->get_position();
3084
3085
_sub_window_update(sw.window);
3086
} else {
3087
gui.subwindow_resize_mode = _sub_window_get_resize_margin(sw.window, mb->get_position());
3088
if (gui.subwindow_resize_mode != SUB_WINDOW_RESIZE_DISABLED) {
3089
if (gui.subwindow_focused != sw.window) {
3090
// Refocus.
3091
_sub_window_grab_focus(sw.window);
3092
}
3093
3094
gui.subwindow_resize_from_rect = r;
3095
gui.subwindow_drag_from = mb->get_position();
3096
gui.subwindow_drag = SUB_WINDOW_DRAG_RESIZE;
3097
click_on_window = sw.window;
3098
}
3099
}
3100
}
3101
if (!click_on_window && r.has_point(mb->get_position())) {
3102
// Clicked, see if it needs to fetch focus.
3103
if (gui.subwindow_focused != sw.window) {
3104
// Refocus.
3105
_sub_window_grab_focus(sw.window);
3106
}
3107
3108
click_on_window = sw.window;
3109
}
3110
3111
if (click_on_window) {
3112
break;
3113
}
3114
}
3115
3116
gui.currently_dragged_subwindow = click_on_window;
3117
3118
if (!click_on_window && gui.subwindow_focused) {
3119
// No window found and clicked, remove focus.
3120
_sub_window_grab_focus(nullptr);
3121
}
3122
}
3123
3124
if (gui.subwindow_focused) {
3125
Ref<InputEventMouseMotion> mm = p_event;
3126
if (mm.is_valid()) {
3127
SubWindowResize resize = _sub_window_get_resize_margin(gui.subwindow_focused, mm->get_position());
3128
if (resize != SUB_WINDOW_RESIZE_DISABLED) {
3129
DisplayServer::CursorShape shapes[SUB_WINDOW_RESIZE_MAX] = {
3130
DisplayServer::CURSOR_ARROW,
3131
DisplayServer::CURSOR_FDIAGSIZE,
3132
DisplayServer::CURSOR_VSIZE,
3133
DisplayServer::CURSOR_BDIAGSIZE,
3134
DisplayServer::CURSOR_HSIZE,
3135
DisplayServer::CURSOR_HSIZE,
3136
DisplayServer::CURSOR_BDIAGSIZE,
3137
DisplayServer::CURSOR_VSIZE,
3138
DisplayServer::CURSOR_FDIAGSIZE
3139
};
3140
3141
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
3142
DisplayServer::get_singleton()->cursor_set_shape(shapes[resize]);
3143
}
3144
3145
return true; // Reserved for showing the resize cursor.
3146
}
3147
}
3148
}
3149
3150
if (gui.subwindow_drag != SUB_WINDOW_DRAG_DISABLED) {
3151
return true; // Dragging, don't pass the event.
3152
}
3153
3154
if (!gui.subwindow_focused) {
3155
// No window focus, check for unfocusable windows under the cursor.
3156
Ref<InputEventMouse> me = p_event;
3157
if (me.is_valid()) {
3158
for (int i = gui.sub_windows.size() - 1; i >= 0; i--) {
3159
const SubWindow &sw = gui.sub_windows[i];
3160
if (!sw.window->get_flag(Window::FLAG_NO_FOCUS) || sw.window->get_flag(Window::FLAG_MOUSE_PASSTHROUGH)) {
3161
continue;
3162
}
3163
Rect2i r = Rect2i(sw.window->get_position(), sw.window->get_size());
3164
if (r.has_point(me->get_position())) {
3165
Transform2D window_ofs;
3166
window_ofs.set_origin(-sw.window->get_position());
3167
Ref<InputEvent> ev = p_event->xformed_by(window_ofs);
3168
sw.window->_window_input(ev);
3169
return true;
3170
}
3171
}
3172
}
3173
return false;
3174
}
3175
3176
Transform2D window_ofs;
3177
window_ofs.set_origin(-gui.subwindow_focused->get_position());
3178
3179
Ref<InputEvent> ev = p_event->xformed_by(window_ofs);
3180
3181
gui.subwindow_focused->_window_input(ev);
3182
3183
return true;
3184
}
3185
3186
void Viewport::_window_start_drag(Window *p_window) {
3187
int index = _sub_window_find(p_window);
3188
ERR_FAIL_COND(index == -1);
3189
3190
SubWindow sw = gui.sub_windows.write[index];
3191
3192
if (gui.subwindow_focused != sw.window) {
3193
// Refocus.
3194
_sub_window_grab_focus(sw.window);
3195
}
3196
3197
gui.subwindow_drag = SUB_WINDOW_DRAG_MOVE;
3198
gui.subwindow_drag_from = get_mouse_position();
3199
gui.subwindow_drag_pos = sw.window->get_position();
3200
gui.currently_dragged_subwindow = sw.window;
3201
3202
_sub_window_update(sw.window);
3203
}
3204
3205
void Viewport::_window_start_resize(SubWindowResize p_edge, Window *p_window) {
3206
int index = _sub_window_find(p_window);
3207
ERR_FAIL_COND(index == -1);
3208
3209
SubWindow sw = gui.sub_windows.write[index];
3210
Rect2i r = Rect2i(sw.window->get_position(), sw.window->get_size());
3211
3212
if (gui.subwindow_focused != sw.window) {
3213
// Refocus.
3214
_sub_window_grab_focus(sw.window);
3215
}
3216
3217
gui.subwindow_drag = SUB_WINDOW_DRAG_RESIZE;
3218
gui.subwindow_resize_mode = p_edge;
3219
gui.subwindow_resize_from_rect = r;
3220
gui.subwindow_drag_from = get_mouse_position();
3221
gui.subwindow_drag_pos = sw.window->get_position();
3222
gui.currently_dragged_subwindow = sw.window;
3223
3224
_sub_window_update(sw.window);
3225
}
3226
3227
void Viewport::_update_mouse_over(const Ref<InputEventMouse> &p_mm) {
3228
// Update gui.mouse_over and gui.subwindow_over in all Viewports.
3229
// Send necessary mouse_enter/mouse_exit signals and the MOUSE_ENTER/MOUSE_EXIT notifications for every Viewport in the SceneTree.
3230
3231
if (is_attached_in_viewport()) {
3232
// Execute this function only, when it is processed by a native Window or a SubViewport, that has no SubViewportContainer as parent.
3233
return;
3234
}
3235
3236
if (get_tree()->get_root()->is_embedding_subwindows() || is_sub_viewport()) {
3237
// Use embedder logic for calculating mouse position.
3238
_update_mouse_over(p_mm->get_position());
3239
} else {
3240
// Native Window: Use DisplayServer logic for calculating mouse position.
3241
Window *receiving_window = get_tree()->get_root()->gui.windowmanager_window_over;
3242
if (!receiving_window) {
3243
return;
3244
}
3245
if (receiving_window->get_window_id() != p_mm->get_window_id()) {
3246
Vector2 pos = DisplayServer::get_singleton()->mouse_get_position() - receiving_window->get_position();
3247
pos = receiving_window->get_final_transform().affine_inverse().xform(pos);
3248
receiving_window->_update_mouse_over(pos);
3249
} else {
3250
receiving_window->_update_mouse_over(p_mm->get_position());
3251
}
3252
}
3253
}
3254
3255
void Viewport::_update_mouse_over(Vector2 p_pos) {
3256
gui.last_mouse_pos = p_pos; // Necessary, because mouse cursor can be over Viewports that are not reached by the InputEvent.
3257
// Look for embedded windows at mouse position.
3258
if (is_embedding_subwindows()) {
3259
for (int i = gui.sub_windows.size() - 1; i >= 0; i--) {
3260
Window *sw = gui.sub_windows[i].window;
3261
Rect2 swrect = Rect2(sw->get_position(), sw->get_size());
3262
Rect2 swrect_border = swrect;
3263
3264
if (!sw->get_flag(Window::FLAG_BORDERLESS)) {
3265
int title_height = sw->theme_cache.title_height;
3266
int margin = sw->theme_cache.resize_margin;
3267
swrect_border.position.y -= title_height + margin;
3268
swrect_border.size.y += title_height + margin * 2;
3269
swrect_border.position.x -= margin;
3270
swrect_border.size.x += margin * 2;
3271
}
3272
3273
if (swrect_border.has_point(p_pos)) {
3274
if (gui.mouse_over.is_valid()) {
3275
_drop_mouse_over();
3276
} else if (!gui.subwindow_over) {
3277
_drop_physics_mouseover();
3278
}
3279
if (swrect.has_point(p_pos)) {
3280
if (sw != gui.subwindow_over) {
3281
if (gui.subwindow_over) {
3282
gui.subwindow_over->_mouse_leave_viewport();
3283
}
3284
gui.subwindow_over = sw;
3285
}
3286
if (!sw->is_input_disabled()) {
3287
sw->_update_mouse_over(sw->get_final_transform().affine_inverse().xform(p_pos - sw->get_position()));
3288
}
3289
} else {
3290
if (gui.subwindow_over) {
3291
gui.subwindow_over->_mouse_leave_viewport();
3292
gui.subwindow_over = nullptr;
3293
}
3294
}
3295
return;
3296
}
3297
}
3298
3299
if (gui.subwindow_over) {
3300
// Take care of moving mouse out of any embedded Window.
3301
gui.subwindow_over->_mouse_leave_viewport();
3302
gui.subwindow_over = nullptr;
3303
}
3304
}
3305
3306
// Look for Controls at mouse position.
3307
Control *over = gui_find_control(p_pos);
3308
ObjectID over_id = over ? over->get_instance_id() : ObjectID();
3309
get_section_root_viewport()->gui.target_control = over;
3310
bool notify_embedded_viewports = false;
3311
if (over_id != gui.mouse_over || (!over && !gui.mouse_over_hierarchy.is_empty())) {
3312
// Find the common ancestor of `gui.mouse_over` and `over`.
3313
Control *common_ancestor = nullptr;
3314
LocalVector<ObjectID> over_ancestors;
3315
3316
if (over) {
3317
// Get all ancestors that the mouse is currently over and need an enter signal.
3318
CanvasItem *ancestor = over;
3319
while (ancestor) {
3320
Control *ancestor_control = Object::cast_to<Control>(ancestor);
3321
if (ancestor_control) {
3322
if (ancestor_control->get_mouse_filter_with_override() != Control::MOUSE_FILTER_IGNORE) {
3323
int found = gui.mouse_over_hierarchy.find(ancestor_control->get_instance_id());
3324
if (found >= 0) {
3325
common_ancestor = ObjectDB::get_instance<Control>(gui.mouse_over_hierarchy[found]);
3326
break;
3327
}
3328
over_ancestors.push_back(ancestor_control->get_instance_id());
3329
}
3330
if (ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
3331
// MOUSE_FILTER_STOP breaks the propagation chain.
3332
break;
3333
}
3334
}
3335
if (ancestor->is_set_as_top_level()) {
3336
// Top level breaks the propagation chain.
3337
break;
3338
}
3339
ancestor = ancestor->get_parent_item();
3340
}
3341
}
3342
3343
if (gui.mouse_over.is_valid() || !gui.mouse_over_hierarchy.is_empty()) {
3344
// Send Mouse Exit Self and Mouse Exit notifications.
3345
_drop_mouse_over(common_ancestor);
3346
} else {
3347
_drop_physics_mouseover();
3348
}
3349
3350
if (over) {
3351
gui.mouse_over = over_id;
3352
gui.mouse_over_hierarchy.reserve(gui.mouse_over_hierarchy.size() + over_ancestors.size());
3353
3354
gui.sending_mouse_enter_exit_notifications = true;
3355
3356
// Send Mouse Enter notifications to parents first.
3357
for (int i = over_ancestors.size() - 1; i >= 0; i--) {
3358
Control *ctrl = ObjectDB::get_instance<Control>(over_ancestors[i]);
3359
if (ctrl) {
3360
gui.mouse_over_hierarchy.push_back(over_ancestors[i]);
3361
ctrl->notification(Control::NOTIFICATION_MOUSE_ENTER);
3362
ctrl->emit_signal(SceneStringName(mouse_entered));
3363
}
3364
}
3365
3366
// Send Mouse Enter Self notification.
3367
CanvasItem *ctrl = ObjectDB::get_instance<CanvasItem>(gui.mouse_over);
3368
if (ctrl) {
3369
ctrl->notification(Control::NOTIFICATION_MOUSE_ENTER_SELF);
3370
}
3371
3372
gui.sending_mouse_enter_exit_notifications = false;
3373
3374
notify_embedded_viewports = true;
3375
}
3376
}
3377
3378
if (over) {
3379
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(over);
3380
if (!c) {
3381
return;
3382
}
3383
Vector2 pos = c->get_global_transform_with_canvas().affine_inverse().xform(p_pos);
3384
if (c->is_stretch_enabled()) {
3385
pos /= c->get_stretch_shrink();
3386
}
3387
3388
for (int i = 0; i < c->get_child_count(); i++) {
3389
SubViewport *v = Object::cast_to<SubViewport>(c->get_child(i));
3390
if (!v || v->is_input_disabled()) {
3391
continue;
3392
}
3393
if (notify_embedded_viewports) {
3394
v->notification(NOTIFICATION_VP_MOUSE_ENTER);
3395
}
3396
v->_update_mouse_over(v->get_final_transform().affine_inverse().xform(pos));
3397
}
3398
3399
Viewport *section_root = get_section_root_viewport();
3400
if (section_root && c->is_mouse_target_enabled()) {
3401
// Evaluating `mouse_target` and adjusting target_control needs to happen
3402
// after `_update_mouse_over` in the SubViewports, because otherwise physics picking
3403
// would not work inside SubViewports.
3404
section_root->gui.target_control = over;
3405
}
3406
}
3407
}
3408
3409
void Viewport::_mouse_leave_viewport() {
3410
if (!is_inside_tree() || is_input_disabled()) {
3411
return;
3412
}
3413
if (gui.subwindow_over) {
3414
gui.subwindow_over->_mouse_leave_viewport();
3415
gui.subwindow_over = nullptr;
3416
} else if (gui.mouse_over.is_valid()) {
3417
_drop_mouse_over();
3418
}
3419
notification(NOTIFICATION_VP_MOUSE_EXIT);
3420
}
3421
3422
void Viewport::_drop_mouse_over(Control *p_until_control) {
3423
if (gui.sending_mouse_enter_exit_notifications) {
3424
// If notifications are already being sent, defer call.
3425
callable_mp(this, &Viewport::_drop_mouse_over).call_deferred(p_until_control);
3426
return;
3427
}
3428
3429
_gui_cancel_tooltip();
3430
SubViewportContainer *c = ObjectDB::get_instance<SubViewportContainer>(gui.mouse_over);
3431
if (c) {
3432
for (int i = 0; i < c->get_child_count(); i++) {
3433
SubViewport *v = Object::cast_to<SubViewport>(c->get_child(i));
3434
if (!v) {
3435
continue;
3436
}
3437
v->_mouse_leave_viewport();
3438
}
3439
}
3440
3441
gui.sending_mouse_enter_exit_notifications = true;
3442
CanvasItem *over = ObjectDB::get_instance<CanvasItem>(gui.mouse_over);
3443
if (over && over->is_inside_tree()) {
3444
over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF);
3445
}
3446
Viewport *section_root = get_section_root_viewport();
3447
if (section_root && section_root->gui.target_control == over) {
3448
section_root->gui.target_control = nullptr;
3449
}
3450
gui.mouse_over = ObjectID();
3451
3452
// Send Mouse Exit notifications to children first. Don't send to p_until_control or above.
3453
int notification_until = p_until_control ? gui.mouse_over_hierarchy.find(p_until_control->get_instance_id()) + 1 : 0;
3454
for (int i = gui.mouse_over_hierarchy.size() - 1; i >= notification_until; i--) {
3455
Control *ctrl = ObjectDB::get_instance<Control>(gui.mouse_over_hierarchy[i]);
3456
if (ctrl && ctrl->is_inside_tree()) {
3457
ctrl->notification(Control::NOTIFICATION_MOUSE_EXIT);
3458
ctrl->emit_signal(SceneStringName(mouse_exited));
3459
}
3460
}
3461
gui.mouse_over_hierarchy.resize(notification_until);
3462
gui.sending_mouse_enter_exit_notifications = false;
3463
}
3464
3465
void Viewport::push_input(RequiredParam<InputEvent> rp_event, bool p_local_coords) {
3466
ERR_MAIN_THREAD_GUARD;
3467
ERR_FAIL_COND(!is_inside_tree());
3468
EXTRACT_PARAM_OR_FAIL(p_event, rp_event);
3469
3470
if (disable_input || disable_input_override) {
3471
return;
3472
}
3473
3474
if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_ancestor_of(this)) {
3475
return;
3476
}
3477
3478
local_input_handled = false;
3479
if (!handle_input_locally) {
3480
Viewport *vp = this;
3481
while (true) {
3482
if (Object::cast_to<Window>(vp) || !vp->get_parent()) {
3483
break;
3484
}
3485
vp = vp->get_parent()->get_viewport();
3486
}
3487
vp->local_input_handled = false;
3488
}
3489
3490
Ref<InputEvent> ev;
3491
if (!p_local_coords) {
3492
ev = _make_input_local(p_event);
3493
} else {
3494
ev = p_event;
3495
}
3496
3497
Ref<InputEventMouse> me = ev;
3498
if (me.is_valid()) {
3499
_update_mouse_over(me);
3500
}
3501
3502
if (is_embedding_subwindows() && _sub_windows_forward_input(ev)) {
3503
set_input_as_handled();
3504
return;
3505
}
3506
3507
if (!_can_consume_input_events()) {
3508
return;
3509
}
3510
3511
if (!is_input_handled()) {
3512
ERR_FAIL_COND(!is_inside_tree());
3513
get_tree()->_call_input_pause(input_group, SceneTree::CALL_INPUT_TYPE_INPUT, ev, this); //not a bug, must happen before GUI, order is _input -> gui input -> _unhandled input
3514
}
3515
3516
if (!is_input_handled()) {
3517
ERR_FAIL_COND(!is_inside_tree());
3518
_gui_input_event(ev);
3519
} else {
3520
// Cleanup internal GUI state after accepting event during _input().
3521
_gui_cleanup_internal_state(ev);
3522
}
3523
3524
if (!is_input_handled()) {
3525
_push_unhandled_input_internal(ev);
3526
}
3527
3528
event_count++;
3529
}
3530
3531
#ifndef DISABLE_DEPRECATED
3532
void Viewport::push_unhandled_input(RequiredParam<InputEvent> rp_event, bool p_local_coords) {
3533
ERR_MAIN_THREAD_GUARD;
3534
WARN_DEPRECATED_MSG(R"*(The "push_unhandled_input()" method is deprecated, use "push_input()" instead.)*");
3535
ERR_FAIL_COND(!is_inside_tree());
3536
EXTRACT_PARAM_OR_FAIL(p_event, rp_event);
3537
3538
local_input_handled = false;
3539
3540
if (disable_input || disable_input_override || !_can_consume_input_events()) {
3541
return;
3542
}
3543
3544
if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_ancestor_of(this)) {
3545
return;
3546
}
3547
3548
Ref<InputEvent> ev;
3549
if (!p_local_coords) {
3550
ev = _make_input_local(p_event);
3551
} else {
3552
ev = p_event;
3553
}
3554
3555
_push_unhandled_input_internal(ev);
3556
}
3557
#endif // DISABLE_DEPRECATED
3558
3559
void Viewport::_push_unhandled_input_internal(const Ref<InputEvent> &p_event) {
3560
// Shortcut Input.
3561
if (Object::cast_to<InputEventKey>(*p_event) != nullptr || Object::cast_to<InputEventShortcut>(*p_event) != nullptr || Object::cast_to<InputEventJoypadButton>(*p_event) != nullptr) {
3562
ERR_FAIL_COND(!is_inside_tree());
3563
get_tree()->_call_input_pause(shortcut_input_group, SceneTree::CALL_INPUT_TYPE_SHORTCUT_INPUT, p_event, this);
3564
}
3565
3566
// Unhandled key Input - Used for performance reasons - This is called a lot less than _unhandled_input since it ignores MouseMotion, and to handle Unicode input with Alt / Ctrl modifiers after handling shortcuts.
3567
if (!is_input_handled() && (Object::cast_to<InputEventKey>(*p_event) != nullptr)) {
3568
ERR_FAIL_COND(!is_inside_tree());
3569
get_tree()->_call_input_pause(unhandled_key_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT, p_event, this);
3570
}
3571
3572
// Unhandled Input.
3573
if (!is_input_handled()) {
3574
ERR_FAIL_COND(!is_inside_tree());
3575
get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, p_event, this);
3576
}
3577
3578
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
3579
if (physics_object_picking && !is_input_handled()) {
3580
if (Input::get_singleton()->get_mouse_mode() != Input::MouseMode::MOUSE_MODE_CAPTURED &&
3581
(Object::cast_to<InputEventMouse>(*p_event) ||
3582
Object::cast_to<InputEventScreenDrag>(*p_event) ||
3583
Object::cast_to<InputEventScreenTouch>(*p_event)
3584
3585
)) {
3586
physics_picking_events.push_back(p_event);
3587
set_input_as_handled();
3588
}
3589
}
3590
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
3591
}
3592
3593
void Viewport::notify_mouse_entered() {
3594
if (gui.mouse_in_viewport) {
3595
WARN_PRINT_ED("The Viewport was previously notified that the mouse is in its area. There is no need to notify it at this time.");
3596
return;
3597
}
3598
notification(NOTIFICATION_VP_MOUSE_ENTER);
3599
}
3600
3601
void Viewport::notify_mouse_exited() {
3602
if (!gui.mouse_in_viewport) {
3603
WARN_PRINT_ED("The Viewport was previously notified that the mouse has left its area. There is no need to notify it at this time.");
3604
return;
3605
}
3606
_mouse_leave_viewport();
3607
}
3608
3609
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
3610
void Viewport::set_physics_object_picking(bool p_enable) {
3611
ERR_MAIN_THREAD_GUARD;
3612
physics_object_picking = p_enable;
3613
if (physics_object_picking) {
3614
add_to_group("_picking_viewports");
3615
} else {
3616
physics_picking_events.clear();
3617
if (is_in_group("_picking_viewports")) {
3618
remove_from_group("_picking_viewports");
3619
}
3620
}
3621
}
3622
3623
bool Viewport::get_physics_object_picking() {
3624
ERR_READ_THREAD_GUARD_V(false);
3625
return physics_object_picking;
3626
}
3627
3628
void Viewport::set_physics_object_picking_sort(bool p_enable) {
3629
ERR_MAIN_THREAD_GUARD;
3630
physics_object_picking_sort = p_enable;
3631
}
3632
3633
bool Viewport::get_physics_object_picking_sort() {
3634
ERR_READ_THREAD_GUARD_V(false);
3635
return physics_object_picking_sort;
3636
}
3637
3638
void Viewport::set_physics_object_picking_first_only(bool p_enable) {
3639
physics_object_picking_first_only = p_enable;
3640
}
3641
3642
bool Viewport::get_physics_object_picking_first_only() {
3643
return physics_object_picking_first_only;
3644
}
3645
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
3646
3647
Vector2 Viewport::get_camera_coords(const Vector2 &p_viewport_coords) const {
3648
ERR_READ_THREAD_GUARD_V(Vector2());
3649
Transform2D xf = stretch_transform * global_canvas_transform;
3650
return xf.xform(p_viewport_coords);
3651
}
3652
3653
Vector2 Viewport::get_camera_rect_size() const {
3654
ERR_READ_THREAD_GUARD_V(Vector2());
3655
return size;
3656
}
3657
3658
void Viewport::set_disable_input(bool p_disable) {
3659
ERR_MAIN_THREAD_GUARD;
3660
if (p_disable == disable_input) {
3661
return;
3662
}
3663
if (p_disable && !disable_input_override) {
3664
_drop_mouse_focus();
3665
_mouse_leave_viewport();
3666
_gui_cancel_tooltip();
3667
}
3668
disable_input = p_disable;
3669
}
3670
3671
bool Viewport::is_input_disabled() const {
3672
ERR_READ_THREAD_GUARD_V(false);
3673
return disable_input;
3674
}
3675
3676
void Viewport::set_disable_input_override(bool p_disable) {
3677
ERR_MAIN_THREAD_GUARD;
3678
if (p_disable == disable_input_override) {
3679
return;
3680
}
3681
if (p_disable && !disable_input) {
3682
_drop_mouse_focus();
3683
_mouse_leave_viewport();
3684
_gui_cancel_tooltip();
3685
}
3686
disable_input_override = p_disable;
3687
}
3688
3689
String Viewport::gui_get_drag_description() const {
3690
ERR_READ_THREAD_GUARD_V(String());
3691
if (get_section_root_viewport()->gui.drag_description.is_empty()) {
3692
return RTR("Drag-and-drop data");
3693
} else {
3694
return get_section_root_viewport()->gui.drag_description;
3695
}
3696
}
3697
3698
void Viewport::gui_set_drag_description(const String &p_description) {
3699
gui.drag_description = p_description;
3700
}
3701
3702
Variant Viewport::gui_get_drag_data() const {
3703
ERR_READ_THREAD_GUARD_V(Variant());
3704
return get_section_root_viewport()->gui.drag_data;
3705
}
3706
3707
PackedStringArray Viewport::get_configuration_warnings() const {
3708
ERR_MAIN_THREAD_GUARD_V(PackedStringArray());
3709
PackedStringArray warnings = Node::get_configuration_warnings();
3710
3711
if (size.x <= 1 || size.y <= 1) {
3712
warnings.push_back(RTR("The Viewport size must be greater than or equal to 2 pixels on both dimensions to render anything."));
3713
}
3714
return warnings;
3715
}
3716
3717
void Viewport::gui_reset_canvas_sort_index() {
3718
ERR_MAIN_THREAD_GUARD;
3719
gui.canvas_sort_index = 0;
3720
}
3721
3722
int Viewport::gui_get_canvas_sort_index() {
3723
ERR_MAIN_THREAD_GUARD_V(0);
3724
return gui.canvas_sort_index++;
3725
}
3726
3727
void Viewport::gui_release_focus() {
3728
ERR_MAIN_THREAD_GUARD;
3729
if (gui.key_focus) {
3730
Control *f = gui.key_focus;
3731
gui.key_focus = nullptr;
3732
f->notification(Control::NOTIFICATION_FOCUS_EXIT, true);
3733
f->queue_redraw();
3734
}
3735
}
3736
3737
Control *Viewport::gui_get_focus_owner() const {
3738
ERR_READ_THREAD_GUARD_V(nullptr);
3739
return gui.key_focus;
3740
}
3741
3742
Control *Viewport::gui_get_hovered_control() const {
3743
ERR_READ_THREAD_GUARD_V(nullptr);
3744
return ObjectDB::get_instance<Control>(gui.mouse_over);
3745
}
3746
3747
void Viewport::set_msaa_2d(MSAA p_msaa) {
3748
ERR_MAIN_THREAD_GUARD;
3749
ERR_FAIL_INDEX(p_msaa, MSAA_MAX);
3750
if (msaa_2d == p_msaa) {
3751
return;
3752
}
3753
msaa_2d = p_msaa;
3754
RS::get_singleton()->viewport_set_msaa_2d(viewport, RS::ViewportMSAA(p_msaa));
3755
}
3756
3757
Viewport::MSAA Viewport::get_msaa_2d() const {
3758
ERR_READ_THREAD_GUARD_V(MSAA_DISABLED);
3759
return msaa_2d;
3760
}
3761
3762
void Viewport::set_msaa_3d(MSAA p_msaa) {
3763
ERR_MAIN_THREAD_GUARD;
3764
ERR_FAIL_INDEX(p_msaa, MSAA_MAX);
3765
if (msaa_3d == p_msaa) {
3766
return;
3767
}
3768
msaa_3d = p_msaa;
3769
RS::get_singleton()->viewport_set_msaa_3d(viewport, RS::ViewportMSAA(p_msaa));
3770
}
3771
3772
Viewport::MSAA Viewport::get_msaa_3d() const {
3773
ERR_READ_THREAD_GUARD_V(MSAA_DISABLED);
3774
return msaa_3d;
3775
}
3776
3777
void Viewport::set_screen_space_aa(ScreenSpaceAA p_screen_space_aa) {
3778
ERR_MAIN_THREAD_GUARD;
3779
ERR_FAIL_INDEX(p_screen_space_aa, SCREEN_SPACE_AA_MAX);
3780
if (screen_space_aa == p_screen_space_aa) {
3781
return;
3782
}
3783
screen_space_aa = p_screen_space_aa;
3784
RS::get_singleton()->viewport_set_screen_space_aa(viewport, RS::ViewportScreenSpaceAA(p_screen_space_aa));
3785
}
3786
3787
Viewport::ScreenSpaceAA Viewport::get_screen_space_aa() const {
3788
ERR_READ_THREAD_GUARD_V(SCREEN_SPACE_AA_DISABLED);
3789
return screen_space_aa;
3790
}
3791
3792
void Viewport::set_use_taa(bool p_use_taa) {
3793
ERR_MAIN_THREAD_GUARD;
3794
if (use_taa == p_use_taa) {
3795
return;
3796
}
3797
use_taa = p_use_taa;
3798
RS::get_singleton()->viewport_set_use_taa(viewport, p_use_taa);
3799
}
3800
3801
bool Viewport::is_using_taa() const {
3802
ERR_READ_THREAD_GUARD_V(false);
3803
return use_taa;
3804
}
3805
3806
void Viewport::set_use_debanding(bool p_use_debanding) {
3807
ERR_MAIN_THREAD_GUARD;
3808
if (use_debanding == p_use_debanding) {
3809
return;
3810
}
3811
use_debanding = p_use_debanding;
3812
RS::get_singleton()->viewport_set_use_debanding(viewport, p_use_debanding);
3813
}
3814
3815
bool Viewport::is_using_debanding() const {
3816
ERR_READ_THREAD_GUARD_V(false);
3817
return use_debanding;
3818
}
3819
3820
void Viewport::set_mesh_lod_threshold(float p_pixels) {
3821
ERR_MAIN_THREAD_GUARD;
3822
mesh_lod_threshold = p_pixels;
3823
RS::get_singleton()->viewport_set_mesh_lod_threshold(viewport, mesh_lod_threshold);
3824
}
3825
3826
float Viewport::get_mesh_lod_threshold() const {
3827
ERR_READ_THREAD_GUARD_V(0);
3828
return mesh_lod_threshold;
3829
}
3830
3831
void Viewport::set_use_occlusion_culling(bool p_use_occlusion_culling) {
3832
ERR_MAIN_THREAD_GUARD;
3833
if (use_occlusion_culling == p_use_occlusion_culling) {
3834
return;
3835
}
3836
3837
use_occlusion_culling = p_use_occlusion_culling;
3838
RS::get_singleton()->viewport_set_use_occlusion_culling(viewport, p_use_occlusion_culling);
3839
3840
notify_property_list_changed();
3841
}
3842
3843
bool Viewport::is_using_occlusion_culling() const {
3844
ERR_READ_THREAD_GUARD_V(false);
3845
return use_occlusion_culling;
3846
}
3847
3848
void Viewport::set_debug_draw(DebugDraw p_debug_draw) {
3849
ERR_MAIN_THREAD_GUARD;
3850
debug_draw = p_debug_draw;
3851
RS::get_singleton()->viewport_set_debug_draw(viewport, RS::ViewportDebugDraw(p_debug_draw));
3852
}
3853
3854
Viewport::DebugDraw Viewport::get_debug_draw() const {
3855
ERR_READ_THREAD_GUARD_V(DEBUG_DRAW_DISABLED);
3856
return debug_draw;
3857
}
3858
3859
int Viewport::get_render_info(RenderInfoType p_type, RenderInfo p_info) {
3860
ERR_READ_THREAD_GUARD_V(0);
3861
return RS::get_singleton()->viewport_get_render_info(viewport, RS::ViewportRenderInfoType(p_type), RS::ViewportRenderInfo(p_info));
3862
}
3863
3864
void Viewport::set_snap_controls_to_pixels(bool p_enable) {
3865
ERR_MAIN_THREAD_GUARD;
3866
snap_controls_to_pixels = p_enable;
3867
}
3868
3869
bool Viewport::is_snap_controls_to_pixels_enabled() const {
3870
ERR_READ_THREAD_GUARD_V(false);
3871
return snap_controls_to_pixels;
3872
}
3873
3874
void Viewport::set_snap_2d_transforms_to_pixel(bool p_enable) {
3875
ERR_MAIN_THREAD_GUARD;
3876
snap_2d_transforms_to_pixel = p_enable;
3877
RS::get_singleton()->viewport_set_snap_2d_transforms_to_pixel(viewport, snap_2d_transforms_to_pixel);
3878
}
3879
3880
bool Viewport::is_snap_2d_transforms_to_pixel_enabled() const {
3881
ERR_READ_THREAD_GUARD_V(false);
3882
return snap_2d_transforms_to_pixel;
3883
}
3884
3885
void Viewport::set_snap_2d_vertices_to_pixel(bool p_enable) {
3886
ERR_MAIN_THREAD_GUARD;
3887
snap_2d_vertices_to_pixel = p_enable;
3888
RS::get_singleton()->viewport_set_snap_2d_vertices_to_pixel(viewport, snap_2d_vertices_to_pixel);
3889
}
3890
3891
bool Viewport::is_snap_2d_vertices_to_pixel_enabled() const {
3892
ERR_READ_THREAD_GUARD_V(false);
3893
return snap_2d_vertices_to_pixel;
3894
}
3895
3896
bool Viewport::gui_is_dragging() const {
3897
ERR_READ_THREAD_GUARD_V(false);
3898
return get_section_root_viewport()->gui.global_dragging;
3899
}
3900
3901
bool Viewport::gui_is_drag_successful() const {
3902
ERR_READ_THREAD_GUARD_V(false);
3903
return gui.drag_successful;
3904
}
3905
3906
void Viewport::gui_cancel_drag() {
3907
ERR_MAIN_THREAD_GUARD;
3908
if (gui_is_dragging()) {
3909
_perform_drop();
3910
}
3911
}
3912
3913
void Viewport::set_input_as_handled() {
3914
ERR_MAIN_THREAD_GUARD;
3915
if (!handle_input_locally) {
3916
ERR_FAIL_COND(!is_inside_tree());
3917
Viewport *vp = this;
3918
while (true) {
3919
if (Object::cast_to<Window>(vp)) {
3920
break;
3921
}
3922
if (!vp->get_parent()) {
3923
break;
3924
}
3925
vp = vp->get_parent()->get_viewport();
3926
}
3927
if (vp != this) {
3928
vp->set_input_as_handled();
3929
return;
3930
}
3931
}
3932
3933
local_input_handled = true;
3934
}
3935
3936
bool Viewport::is_input_handled() const {
3937
ERR_READ_THREAD_GUARD_V(false);
3938
if (!handle_input_locally) {
3939
ERR_FAIL_COND_V(!is_inside_tree(), false);
3940
const Viewport *vp = this;
3941
while (true) {
3942
if (Object::cast_to<Window>(vp)) {
3943
break;
3944
}
3945
if (!vp->get_parent()) {
3946
break;
3947
}
3948
vp = vp->get_parent()->get_viewport();
3949
}
3950
if (vp != this) {
3951
return vp->is_input_handled();
3952
}
3953
}
3954
return local_input_handled;
3955
}
3956
3957
void Viewport::set_handle_input_locally(bool p_enable) {
3958
ERR_MAIN_THREAD_GUARD;
3959
handle_input_locally = p_enable;
3960
}
3961
3962
bool Viewport::is_handling_input_locally() const {
3963
ERR_READ_THREAD_GUARD_V(false);
3964
return handle_input_locally;
3965
}
3966
3967
void Viewport::set_default_canvas_item_texture_filter(DefaultCanvasItemTextureFilter p_filter) {
3968
ERR_MAIN_THREAD_GUARD;
3969
ERR_FAIL_INDEX(p_filter, DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_MAX);
3970
3971
if (default_canvas_item_texture_filter == p_filter) {
3972
return;
3973
}
3974
default_canvas_item_texture_filter = p_filter;
3975
switch (default_canvas_item_texture_filter) {
3976
case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST:
3977
RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
3978
break;
3979
case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR:
3980
RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR);
3981
break;
3982
case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS:
3983
RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS);
3984
break;
3985
case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS:
3986
RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
3987
break;
3988
default: {
3989
}
3990
}
3991
}
3992
3993
Viewport::DefaultCanvasItemTextureFilter Viewport::get_default_canvas_item_texture_filter() const {
3994
ERR_READ_THREAD_GUARD_V(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
3995
return default_canvas_item_texture_filter;
3996
}
3997
3998
void Viewport::set_default_canvas_item_texture_repeat(DefaultCanvasItemTextureRepeat p_repeat) {
3999
ERR_MAIN_THREAD_GUARD;
4000
ERR_FAIL_INDEX(p_repeat, DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX);
4001
4002
if (default_canvas_item_texture_repeat == p_repeat) {
4003
return;
4004
}
4005
4006
default_canvas_item_texture_repeat = p_repeat;
4007
4008
switch (default_canvas_item_texture_repeat) {
4009
case DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED:
4010
RS::get_singleton()->viewport_set_default_canvas_item_texture_repeat(viewport, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
4011
break;
4012
case DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_ENABLED:
4013
RS::get_singleton()->viewport_set_default_canvas_item_texture_repeat(viewport, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
4014
break;
4015
case DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MIRROR:
4016
RS::get_singleton()->viewport_set_default_canvas_item_texture_repeat(viewport, RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR);
4017
break;
4018
default: {
4019
}
4020
}
4021
}
4022
4023
Viewport::DefaultCanvasItemTextureRepeat Viewport::get_default_canvas_item_texture_repeat() const {
4024
ERR_READ_THREAD_GUARD_V(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
4025
return default_canvas_item_texture_repeat;
4026
}
4027
4028
void Viewport::set_vrs_mode(Viewport::VRSMode p_vrs_mode) {
4029
ERR_MAIN_THREAD_GUARD;
4030
// Note, set this even if not supported on this hardware, it will only be used if it is but we want to save the value as set by the user.
4031
vrs_mode = p_vrs_mode;
4032
4033
switch (p_vrs_mode) {
4034
case VRS_TEXTURE: {
4035
RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_TEXTURE);
4036
} break;
4037
case VRS_XR: {
4038
RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_XR);
4039
} break;
4040
default: {
4041
RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_DISABLED);
4042
} break;
4043
}
4044
4045
notify_property_list_changed();
4046
}
4047
4048
Viewport::VRSMode Viewport::get_vrs_mode() const {
4049
ERR_READ_THREAD_GUARD_V(VRS_DISABLED);
4050
return vrs_mode;
4051
}
4052
4053
void Viewport::set_vrs_update_mode(VRSUpdateMode p_vrs_update_mode) {
4054
ERR_MAIN_THREAD_GUARD;
4055
4056
vrs_update_mode = p_vrs_update_mode;
4057
switch (p_vrs_update_mode) {
4058
case VRS_UPDATE_ONCE: {
4059
RS::get_singleton()->viewport_set_vrs_update_mode(viewport, RS::VIEWPORT_VRS_UPDATE_ONCE);
4060
} break;
4061
case VRS_UPDATE_ALWAYS: {
4062
RS::get_singleton()->viewport_set_vrs_update_mode(viewport, RS::VIEWPORT_VRS_UPDATE_ALWAYS);
4063
} break;
4064
default: {
4065
RS::get_singleton()->viewport_set_vrs_update_mode(viewport, RS::VIEWPORT_VRS_UPDATE_DISABLED);
4066
} break;
4067
}
4068
}
4069
4070
Viewport::VRSUpdateMode Viewport::get_vrs_update_mode() const {
4071
ERR_READ_THREAD_GUARD_V(VRS_UPDATE_DISABLED);
4072
return vrs_update_mode;
4073
}
4074
4075
void Viewport::set_vrs_texture(Ref<Texture2D> p_texture) {
4076
ERR_MAIN_THREAD_GUARD;
4077
vrs_texture = p_texture;
4078
4079
// TODO need to add something here in case the RID changes
4080
RID tex = p_texture.is_valid() ? p_texture->get_rid() : RID();
4081
RS::get_singleton()->viewport_set_vrs_texture(viewport, tex);
4082
}
4083
4084
Ref<Texture2D> Viewport::get_vrs_texture() const {
4085
ERR_READ_THREAD_GUARD_V(Ref<Texture2D>());
4086
return vrs_texture;
4087
}
4088
4089
DisplayServer::WindowID Viewport::get_window_id() const {
4090
ERR_READ_THREAD_GUARD_V(DisplayServer::INVALID_WINDOW_ID);
4091
return DisplayServer::MAIN_WINDOW_ID;
4092
}
4093
4094
Viewport *Viewport::get_parent_viewport() const {
4095
ERR_READ_THREAD_GUARD_V(nullptr);
4096
ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
4097
if (!get_parent()) {
4098
return nullptr; //root viewport
4099
}
4100
4101
return get_parent()->get_viewport();
4102
}
4103
4104
void Viewport::set_embedding_subwindows(bool p_embed) {
4105
ERR_THREAD_GUARD;
4106
if (gui.embed_subwindows_hint == p_embed) {
4107
return;
4108
}
4109
4110
bool allow_change = true;
4111
4112
if (!is_inside_tree()) {
4113
// Change can happen since no child window is displayed.
4114
} else if (gui.embed_subwindows_hint) {
4115
if (!gui.sub_windows.is_empty()) {
4116
// Prevent change when this viewport has embedded windows.
4117
allow_change = false;
4118
}
4119
} else {
4120
Viewport *vp = this;
4121
while (true) {
4122
if (!vp->get_parent()) {
4123
// Root window reached.
4124
break;
4125
}
4126
vp = vp->get_parent()->get_viewport();
4127
if (vp->is_embedding_subwindows()) {
4128
for (int i = 0; i < vp->gui.sub_windows.size(); i++) {
4129
if (is_ancestor_of(vp->gui.sub_windows[i].window)) {
4130
// Prevent change when this viewport has child windows that are displayed in an ancestor viewport.
4131
allow_change = false;
4132
break;
4133
}
4134
}
4135
}
4136
}
4137
4138
if (allow_change) {
4139
Vector<DisplayServer::WindowID> wl = DisplayServer::get_singleton()->get_window_list();
4140
for (const DisplayServer::WindowID &window_id : wl) {
4141
const Window *w = Window::get_from_id(window_id);
4142
if (w && is_ancestor_of(w)) {
4143
// Prevent change when this viewport has child windows that are displayed as native windows.
4144
allow_change = false;
4145
break;
4146
}
4147
}
4148
}
4149
}
4150
4151
if (allow_change) {
4152
gui.embed_subwindows_hint = p_embed;
4153
} else {
4154
WARN_PRINT("Can't change \"gui_embed_subwindows\" while a child window is displayed. Consider hiding all child windows before changing this value.");
4155
}
4156
}
4157
4158
bool Viewport::is_embedding_subwindows() const {
4159
ERR_READ_THREAD_GUARD_V(false);
4160
return gui.embed_subwindows_hint;
4161
}
4162
4163
TypedArray<Window> Viewport::get_embedded_subwindows() const {
4164
TypedArray<Window> windows;
4165
for (int i = 0; i < gui.sub_windows.size(); i++) {
4166
windows.append(gui.sub_windows[i].window);
4167
}
4168
4169
return windows;
4170
}
4171
4172
void Viewport::set_drag_threshold(int p_threshold) {
4173
ERR_MAIN_THREAD_GUARD
4174
gui.drag_threshold = p_threshold;
4175
}
4176
4177
int Viewport::get_drag_threshold() const {
4178
ERR_READ_THREAD_GUARD_V(10);
4179
return gui.drag_threshold;
4180
}
4181
4182
void Viewport::subwindow_set_popup_safe_rect(Window *p_window, const Rect2i &p_rect) {
4183
int index = _sub_window_find(p_window);
4184
ERR_FAIL_COND(index == -1);
4185
4186
gui.sub_windows.write[index].parent_safe_rect = p_rect;
4187
}
4188
4189
Rect2i Viewport::subwindow_get_popup_safe_rect(Window *p_window) const {
4190
int index = _sub_window_find(p_window);
4191
// FIXME: Re-enable ERR_FAIL_COND after rewriting embedded window popup closing.
4192
// Currently it is expected, that index == -1 can happen.
4193
if (index == -1) {
4194
return Rect2i();
4195
}
4196
// ERR_FAIL_COND_V(index == -1, Rect2i());
4197
4198
return gui.sub_windows[index].parent_safe_rect;
4199
}
4200
4201
void Viewport::set_sdf_oversize(SDFOversize p_sdf_oversize) {
4202
ERR_MAIN_THREAD_GUARD;
4203
ERR_FAIL_INDEX(p_sdf_oversize, SDF_OVERSIZE_MAX);
4204
sdf_oversize = p_sdf_oversize;
4205
RS::get_singleton()->viewport_set_sdf_oversize_and_scale(viewport, RS::ViewportSDFOversize(sdf_oversize), RS::ViewportSDFScale(sdf_scale));
4206
}
4207
4208
Viewport::SDFOversize Viewport::get_sdf_oversize() const {
4209
ERR_READ_THREAD_GUARD_V(SDF_OVERSIZE_100_PERCENT);
4210
return sdf_oversize;
4211
}
4212
4213
void Viewport::set_sdf_scale(SDFScale p_sdf_scale) {
4214
ERR_MAIN_THREAD_GUARD;
4215
ERR_FAIL_INDEX(p_sdf_scale, SDF_SCALE_MAX);
4216
sdf_scale = p_sdf_scale;
4217
RS::get_singleton()->viewport_set_sdf_oversize_and_scale(viewport, RS::ViewportSDFOversize(sdf_oversize), RS::ViewportSDFScale(sdf_scale));
4218
}
4219
4220
Viewport::SDFScale Viewport::get_sdf_scale() const {
4221
ERR_READ_THREAD_GUARD_V(SDF_SCALE_100_PERCENT);
4222
return sdf_scale;
4223
}
4224
4225
Transform2D Viewport::get_screen_transform() const {
4226
ERR_READ_THREAD_GUARD_V(Transform2D());
4227
return get_screen_transform_internal();
4228
}
4229
4230
Transform2D Viewport::get_screen_transform_internal(bool p_absolute_position) const {
4231
ERR_READ_THREAD_GUARD_V(Transform2D());
4232
return get_final_transform();
4233
}
4234
4235
void Viewport::update_mouse_cursor_state() {
4236
// Updates need to happen in Window, because SubViewportContainers might be hidden behind other Controls.
4237
Window *base_window = get_base_window();
4238
if (base_window) {
4239
base_window->update_mouse_cursor_state();
4240
}
4241
}
4242
4243
void Viewport::set_canvas_cull_mask(uint32_t p_canvas_cull_mask) {
4244
ERR_MAIN_THREAD_GUARD;
4245
canvas_cull_mask = p_canvas_cull_mask;
4246
RenderingServer::get_singleton()->viewport_set_canvas_cull_mask(viewport, canvas_cull_mask);
4247
}
4248
4249
uint32_t Viewport::get_canvas_cull_mask() const {
4250
ERR_READ_THREAD_GUARD_V(0);
4251
return canvas_cull_mask;
4252
}
4253
4254
void Viewport::set_canvas_cull_mask_bit(uint32_t p_layer, bool p_enable) {
4255
ERR_MAIN_THREAD_GUARD;
4256
ERR_FAIL_UNSIGNED_INDEX(p_layer, 32);
4257
if (p_enable) {
4258
set_canvas_cull_mask(canvas_cull_mask | (1 << p_layer));
4259
} else {
4260
set_canvas_cull_mask(canvas_cull_mask & (~(1 << p_layer)));
4261
}
4262
}
4263
4264
bool Viewport::get_canvas_cull_mask_bit(uint32_t p_layer) const {
4265
ERR_READ_THREAD_GUARD_V(false);
4266
ERR_FAIL_UNSIGNED_INDEX_V(p_layer, 32, false);
4267
return (canvas_cull_mask & (1 << p_layer));
4268
}
4269
4270
#ifdef TOOLS_ENABLED
4271
bool Viewport::is_visible_subviewport() const {
4272
if (!is_sub_viewport()) {
4273
return true;
4274
}
4275
SubViewportContainer *container = Object::cast_to<SubViewportContainer>(get_parent());
4276
return container && container->is_visible_in_tree();
4277
}
4278
#endif // TOOLS_ENABLED
4279
4280
void Viewport::_update_audio_listener_2d() {
4281
if (AudioServer::get_singleton()) {
4282
AudioServer::get_singleton()->notify_listener_changed();
4283
}
4284
}
4285
4286
void Viewport::_audio_listener_2d_set(AudioListener2D *p_audio_listener) {
4287
if (audio_listener_2d == p_audio_listener) {
4288
return;
4289
} else if (audio_listener_2d) {
4290
audio_listener_2d->clear_current();
4291
}
4292
audio_listener_2d = p_audio_listener;
4293
}
4294
4295
void Viewport::_audio_listener_2d_remove(AudioListener2D *p_audio_listener) {
4296
if (audio_listener_2d == p_audio_listener) {
4297
audio_listener_2d = nullptr;
4298
}
4299
}
4300
4301
void Viewport::_camera_2d_set(Camera2D *p_camera_2d) {
4302
#if DEBUG_ENABLED
4303
if (is_camera_2d_override_enabled()) {
4304
camera_2d_override.set_overridden_camera(p_camera_2d);
4305
return;
4306
}
4307
#endif // DEBUG_ENABLED
4308
4309
camera_2d = p_camera_2d;
4310
}
4311
4312
#ifndef PHYSICS_2D_DISABLED
4313
void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paused_only, uint64_t p_frame_reference) {
4314
List<ObjectID> to_erase;
4315
List<ObjectID> to_mouse_exit;
4316
4317
for (const KeyValue<ObjectID, uint64_t> &E : physics_2d_mouseover) {
4318
if (!p_clean_all_frames && E.value == p_frame_reference) {
4319
continue;
4320
}
4321
4322
Object *o = ObjectDB::get_instance(E.key);
4323
if (o) {
4324
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
4325
if (co && co->is_inside_tree()) {
4326
if (p_clean_all_frames && p_paused_only && co->can_process()) {
4327
continue;
4328
}
4329
to_mouse_exit.push_back(E.key);
4330
}
4331
}
4332
to_erase.push_back(E.key);
4333
}
4334
4335
while (to_erase.size()) {
4336
physics_2d_mouseover.erase(to_erase.front()->get());
4337
to_erase.pop_front();
4338
}
4339
4340
// Per-shape.
4341
List<Pair<ObjectID, int>> shapes_to_erase;
4342
List<Pair<ObjectID, int>> shapes_to_mouse_exit;
4343
4344
for (KeyValue<Pair<ObjectID, int>, uint64_t> &E : physics_2d_shape_mouseover) {
4345
if (!p_clean_all_frames && E.value == p_frame_reference) {
4346
continue;
4347
}
4348
4349
Object *o = ObjectDB::get_instance(E.key.first);
4350
if (o) {
4351
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
4352
if (co && co->is_inside_tree()) {
4353
if (p_clean_all_frames && p_paused_only && co->can_process()) {
4354
continue;
4355
}
4356
shapes_to_mouse_exit.push_back(E.key);
4357
}
4358
}
4359
shapes_to_erase.push_back(E.key);
4360
}
4361
4362
while (shapes_to_erase.size()) {
4363
physics_2d_shape_mouseover.erase(shapes_to_erase.front()->get());
4364
shapes_to_erase.pop_front();
4365
}
4366
4367
while (to_mouse_exit.size()) {
4368
Object *o = ObjectDB::get_instance(to_mouse_exit.front()->get());
4369
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
4370
co->_mouse_exit();
4371
to_mouse_exit.pop_front();
4372
}
4373
4374
while (shapes_to_mouse_exit.size()) {
4375
Pair<ObjectID, int> e = shapes_to_mouse_exit.front()->get();
4376
Object *o = ObjectDB::get_instance(e.first);
4377
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
4378
co->_mouse_shape_exit(e.second);
4379
shapes_to_mouse_exit.pop_front();
4380
}
4381
}
4382
#endif // PHYSICS_2D_DISABLED
4383
4384
AudioListener2D *Viewport::get_audio_listener_2d() const {
4385
ERR_READ_THREAD_GUARD_V(nullptr);
4386
return audio_listener_2d;
4387
}
4388
4389
void Viewport::set_as_audio_listener_2d(bool p_enable) {
4390
ERR_MAIN_THREAD_GUARD;
4391
if (p_enable == is_audio_listener_2d_enabled) {
4392
return;
4393
}
4394
4395
is_audio_listener_2d_enabled = p_enable;
4396
_update_audio_listener_2d();
4397
}
4398
4399
bool Viewport::is_audio_listener_2d() const {
4400
ERR_READ_THREAD_GUARD_V(false);
4401
return is_audio_listener_2d_enabled;
4402
}
4403
4404
Camera2D *Viewport::get_camera_2d() const {
4405
ERR_READ_THREAD_GUARD_V(nullptr);
4406
return camera_2d;
4407
}
4408
4409
void Viewport::assign_next_enabled_camera_2d(const StringName &p_camera_group) {
4410
ERR_MAIN_THREAD_GUARD;
4411
Vector<Node *> camera_list = get_tree()->get_nodes_in_group(p_camera_group);
4412
4413
Camera2D *new_camera = nullptr;
4414
for (Node *E : camera_list) {
4415
Camera2D *cam = Object::cast_to<Camera2D>(E);
4416
if (!cam) {
4417
continue; // Non-camera node (e.g. ParallaxBackground).
4418
}
4419
4420
if (cam->is_enabled()) {
4421
new_camera = cam;
4422
break;
4423
}
4424
}
4425
4426
_camera_2d_set(new_camera);
4427
if (!camera_2d) {
4428
set_canvas_transform(Transform2D());
4429
}
4430
}
4431
4432
#if DEBUG_ENABLED
4433
void Viewport::enable_camera_2d_override(bool p_enable) {
4434
ERR_MAIN_THREAD_GUARD;
4435
4436
if (p_enable) {
4437
camera_2d_override.enable(this, camera_2d);
4438
} else {
4439
camera_2d_override.disable(camera_2d);
4440
}
4441
}
4442
4443
bool Viewport::is_camera_2d_override_enabled() const {
4444
ERR_READ_THREAD_GUARD_V(false);
4445
return camera_2d_override.is_enabled();
4446
}
4447
4448
Camera2D *Viewport::get_overridden_camera_2d() const {
4449
ERR_READ_THREAD_GUARD_V(nullptr);
4450
ERR_FAIL_COND_V(!camera_2d_override.is_enabled(), nullptr);
4451
return camera_2d_override.get_overridden_camera();
4452
}
4453
4454
Camera2D *Viewport::get_override_camera_2d() const {
4455
ERR_READ_THREAD_GUARD_V(nullptr);
4456
ERR_FAIL_COND_V(!camera_2d_override.is_enabled(), nullptr);
4457
return camera_2d_override.is_enabled() ? get_camera_2d() : nullptr;
4458
}
4459
#endif // DEBUG_ENABLED
4460
4461
#ifndef _3D_DISABLED
4462
AudioListener3D *Viewport::get_audio_listener_3d() const {
4463
ERR_READ_THREAD_GUARD_V(nullptr);
4464
return audio_listener_3d;
4465
}
4466
4467
void Viewport::set_as_audio_listener_3d(bool p_enable) {
4468
ERR_MAIN_THREAD_GUARD;
4469
if (p_enable == is_audio_listener_3d_enabled) {
4470
return;
4471
}
4472
4473
is_audio_listener_3d_enabled = p_enable;
4474
_update_audio_listener_3d();
4475
}
4476
4477
bool Viewport::is_audio_listener_3d() const {
4478
ERR_READ_THREAD_GUARD_V(false);
4479
return is_audio_listener_3d_enabled;
4480
}
4481
4482
void Viewport::_update_audio_listener_3d() {
4483
if (AudioServer::get_singleton()) {
4484
AudioServer::get_singleton()->notify_listener_changed();
4485
}
4486
}
4487
4488
void Viewport::_listener_transform_3d_changed_notify() {
4489
}
4490
4491
void Viewport::_audio_listener_3d_set(AudioListener3D *p_listener) {
4492
if (audio_listener_3d == p_listener) {
4493
return;
4494
}
4495
4496
audio_listener_3d = p_listener;
4497
4498
_update_audio_listener_3d();
4499
_listener_transform_3d_changed_notify();
4500
}
4501
4502
bool Viewport::_audio_listener_3d_add(AudioListener3D *p_listener) {
4503
audio_listener_3d_set.insert(p_listener);
4504
return audio_listener_3d_set.size() == 1;
4505
}
4506
4507
void Viewport::_audio_listener_3d_remove(AudioListener3D *p_listener) {
4508
audio_listener_3d_set.erase(p_listener);
4509
if (audio_listener_3d == p_listener) {
4510
audio_listener_3d = nullptr;
4511
}
4512
}
4513
4514
void Viewport::_audio_listener_3d_make_next_current(AudioListener3D *p_exclude) {
4515
if (audio_listener_3d_set.size() > 0) {
4516
for (AudioListener3D *E : audio_listener_3d_set) {
4517
if (p_exclude == E) {
4518
continue;
4519
}
4520
if (!E->is_inside_tree()) {
4521
continue;
4522
}
4523
if (audio_listener_3d != nullptr) {
4524
return;
4525
}
4526
4527
E->make_current();
4528
}
4529
} else {
4530
// Attempt to reset listener to the camera position.
4531
if (camera_3d != nullptr) {
4532
_update_audio_listener_3d();
4533
_camera_3d_transform_changed_notify();
4534
}
4535
}
4536
}
4537
4538
#ifndef PHYSICS_3D_DISABLED
4539
void Viewport::_collision_object_3d_input_event(CollisionObject3D *p_object, Camera3D *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape) {
4540
Transform3D object_transform = p_object->get_global_transform();
4541
Transform3D camera_transform = p_camera->get_global_transform();
4542
ObjectID id = p_object->get_instance_id();
4543
4544
// Avoid sending the fake event unnecessarily if nothing really changed in the context.
4545
if (object_transform == physics_last_object_transform && camera_transform == physics_last_camera_transform && physics_last_id == id) {
4546
Ref<InputEventMouseMotion> mm = p_input_event;
4547
if (mm.is_valid() && mm->get_device() == InputEvent::DEVICE_ID_INTERNAL) {
4548
return; // Discarded.
4549
}
4550
}
4551
p_object->_input_event_call(camera_3d, p_input_event, p_pos, p_normal, p_shape);
4552
physics_last_object_transform = object_transform;
4553
physics_last_camera_transform = camera_transform;
4554
physics_last_id = id;
4555
}
4556
#endif // PHYSICS_3D_DISABLED
4557
4558
Camera3D *Viewport::get_camera_3d() const {
4559
ERR_READ_THREAD_GUARD_V(nullptr);
4560
return camera_3d;
4561
}
4562
4563
void Viewport::_camera_3d_transform_changed_notify() {
4564
}
4565
4566
void Viewport::_camera_3d_set(Camera3D *p_camera) {
4567
if (camera_3d == p_camera) {
4568
return;
4569
}
4570
4571
#if DEBUG_ENABLED
4572
if (is_camera_3d_override_enabled()) {
4573
camera_3d_override.set_overridden_camera(p_camera);
4574
return;
4575
}
4576
#endif // DEBUG_ENABLED
4577
4578
if (camera_3d) {
4579
camera_3d->notification(Camera3D::NOTIFICATION_LOST_CURRENT);
4580
}
4581
4582
camera_3d = p_camera;
4583
4584
if (camera_3d) {
4585
RenderingServer::get_singleton()->viewport_attach_camera(viewport, camera_3d->get_camera());
4586
} else {
4587
RenderingServer::get_singleton()->viewport_attach_camera(viewport, RID());
4588
}
4589
4590
if (camera_3d) {
4591
camera_3d->notification(Camera3D::NOTIFICATION_BECAME_CURRENT);
4592
}
4593
4594
_update_audio_listener_3d();
4595
_camera_3d_transform_changed_notify();
4596
}
4597
4598
bool Viewport::_camera_3d_add(Camera3D *p_camera) {
4599
camera_3d_set.insert(p_camera);
4600
return camera_3d_set.size() == 1;
4601
}
4602
4603
void Viewport::_camera_3d_remove(Camera3D *p_camera) {
4604
camera_3d_set.erase(p_camera);
4605
if (camera_3d == p_camera) {
4606
_camera_3d_set(nullptr);
4607
}
4608
}
4609
4610
void Viewport::_camera_3d_make_next_current(Camera3D *p_exclude) {
4611
for (Camera3D *E : camera_3d_set) {
4612
if (p_exclude == E) {
4613
continue;
4614
}
4615
if (!E->is_inside_tree()) {
4616
continue;
4617
}
4618
if (camera_3d != nullptr) {
4619
return;
4620
}
4621
4622
E->make_current();
4623
}
4624
}
4625
4626
#if DEBUG_ENABLED
4627
void Viewport::enable_camera_3d_override(bool p_enable) {
4628
ERR_MAIN_THREAD_GUARD;
4629
4630
if (p_enable) {
4631
camera_3d_override.enable(this, camera_3d);
4632
} else {
4633
camera_3d_override.disable(camera_3d);
4634
}
4635
}
4636
4637
bool Viewport::is_camera_3d_override_enabled() const {
4638
ERR_READ_THREAD_GUARD_V(false);
4639
return camera_3d_override.is_enabled();
4640
}
4641
4642
Camera3D *Viewport::get_overridden_camera_3d() const {
4643
ERR_READ_THREAD_GUARD_V(nullptr);
4644
ERR_FAIL_COND_V(!camera_3d_override.is_enabled(), nullptr);
4645
return camera_3d_override.get_overridden_camera();
4646
}
4647
4648
Camera3D *Viewport::get_override_camera_3d() const {
4649
ERR_READ_THREAD_GUARD_V(nullptr);
4650
ERR_FAIL_COND_V(!camera_3d_override.is_enabled(), nullptr);
4651
return get_camera_3d();
4652
}
4653
#endif //DEBUG_ENABLED
4654
4655
void Viewport::set_disable_3d(bool p_disable) {
4656
ERR_MAIN_THREAD_GUARD;
4657
disable_3d = p_disable;
4658
RenderingServer::get_singleton()->viewport_set_disable_3d(viewport, disable_3d);
4659
}
4660
4661
bool Viewport::is_3d_disabled() const {
4662
ERR_READ_THREAD_GUARD_V(false);
4663
return disable_3d;
4664
}
4665
4666
Ref<World3D> Viewport::get_world_3d() const {
4667
ERR_READ_THREAD_GUARD_V(Ref<World3D>());
4668
return world_3d;
4669
}
4670
4671
Ref<World3D> Viewport::find_world_3d() const {
4672
ERR_READ_THREAD_GUARD_V(Ref<World3D>());
4673
if (own_world_3d.is_valid()) {
4674
return own_world_3d;
4675
} else if (world_3d.is_valid()) {
4676
return world_3d;
4677
} else if (parent) {
4678
return parent->find_world_3d();
4679
} else {
4680
return Ref<World3D>();
4681
}
4682
}
4683
4684
void Viewport::set_world_3d(const Ref<World3D> &p_world_3d) {
4685
ERR_MAIN_THREAD_GUARD;
4686
if (world_3d == p_world_3d) {
4687
return;
4688
}
4689
4690
if (is_inside_tree()) {
4691
_propagate_exit_world_3d(this);
4692
}
4693
4694
if (own_world_3d.is_valid() && world_3d.is_valid()) {
4695
world_3d->disconnect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
4696
}
4697
4698
world_3d = p_world_3d;
4699
4700
if (own_world_3d.is_valid()) {
4701
if (world_3d.is_valid()) {
4702
own_world_3d = world_3d->duplicate();
4703
world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
4704
} else {
4705
own_world_3d.instantiate();
4706
}
4707
}
4708
4709
if (is_inside_tree()) {
4710
_propagate_enter_world_3d(this);
4711
}
4712
4713
if (is_inside_tree()) {
4714
RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario());
4715
}
4716
4717
_update_audio_listener_3d();
4718
}
4719
4720
void Viewport::_own_world_3d_changed() {
4721
ERR_FAIL_COND(world_3d.is_null());
4722
ERR_FAIL_COND(own_world_3d.is_null());
4723
4724
if (is_inside_tree()) {
4725
_propagate_exit_world_3d(this);
4726
}
4727
4728
own_world_3d = world_3d->duplicate();
4729
4730
if (is_inside_tree()) {
4731
_propagate_enter_world_3d(this);
4732
}
4733
4734
if (is_inside_tree()) {
4735
RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario());
4736
}
4737
4738
_update_audio_listener_3d();
4739
}
4740
4741
void Viewport::set_use_own_world_3d(bool p_use_own_world_3d) {
4742
ERR_MAIN_THREAD_GUARD;
4743
if (p_use_own_world_3d == own_world_3d.is_valid()) {
4744
return;
4745
}
4746
4747
if (is_inside_tree()) {
4748
_propagate_exit_world_3d(this);
4749
}
4750
4751
if (p_use_own_world_3d) {
4752
if (world_3d.is_valid()) {
4753
own_world_3d = world_3d->duplicate();
4754
world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
4755
} else {
4756
own_world_3d.instantiate();
4757
}
4758
} else {
4759
own_world_3d = Ref<World3D>();
4760
if (world_3d.is_valid()) {
4761
world_3d->disconnect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
4762
}
4763
}
4764
4765
if (is_inside_tree()) {
4766
_propagate_enter_world_3d(this);
4767
}
4768
4769
if (is_inside_tree()) {
4770
RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario());
4771
}
4772
4773
_update_audio_listener_3d();
4774
}
4775
4776
bool Viewport::is_using_own_world_3d() const {
4777
ERR_READ_THREAD_GUARD_V(false);
4778
return own_world_3d.is_valid();
4779
}
4780
4781
void Viewport::_propagate_enter_world_3d(Node *p_node) {
4782
if (p_node != this) {
4783
if (!p_node->is_inside_tree()) { //may not have entered scene yet
4784
return;
4785
}
4786
4787
if (Object::cast_to<Node3D>(p_node) || Object::cast_to<WorldEnvironment>(p_node)) {
4788
p_node->notification(Node3D::NOTIFICATION_ENTER_WORLD);
4789
} else {
4790
Viewport *v = Object::cast_to<Viewport>(p_node);
4791
if (v) {
4792
if (v->world_3d.is_valid() || v->own_world_3d.is_valid()) {
4793
return;
4794
}
4795
}
4796
}
4797
}
4798
4799
for (int i = 0; i < p_node->get_child_count(); i++) {
4800
_propagate_enter_world_3d(p_node->get_child(i));
4801
}
4802
}
4803
4804
void Viewport::_propagate_exit_world_3d(Node *p_node) {
4805
if (p_node != this) {
4806
if (!p_node->is_inside_tree()) { //may have exited scene already
4807
return;
4808
}
4809
4810
if (Object::cast_to<Node3D>(p_node) || Object::cast_to<WorldEnvironment>(p_node)) {
4811
p_node->notification(Node3D::NOTIFICATION_EXIT_WORLD);
4812
} else {
4813
Viewport *v = Object::cast_to<Viewport>(p_node);
4814
if (v) {
4815
if (v->world_3d.is_valid() || v->own_world_3d.is_valid()) {
4816
return;
4817
}
4818
}
4819
}
4820
}
4821
4822
for (int i = 0; i < p_node->get_child_count(); i++) {
4823
_propagate_exit_world_3d(p_node->get_child(i));
4824
}
4825
}
4826
4827
#ifndef XR_DISABLED
4828
void Viewport::set_use_xr(bool p_use_xr) {
4829
ERR_MAIN_THREAD_GUARD;
4830
if (use_xr != p_use_xr) {
4831
use_xr = p_use_xr;
4832
4833
RS::get_singleton()->viewport_set_use_xr(viewport, use_xr);
4834
4835
if (!use_xr) {
4836
// Set viewport to previous size when exiting XR.
4837
if (size_allocated) {
4838
RS::get_singleton()->viewport_set_size(viewport, size.width, size.height);
4839
} else {
4840
RS::get_singleton()->viewport_set_size(viewport, 0, 0);
4841
}
4842
4843
// Reset render target override textures.
4844
RID rt = RS::get_singleton()->viewport_get_render_target(viewport);
4845
RSG::texture_storage->render_target_set_override(rt, RID(), RID(), RID(), RID());
4846
}
4847
}
4848
}
4849
4850
bool Viewport::is_using_xr() {
4851
ERR_READ_THREAD_GUARD_V(false);
4852
return use_xr;
4853
}
4854
#endif // XR_DISABLED
4855
4856
void Viewport::set_scaling_3d_mode(Scaling3DMode p_scaling_3d_mode) {
4857
ERR_MAIN_THREAD_GUARD;
4858
if (scaling_3d_mode == p_scaling_3d_mode) {
4859
return;
4860
}
4861
4862
scaling_3d_mode = p_scaling_3d_mode;
4863
RS::get_singleton()->viewport_set_scaling_3d_mode(viewport, (RS::ViewportScaling3DMode)(int)p_scaling_3d_mode);
4864
}
4865
4866
Viewport::Scaling3DMode Viewport::get_scaling_3d_mode() const {
4867
ERR_READ_THREAD_GUARD_V(SCALING_3D_MODE_BILINEAR);
4868
return scaling_3d_mode;
4869
}
4870
4871
void Viewport::set_scaling_3d_scale(float p_scaling_3d_scale) {
4872
ERR_MAIN_THREAD_GUARD;
4873
// Clamp to reasonable values that are actually useful.
4874
// Values above 2.0 don't serve a practical purpose since the viewport
4875
// isn't displayed with mipmaps.
4876
scaling_3d_scale = CLAMP(p_scaling_3d_scale, 0.1, 2.0);
4877
4878
RS::get_singleton()->viewport_set_scaling_3d_scale(viewport, scaling_3d_scale);
4879
}
4880
4881
float Viewport::get_scaling_3d_scale() const {
4882
ERR_READ_THREAD_GUARD_V(0);
4883
return scaling_3d_scale;
4884
}
4885
4886
void Viewport::set_fsr_sharpness(float p_fsr_sharpness) {
4887
ERR_MAIN_THREAD_GUARD;
4888
if (fsr_sharpness == p_fsr_sharpness) {
4889
return;
4890
}
4891
4892
if (p_fsr_sharpness < 0.0f) {
4893
p_fsr_sharpness = 0.0f;
4894
}
4895
4896
fsr_sharpness = p_fsr_sharpness;
4897
RS::get_singleton()->viewport_set_fsr_sharpness(viewport, p_fsr_sharpness);
4898
}
4899
4900
float Viewport::get_fsr_sharpness() const {
4901
ERR_READ_THREAD_GUARD_V(0);
4902
return fsr_sharpness;
4903
}
4904
4905
void Viewport::set_texture_mipmap_bias(float p_texture_mipmap_bias) {
4906
ERR_MAIN_THREAD_GUARD;
4907
if (texture_mipmap_bias == p_texture_mipmap_bias) {
4908
return;
4909
}
4910
4911
texture_mipmap_bias = p_texture_mipmap_bias;
4912
RS::get_singleton()->viewport_set_texture_mipmap_bias(viewport, p_texture_mipmap_bias);
4913
}
4914
4915
float Viewport::get_texture_mipmap_bias() const {
4916
ERR_READ_THREAD_GUARD_V(0);
4917
return texture_mipmap_bias;
4918
}
4919
4920
void Viewport::set_anisotropic_filtering_level(AnisotropicFiltering p_anisotropic_filtering_level) {
4921
ERR_MAIN_THREAD_GUARD;
4922
if (anisotropic_filtering_level == p_anisotropic_filtering_level) {
4923
return;
4924
}
4925
4926
anisotropic_filtering_level = p_anisotropic_filtering_level;
4927
RS::get_singleton()->viewport_set_anisotropic_filtering_level(viewport, (RS::ViewportAnisotropicFiltering)(int)p_anisotropic_filtering_level);
4928
}
4929
4930
Viewport::AnisotropicFiltering Viewport::get_anisotropic_filtering_level() const {
4931
ERR_READ_THREAD_GUARD_V(ANISOTROPY_DISABLED);
4932
return anisotropic_filtering_level;
4933
}
4934
4935
#endif // _3D_DISABLED
4936
4937
void Viewport::_propagate_world_2d_changed(Node *p_node) {
4938
if (p_node != this) {
4939
if (Object::cast_to<CanvasItem>(p_node)) {
4940
p_node->notification(CanvasItem::NOTIFICATION_WORLD_2D_CHANGED);
4941
} else {
4942
Viewport *v = Object::cast_to<Viewport>(p_node);
4943
if (v) {
4944
if (v->world_2d.is_valid()) {
4945
return;
4946
}
4947
}
4948
}
4949
}
4950
4951
for (int i = 0; i < p_node->get_child_count(); ++i) {
4952
_propagate_world_2d_changed(p_node->get_child(i));
4953
}
4954
}
4955
4956
void Viewport::_bind_methods() {
4957
ClassDB::bind_method(D_METHOD("set_world_2d", "world_2d"), &Viewport::set_world_2d);
4958
ClassDB::bind_method(D_METHOD("get_world_2d"), &Viewport::get_world_2d);
4959
ClassDB::bind_method(D_METHOD("find_world_2d"), &Viewport::find_world_2d);
4960
4961
ClassDB::bind_method(D_METHOD("set_canvas_transform", "xform"), &Viewport::set_canvas_transform);
4962
ClassDB::bind_method(D_METHOD("get_canvas_transform"), &Viewport::get_canvas_transform);
4963
4964
ClassDB::bind_method(D_METHOD("set_global_canvas_transform", "xform"), &Viewport::set_global_canvas_transform);
4965
ClassDB::bind_method(D_METHOD("get_global_canvas_transform"), &Viewport::get_global_canvas_transform);
4966
ClassDB::bind_method(D_METHOD("get_stretch_transform"), &Viewport::get_stretch_transform);
4967
ClassDB::bind_method(D_METHOD("get_final_transform"), &Viewport::get_final_transform);
4968
ClassDB::bind_method(D_METHOD("get_screen_transform"), &Viewport::get_screen_transform);
4969
4970
ClassDB::bind_method(D_METHOD("get_visible_rect"), &Viewport::get_visible_rect);
4971
ClassDB::bind_method(D_METHOD("set_transparent_background", "enable"), &Viewport::set_transparent_background);
4972
ClassDB::bind_method(D_METHOD("has_transparent_background"), &Viewport::has_transparent_background);
4973
ClassDB::bind_method(D_METHOD("set_use_hdr_2d", "enable"), &Viewport::set_use_hdr_2d);
4974
ClassDB::bind_method(D_METHOD("is_using_hdr_2d"), &Viewport::is_using_hdr_2d);
4975
4976
ClassDB::bind_method(D_METHOD("set_msaa_2d", "msaa"), &Viewport::set_msaa_2d);
4977
ClassDB::bind_method(D_METHOD("get_msaa_2d"), &Viewport::get_msaa_2d);
4978
4979
ClassDB::bind_method(D_METHOD("set_msaa_3d", "msaa"), &Viewport::set_msaa_3d);
4980
ClassDB::bind_method(D_METHOD("get_msaa_3d"), &Viewport::get_msaa_3d);
4981
4982
ClassDB::bind_method(D_METHOD("set_screen_space_aa", "screen_space_aa"), &Viewport::set_screen_space_aa);
4983
ClassDB::bind_method(D_METHOD("get_screen_space_aa"), &Viewport::get_screen_space_aa);
4984
4985
ClassDB::bind_method(D_METHOD("set_use_taa", "enable"), &Viewport::set_use_taa);
4986
ClassDB::bind_method(D_METHOD("is_using_taa"), &Viewport::is_using_taa);
4987
4988
ClassDB::bind_method(D_METHOD("set_use_debanding", "enable"), &Viewport::set_use_debanding);
4989
ClassDB::bind_method(D_METHOD("is_using_debanding"), &Viewport::is_using_debanding);
4990
4991
ClassDB::bind_method(D_METHOD("set_use_occlusion_culling", "enable"), &Viewport::set_use_occlusion_culling);
4992
ClassDB::bind_method(D_METHOD("is_using_occlusion_culling"), &Viewport::is_using_occlusion_culling);
4993
4994
ClassDB::bind_method(D_METHOD("set_debug_draw", "debug_draw"), &Viewport::set_debug_draw);
4995
ClassDB::bind_method(D_METHOD("get_debug_draw"), &Viewport::get_debug_draw);
4996
4997
ClassDB::bind_method(D_METHOD("set_use_oversampling", "enable"), &Viewport::set_use_oversampling);
4998
ClassDB::bind_method(D_METHOD("is_using_oversampling"), &Viewport::is_using_oversampling);
4999
5000
ClassDB::bind_method(D_METHOD("set_oversampling_override", "oversampling"), &Viewport::set_oversampling_override);
5001
ClassDB::bind_method(D_METHOD("get_oversampling_override"), &Viewport::get_oversampling_override);
5002
5003
ClassDB::bind_method(D_METHOD("get_oversampling"), &Viewport::get_oversampling);
5004
5005
ClassDB::bind_method(D_METHOD("get_render_info", "type", "info"), &Viewport::get_render_info);
5006
5007
ClassDB::bind_method(D_METHOD("get_texture"), &Viewport::get_texture);
5008
5009
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
5010
ClassDB::bind_method(D_METHOD("set_physics_object_picking", "enable"), &Viewport::set_physics_object_picking);
5011
ClassDB::bind_method(D_METHOD("get_physics_object_picking"), &Viewport::get_physics_object_picking);
5012
ClassDB::bind_method(D_METHOD("set_physics_object_picking_sort", "enable"), &Viewport::set_physics_object_picking_sort);
5013
ClassDB::bind_method(D_METHOD("get_physics_object_picking_sort"), &Viewport::get_physics_object_picking_sort);
5014
ClassDB::bind_method(D_METHOD("set_physics_object_picking_first_only", "enable"), &Viewport::set_physics_object_picking_first_only);
5015
ClassDB::bind_method(D_METHOD("get_physics_object_picking_first_only"), &Viewport::get_physics_object_picking_first_only);
5016
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
5017
5018
ClassDB::bind_method(D_METHOD("get_viewport_rid"), &Viewport::get_viewport_rid);
5019
ClassDB::bind_method(D_METHOD("push_text_input", "text"), &Viewport::push_text_input);
5020
ClassDB::bind_method(D_METHOD("push_input", "event", "in_local_coords"), &Viewport::push_input, DEFVAL(false));
5021
#ifndef DISABLE_DEPRECATED
5022
ClassDB::bind_method(D_METHOD("push_unhandled_input", "event", "in_local_coords"), &Viewport::push_unhandled_input, DEFVAL(false));
5023
#endif // DISABLE_DEPRECATED
5024
ClassDB::bind_method(D_METHOD("notify_mouse_entered"), &Viewport::notify_mouse_entered);
5025
ClassDB::bind_method(D_METHOD("notify_mouse_exited"), &Viewport::notify_mouse_exited);
5026
5027
ClassDB::bind_method(D_METHOD("get_mouse_position"), &Viewport::get_mouse_position);
5028
ClassDB::bind_method(D_METHOD("warp_mouse", "position"), &Viewport::warp_mouse);
5029
ClassDB::bind_method(D_METHOD("update_mouse_cursor_state"), &Viewport::update_mouse_cursor_state);
5030
5031
ClassDB::bind_method(D_METHOD("gui_cancel_drag"), &Viewport::gui_cancel_drag);
5032
ClassDB::bind_method(D_METHOD("gui_get_drag_data"), &Viewport::gui_get_drag_data);
5033
ClassDB::bind_method(D_METHOD("gui_get_drag_description"), &Viewport::gui_get_drag_description);
5034
ClassDB::bind_method(D_METHOD("gui_set_drag_description", "description"), &Viewport::gui_set_drag_description);
5035
ClassDB::bind_method(D_METHOD("gui_is_dragging"), &Viewport::gui_is_dragging);
5036
ClassDB::bind_method(D_METHOD("gui_is_drag_successful"), &Viewport::gui_is_drag_successful);
5037
5038
ClassDB::bind_method(D_METHOD("gui_release_focus"), &Viewport::gui_release_focus);
5039
ClassDB::bind_method(D_METHOD("gui_get_focus_owner"), &Viewport::gui_get_focus_owner);
5040
ClassDB::bind_method(D_METHOD("gui_get_hovered_control"), &Viewport::gui_get_hovered_control);
5041
5042
ClassDB::bind_method(D_METHOD("set_disable_input", "disable"), &Viewport::set_disable_input);
5043
ClassDB::bind_method(D_METHOD("is_input_disabled"), &Viewport::is_input_disabled);
5044
5045
ClassDB::bind_method(D_METHOD("_gui_remove_focus_for_window"), &Viewport::_gui_remove_focus_for_window);
5046
5047
ClassDB::bind_method(D_METHOD("set_positional_shadow_atlas_size", "size"), &Viewport::set_positional_shadow_atlas_size);
5048
ClassDB::bind_method(D_METHOD("get_positional_shadow_atlas_size"), &Viewport::get_positional_shadow_atlas_size);
5049
5050
ClassDB::bind_method(D_METHOD("set_positional_shadow_atlas_16_bits", "enable"), &Viewport::set_positional_shadow_atlas_16_bits);
5051
ClassDB::bind_method(D_METHOD("get_positional_shadow_atlas_16_bits"), &Viewport::get_positional_shadow_atlas_16_bits);
5052
5053
ClassDB::bind_method(D_METHOD("set_snap_controls_to_pixels", "enabled"), &Viewport::set_snap_controls_to_pixels);
5054
ClassDB::bind_method(D_METHOD("is_snap_controls_to_pixels_enabled"), &Viewport::is_snap_controls_to_pixels_enabled);
5055
5056
ClassDB::bind_method(D_METHOD("set_snap_2d_transforms_to_pixel", "enabled"), &Viewport::set_snap_2d_transforms_to_pixel);
5057
ClassDB::bind_method(D_METHOD("is_snap_2d_transforms_to_pixel_enabled"), &Viewport::is_snap_2d_transforms_to_pixel_enabled);
5058
5059
ClassDB::bind_method(D_METHOD("set_snap_2d_vertices_to_pixel", "enabled"), &Viewport::set_snap_2d_vertices_to_pixel);
5060
ClassDB::bind_method(D_METHOD("is_snap_2d_vertices_to_pixel_enabled"), &Viewport::is_snap_2d_vertices_to_pixel_enabled);
5061
5062
ClassDB::bind_method(D_METHOD("set_positional_shadow_atlas_quadrant_subdiv", "quadrant", "subdiv"), &Viewport::set_positional_shadow_atlas_quadrant_subdiv);
5063
ClassDB::bind_method(D_METHOD("get_positional_shadow_atlas_quadrant_subdiv", "quadrant"), &Viewport::get_positional_shadow_atlas_quadrant_subdiv);
5064
5065
ClassDB::bind_method(D_METHOD("set_input_as_handled"), &Viewport::set_input_as_handled);
5066
ClassDB::bind_method(D_METHOD("is_input_handled"), &Viewport::is_input_handled);
5067
5068
ClassDB::bind_method(D_METHOD("set_handle_input_locally", "enable"), &Viewport::set_handle_input_locally);
5069
ClassDB::bind_method(D_METHOD("is_handling_input_locally"), &Viewport::is_handling_input_locally);
5070
5071
ClassDB::bind_method(D_METHOD("set_default_canvas_item_texture_filter", "mode"), &Viewport::set_default_canvas_item_texture_filter);
5072
ClassDB::bind_method(D_METHOD("get_default_canvas_item_texture_filter"), &Viewport::get_default_canvas_item_texture_filter);
5073
5074
ClassDB::bind_method(D_METHOD("set_embedding_subwindows", "enable"), &Viewport::set_embedding_subwindows);
5075
ClassDB::bind_method(D_METHOD("is_embedding_subwindows"), &Viewport::is_embedding_subwindows);
5076
ClassDB::bind_method(D_METHOD("get_embedded_subwindows"), &Viewport::get_embedded_subwindows);
5077
5078
ClassDB::bind_method(D_METHOD("set_drag_threshold", "threshold"), &Viewport::set_drag_threshold);
5079
ClassDB::bind_method(D_METHOD("get_drag_threshold"), &Viewport::get_drag_threshold);
5080
5081
ClassDB::bind_method(D_METHOD("set_canvas_cull_mask", "mask"), &Viewport::set_canvas_cull_mask);
5082
ClassDB::bind_method(D_METHOD("get_canvas_cull_mask"), &Viewport::get_canvas_cull_mask);
5083
5084
ClassDB::bind_method(D_METHOD("set_canvas_cull_mask_bit", "layer", "enable"), &Viewport::set_canvas_cull_mask_bit);
5085
ClassDB::bind_method(D_METHOD("get_canvas_cull_mask_bit", "layer"), &Viewport::get_canvas_cull_mask_bit);
5086
5087
ClassDB::bind_method(D_METHOD("set_default_canvas_item_texture_repeat", "mode"), &Viewport::set_default_canvas_item_texture_repeat);
5088
ClassDB::bind_method(D_METHOD("get_default_canvas_item_texture_repeat"), &Viewport::get_default_canvas_item_texture_repeat);
5089
5090
ClassDB::bind_method(D_METHOD("set_sdf_oversize", "oversize"), &Viewport::set_sdf_oversize);
5091
ClassDB::bind_method(D_METHOD("get_sdf_oversize"), &Viewport::get_sdf_oversize);
5092
5093
ClassDB::bind_method(D_METHOD("set_sdf_scale", "scale"), &Viewport::set_sdf_scale);
5094
ClassDB::bind_method(D_METHOD("get_sdf_scale"), &Viewport::get_sdf_scale);
5095
5096
ClassDB::bind_method(D_METHOD("set_mesh_lod_threshold", "pixels"), &Viewport::set_mesh_lod_threshold);
5097
ClassDB::bind_method(D_METHOD("get_mesh_lod_threshold"), &Viewport::get_mesh_lod_threshold);
5098
5099
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
5100
ClassDB::bind_method(D_METHOD("_process_picking"), &Viewport::_process_picking);
5101
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
5102
5103
ClassDB::bind_method(D_METHOD("set_as_audio_listener_2d", "enable"), &Viewport::set_as_audio_listener_2d);
5104
ClassDB::bind_method(D_METHOD("is_audio_listener_2d"), &Viewport::is_audio_listener_2d);
5105
ClassDB::bind_method(D_METHOD("get_audio_listener_2d"), &Viewport::get_audio_listener_2d);
5106
ClassDB::bind_method(D_METHOD("get_camera_2d"), &Viewport::get_camera_2d);
5107
5108
#ifndef _3D_DISABLED
5109
ClassDB::bind_method(D_METHOD("set_world_3d", "world_3d"), &Viewport::set_world_3d);
5110
ClassDB::bind_method(D_METHOD("get_world_3d"), &Viewport::get_world_3d);
5111
ClassDB::bind_method(D_METHOD("find_world_3d"), &Viewport::find_world_3d);
5112
5113
ClassDB::bind_method(D_METHOD("set_use_own_world_3d", "enable"), &Viewport::set_use_own_world_3d);
5114
ClassDB::bind_method(D_METHOD("is_using_own_world_3d"), &Viewport::is_using_own_world_3d);
5115
5116
ClassDB::bind_method(D_METHOD("get_audio_listener_3d"), &Viewport::get_audio_listener_3d);
5117
ClassDB::bind_method(D_METHOD("get_camera_3d"), &Viewport::get_camera_3d);
5118
ClassDB::bind_method(D_METHOD("set_as_audio_listener_3d", "enable"), &Viewport::set_as_audio_listener_3d);
5119
ClassDB::bind_method(D_METHOD("is_audio_listener_3d"), &Viewport::is_audio_listener_3d);
5120
5121
ClassDB::bind_method(D_METHOD("set_disable_3d", "disable"), &Viewport::set_disable_3d);
5122
ClassDB::bind_method(D_METHOD("is_3d_disabled"), &Viewport::is_3d_disabled);
5123
5124
#ifndef XR_DISABLED
5125
ClassDB::bind_method(D_METHOD("set_use_xr", "use"), &Viewport::set_use_xr);
5126
ClassDB::bind_method(D_METHOD("is_using_xr"), &Viewport::is_using_xr);
5127
#endif // XR_DISABLED
5128
5129
ClassDB::bind_method(D_METHOD("set_scaling_3d_mode", "scaling_3d_mode"), &Viewport::set_scaling_3d_mode);
5130
ClassDB::bind_method(D_METHOD("get_scaling_3d_mode"), &Viewport::get_scaling_3d_mode);
5131
5132
ClassDB::bind_method(D_METHOD("set_scaling_3d_scale", "scale"), &Viewport::set_scaling_3d_scale);
5133
ClassDB::bind_method(D_METHOD("get_scaling_3d_scale"), &Viewport::get_scaling_3d_scale);
5134
5135
ClassDB::bind_method(D_METHOD("set_fsr_sharpness", "fsr_sharpness"), &Viewport::set_fsr_sharpness);
5136
ClassDB::bind_method(D_METHOD("get_fsr_sharpness"), &Viewport::get_fsr_sharpness);
5137
5138
ClassDB::bind_method(D_METHOD("set_texture_mipmap_bias", "texture_mipmap_bias"), &Viewport::set_texture_mipmap_bias);
5139
ClassDB::bind_method(D_METHOD("get_texture_mipmap_bias"), &Viewport::get_texture_mipmap_bias);
5140
5141
ClassDB::bind_method(D_METHOD("set_anisotropic_filtering_level", "anisotropic_filtering_level"), &Viewport::set_anisotropic_filtering_level);
5142
ClassDB::bind_method(D_METHOD("get_anisotropic_filtering_level"), &Viewport::get_anisotropic_filtering_level);
5143
5144
ClassDB::bind_method(D_METHOD("set_vrs_mode", "mode"), &Viewport::set_vrs_mode);
5145
ClassDB::bind_method(D_METHOD("get_vrs_mode"), &Viewport::get_vrs_mode);
5146
5147
ClassDB::bind_method(D_METHOD("set_vrs_update_mode", "mode"), &Viewport::set_vrs_update_mode);
5148
ClassDB::bind_method(D_METHOD("get_vrs_update_mode"), &Viewport::get_vrs_update_mode);
5149
5150
ClassDB::bind_method(D_METHOD("set_vrs_texture", "texture"), &Viewport::set_vrs_texture);
5151
ClassDB::bind_method(D_METHOD("get_vrs_texture"), &Viewport::get_vrs_texture);
5152
5153
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled");
5154
#ifndef XR_DISABLED
5155
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_xr"), "set_use_xr", "is_using_xr");
5156
#endif // XR_DISABLED
5157
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d");
5158
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, World3D::get_class_static()), "set_world_3d", "get_world_3d");
5159
#endif // _3D_DISABLED
5160
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_2d", PROPERTY_HINT_RESOURCE_TYPE, World2D::get_class_static(), PROPERTY_USAGE_NONE), "set_world_2d", "get_world_2d");
5161
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transparent_bg"), "set_transparent_background", "has_transparent_background");
5162
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handle_input_locally"), "set_handle_input_locally", "is_handling_input_locally");
5163
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_transforms_to_pixel"), "set_snap_2d_transforms_to_pixel", "is_snap_2d_transforms_to_pixel_enabled");
5164
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_vertices_to_pixel"), "set_snap_2d_vertices_to_pixel", "is_snap_2d_vertices_to_pixel_enabled");
5165
ADD_GROUP("Rendering", "");
5166
ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_2d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa_2d", "get_msaa_2d");
5167
ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_3d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa_3d", "get_msaa_3d");
5168
ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast),SMAA (Average)"), "set_screen_space_aa", "get_screen_space_aa");
5169
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_taa"), "set_use_taa", "is_using_taa");
5170
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding");
5171
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_occlusion_culling"), "set_use_occlusion_culling", "is_using_occlusion_culling");
5172
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mesh_lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_mesh_lod_threshold", "get_mesh_lod_threshold");
5173
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Lighting,Overdraw,Wireframe,Normal Buffer,VoxelGI Albedo,VoxelGI Lighting,VoxelGI Emission,Shadow Atlas,Directional Shadow Map,Scene Luminance,SSAO,SSIL,Directional Shadow Splits,Decal Atlas,SDFGI Cascades,SDFGI Probes,VoxelGI/SDFGI Buffer,Disable Mesh LOD,OmniLight3D Cluster,SpotLight3D Cluster,Decal Cluster,ReflectionProbe Cluster,Occlusion Culling Buffer,Motion Vectors,Internal Buffer"), "set_debug_draw", "get_debug_draw");
5174
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr_2d"), "set_use_hdr_2d", "is_using_hdr_2d");
5175
5176
#ifndef _3D_DISABLED
5177
ADD_GROUP("Scaling 3D", "");
5178
ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast),FSR 2.2 (Slow),MetalFX (Spatial),MetalFX (Temporal)"), "set_scaling_3d_mode", "get_scaling_3d_mode");
5179
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scaling_3d_scale", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), "set_scaling_3d_scale", "get_scaling_3d_scale");
5180
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.001"), "set_texture_mipmap_bias", "get_texture_mipmap_bias");
5181
ADD_PROPERTY(PropertyInfo(Variant::INT, "anisotropic_filtering_level", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Faster),4× (Fast),8× (Average),16x (Slow)")), "set_anisotropic_filtering_level", "get_anisotropic_filtering_level");
5182
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), "set_fsr_sharpness", "get_fsr_sharpness");
5183
ADD_GROUP("Variable Rate Shading", "vrs_");
5184
ADD_PROPERTY(PropertyInfo(Variant::INT, "vrs_mode", PROPERTY_HINT_ENUM, "Disabled,Texture,XR"), "set_vrs_mode", "get_vrs_mode");
5185
ADD_PROPERTY(PropertyInfo(Variant::INT, "vrs_update_mode", PROPERTY_HINT_ENUM, "Disabled,Once,Always"), "set_vrs_update_mode", "get_vrs_update_mode");
5186
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "vrs_texture", PROPERTY_HINT_RESOURCE_TYPE, Texture2D::get_class_static()), "set_vrs_texture", "get_vrs_texture");
5187
#endif
5188
ADD_GROUP("Canvas Items", "canvas_item_");
5189
ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Linear Mipmap,Nearest Mipmap"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter");
5190
ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirror"), "set_default_canvas_item_texture_repeat", "get_default_canvas_item_texture_repeat");
5191
ADD_GROUP("Audio Listener", "audio_listener_");
5192
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_2d"), "set_as_audio_listener_2d", "is_audio_listener_2d");
5193
#ifndef _3D_DISABLED
5194
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_3d"), "set_as_audio_listener_3d", "is_audio_listener_3d");
5195
#endif // _3D_DISABLED
5196
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
5197
ADD_GROUP("Physics", "physics_");
5198
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "physics_object_picking"), "set_physics_object_picking", "get_physics_object_picking");
5199
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "physics_object_picking_sort"), "set_physics_object_picking_sort", "get_physics_object_picking_sort");
5200
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "physics_object_picking_first_only"), "set_physics_object_picking_first_only", "get_physics_object_picking_first_only");
5201
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
5202
ADD_GROUP("GUI", "gui_");
5203
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_disable_input"), "set_disable_input", "is_input_disabled");
5204
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_snap_controls_to_pixels"), "set_snap_controls_to_pixels", "is_snap_controls_to_pixels_enabled");
5205
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_embed_subwindows"), "set_embedding_subwindows", "is_embedding_subwindows");
5206
ADD_PROPERTY(PropertyInfo(Variant::INT, "gui_drag_threshold"), "set_drag_threshold", "get_drag_threshold");
5207
ADD_GROUP("SDF", "sdf_");
5208
ADD_PROPERTY(PropertyInfo(Variant::INT, "sdf_oversize", PROPERTY_HINT_ENUM, "100%,120%,150%,200%"), "set_sdf_oversize", "get_sdf_oversize");
5209
ADD_PROPERTY(PropertyInfo(Variant::INT, "sdf_scale", PROPERTY_HINT_ENUM, "100%,50%,25%"), "set_sdf_scale", "get_sdf_scale");
5210
ADD_GROUP("Positional Shadow Atlas", "positional_shadow_atlas_");
5211
ADD_PROPERTY(PropertyInfo(Variant::INT, "positional_shadow_atlas_size"), "set_positional_shadow_atlas_size", "get_positional_shadow_atlas_size");
5212
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "positional_shadow_atlas_16_bits"), "set_positional_shadow_atlas_16_bits", "get_positional_shadow_atlas_16_bits");
5213
ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_0", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 0);
5214
ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_1", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 1);
5215
ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_2", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 2);
5216
ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_3", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 3);
5217
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "canvas_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_canvas_transform", "get_canvas_transform");
5218
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_canvas_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_canvas_transform", "get_global_canvas_transform");
5219
ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_cull_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_canvas_cull_mask", "get_canvas_cull_mask");
5220
5221
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "oversampling"), "set_use_oversampling", "is_using_oversampling");
5222
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling_override", PROPERTY_HINT_RANGE, "0,16,0.0001,or_greater"), "set_oversampling_override", "get_oversampling_override");
5223
5224
ADD_SIGNAL(MethodInfo("size_changed"));
5225
ADD_SIGNAL(MethodInfo("gui_focus_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, Control::get_class_static())));
5226
5227
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED);
5228
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_1);
5229
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_4);
5230
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_16);
5231
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_64);
5232
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_256);
5233
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_1024);
5234
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_MAX);
5235
5236
BIND_ENUM_CONSTANT(SCALING_3D_MODE_BILINEAR);
5237
BIND_ENUM_CONSTANT(SCALING_3D_MODE_FSR);
5238
BIND_ENUM_CONSTANT(SCALING_3D_MODE_FSR2);
5239
BIND_ENUM_CONSTANT(SCALING_3D_MODE_METALFX_SPATIAL);
5240
BIND_ENUM_CONSTANT(SCALING_3D_MODE_METALFX_TEMPORAL);
5241
BIND_ENUM_CONSTANT(SCALING_3D_MODE_MAX);
5242
5243
BIND_ENUM_CONSTANT(MSAA_DISABLED);
5244
BIND_ENUM_CONSTANT(MSAA_2X);
5245
BIND_ENUM_CONSTANT(MSAA_4X);
5246
BIND_ENUM_CONSTANT(MSAA_8X);
5247
BIND_ENUM_CONSTANT(MSAA_MAX);
5248
5249
BIND_ENUM_CONSTANT(ANISOTROPY_DISABLED);
5250
BIND_ENUM_CONSTANT(ANISOTROPY_2X);
5251
BIND_ENUM_CONSTANT(ANISOTROPY_4X);
5252
BIND_ENUM_CONSTANT(ANISOTROPY_8X);
5253
BIND_ENUM_CONSTANT(ANISOTROPY_16X);
5254
BIND_ENUM_CONSTANT(ANISOTROPY_MAX);
5255
5256
BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_DISABLED);
5257
BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_FXAA);
5258
BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_SMAA);
5259
BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_MAX);
5260
5261
BIND_ENUM_CONSTANT(RENDER_INFO_OBJECTS_IN_FRAME);
5262
BIND_ENUM_CONSTANT(RENDER_INFO_PRIMITIVES_IN_FRAME);
5263
BIND_ENUM_CONSTANT(RENDER_INFO_DRAW_CALLS_IN_FRAME);
5264
BIND_ENUM_CONSTANT(RENDER_INFO_MAX);
5265
5266
BIND_ENUM_CONSTANT(RENDER_INFO_TYPE_VISIBLE);
5267
BIND_ENUM_CONSTANT(RENDER_INFO_TYPE_SHADOW);
5268
BIND_ENUM_CONSTANT(RENDER_INFO_TYPE_CANVAS);
5269
BIND_ENUM_CONSTANT(RENDER_INFO_TYPE_MAX);
5270
5271
BIND_ENUM_CONSTANT(DEBUG_DRAW_DISABLED);
5272
BIND_ENUM_CONSTANT(DEBUG_DRAW_UNSHADED);
5273
BIND_ENUM_CONSTANT(DEBUG_DRAW_LIGHTING);
5274
BIND_ENUM_CONSTANT(DEBUG_DRAW_OVERDRAW);
5275
BIND_ENUM_CONSTANT(DEBUG_DRAW_WIREFRAME);
5276
BIND_ENUM_CONSTANT(DEBUG_DRAW_NORMAL_BUFFER);
5277
BIND_ENUM_CONSTANT(DEBUG_DRAW_VOXEL_GI_ALBEDO);
5278
BIND_ENUM_CONSTANT(DEBUG_DRAW_VOXEL_GI_LIGHTING);
5279
BIND_ENUM_CONSTANT(DEBUG_DRAW_VOXEL_GI_EMISSION);
5280
BIND_ENUM_CONSTANT(DEBUG_DRAW_SHADOW_ATLAS);
5281
BIND_ENUM_CONSTANT(DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS);
5282
BIND_ENUM_CONSTANT(DEBUG_DRAW_SCENE_LUMINANCE);
5283
BIND_ENUM_CONSTANT(DEBUG_DRAW_SSAO);
5284
BIND_ENUM_CONSTANT(DEBUG_DRAW_SSIL);
5285
BIND_ENUM_CONSTANT(DEBUG_DRAW_PSSM_SPLITS);
5286
BIND_ENUM_CONSTANT(DEBUG_DRAW_DECAL_ATLAS);
5287
BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI);
5288
BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI_PROBES);
5289
BIND_ENUM_CONSTANT(DEBUG_DRAW_GI_BUFFER);
5290
BIND_ENUM_CONSTANT(DEBUG_DRAW_DISABLE_LOD);
5291
BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_OMNI_LIGHTS);
5292
BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_SPOT_LIGHTS);
5293
BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_DECALS);
5294
BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_REFLECTION_PROBES);
5295
BIND_ENUM_CONSTANT(DEBUG_DRAW_OCCLUDERS)
5296
BIND_ENUM_CONSTANT(DEBUG_DRAW_MOTION_VECTORS)
5297
BIND_ENUM_CONSTANT(DEBUG_DRAW_INTERNAL_BUFFER);
5298
5299
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
5300
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR);
5301
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS);
5302
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
5303
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_MAX);
5304
5305
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
5306
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
5307
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MIRROR);
5308
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX);
5309
5310
BIND_ENUM_CONSTANT(SDF_OVERSIZE_100_PERCENT);
5311
BIND_ENUM_CONSTANT(SDF_OVERSIZE_120_PERCENT);
5312
BIND_ENUM_CONSTANT(SDF_OVERSIZE_150_PERCENT);
5313
BIND_ENUM_CONSTANT(SDF_OVERSIZE_200_PERCENT);
5314
BIND_ENUM_CONSTANT(SDF_OVERSIZE_MAX);
5315
5316
BIND_ENUM_CONSTANT(SDF_SCALE_100_PERCENT);
5317
BIND_ENUM_CONSTANT(SDF_SCALE_50_PERCENT);
5318
BIND_ENUM_CONSTANT(SDF_SCALE_25_PERCENT);
5319
BIND_ENUM_CONSTANT(SDF_SCALE_MAX);
5320
5321
BIND_ENUM_CONSTANT(VRS_DISABLED);
5322
BIND_ENUM_CONSTANT(VRS_TEXTURE);
5323
BIND_ENUM_CONSTANT(VRS_XR);
5324
BIND_ENUM_CONSTANT(VRS_MAX);
5325
5326
BIND_ENUM_CONSTANT(VRS_UPDATE_DISABLED);
5327
BIND_ENUM_CONSTANT(VRS_UPDATE_ONCE);
5328
BIND_ENUM_CONSTANT(VRS_UPDATE_ALWAYS);
5329
BIND_ENUM_CONSTANT(VRS_UPDATE_MAX);
5330
}
5331
5332
void Viewport::_validate_property(PropertyInfo &p_property) const {
5333
if (!Engine::get_singleton()->is_editor_hint()) {
5334
return;
5335
}
5336
if (vrs_mode != VRS_TEXTURE && (p_property.name == "vrs_texture")) {
5337
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
5338
}
5339
5340
if (vrs_mode == VRS_DISABLED && (p_property.name == "vrs_update_mode")) {
5341
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
5342
}
5343
}
5344
5345
Viewport::Viewport() {
5346
world_2d.instantiate();
5347
world_2d->register_viewport(this);
5348
5349
viewport = RenderingServer::get_singleton()->viewport_create();
5350
texture_rid = RenderingServer::get_singleton()->viewport_get_texture(viewport);
5351
5352
default_texture.instantiate();
5353
default_texture->vp = this;
5354
viewport_textures.insert(default_texture.ptr());
5355
default_texture->proxy = RS::get_singleton()->texture_proxy_create(texture_rid);
5356
5357
canvas_layers.insert(nullptr); // This eases picking code (interpreted as the canvas of the Viewport).
5358
5359
set_positional_shadow_atlas_size(positional_shadow_atlas_size);
5360
5361
for (int i = 0; i < 4; i++) {
5362
positional_shadow_atlas_quadrant_subdiv[i] = SHADOW_ATLAS_QUADRANT_SUBDIV_MAX;
5363
}
5364
set_positional_shadow_atlas_quadrant_subdiv(0, SHADOW_ATLAS_QUADRANT_SUBDIV_4);
5365
set_positional_shadow_atlas_quadrant_subdiv(1, SHADOW_ATLAS_QUADRANT_SUBDIV_4);
5366
set_positional_shadow_atlas_quadrant_subdiv(2, SHADOW_ATLAS_QUADRANT_SUBDIV_16);
5367
set_positional_shadow_atlas_quadrant_subdiv(3, SHADOW_ATLAS_QUADRANT_SUBDIV_64);
5368
5369
set_mesh_lod_threshold(mesh_lod_threshold);
5370
5371
String id = itos(get_instance_id());
5372
input_group = "_vp_input" + id;
5373
unhandled_input_group = "_vp_unhandled_input" + id;
5374
shortcut_input_group = "_vp_shortcut_input" + id;
5375
unhandled_key_input_group = "_vp_unhandled_key_input" + id;
5376
5377
// Window tooltip.
5378
gui.tooltip_delay = GLOBAL_GET("gui/timers/tooltip_delay_sec");
5379
5380
#ifndef _3D_DISABLED
5381
set_scaling_3d_mode((Viewport::Scaling3DMode)(int)GLOBAL_GET("rendering/scaling_3d/mode"));
5382
set_scaling_3d_scale(GLOBAL_GET("rendering/scaling_3d/scale"));
5383
set_fsr_sharpness((float)GLOBAL_GET("rendering/scaling_3d/fsr_sharpness"));
5384
set_texture_mipmap_bias((float)GLOBAL_GET("rendering/textures/default_filters/texture_mipmap_bias"));
5385
set_anisotropic_filtering_level((Viewport::AnisotropicFiltering)(int)GLOBAL_GET("rendering/textures/default_filters/anisotropic_filtering_level"));
5386
#endif // _3D_DISABLED
5387
5388
set_sdf_oversize(sdf_oversize); // Set to server.
5389
5390
// Physics interpolation mode for viewports is a special case.
5391
// Typically viewports will be housed within Controls,
5392
// and Controls default to PHYSICS_INTERPOLATION_MODE_OFF.
5393
// Viewports can thus inherit physics interpolation OFF, which is unexpected.
5394
// Setting to ON allows each viewport to have a fresh interpolation state.
5395
set_physics_interpolation_mode(Node::PHYSICS_INTERPOLATION_MODE_ON);
5396
5397
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &Viewport::_on_settings_changed));
5398
}
5399
5400
Viewport::~Viewport() {
5401
// Erase itself from viewport textures.
5402
for (ViewportTexture *E : viewport_textures) {
5403
E->vp = nullptr;
5404
}
5405
if (world_2d.is_valid()) {
5406
world_2d->remove_viewport(this);
5407
}
5408
ERR_FAIL_NULL(RenderingServer::get_singleton());
5409
RenderingServer::get_singleton()->free_rid(viewport);
5410
}
5411
5412
/////////////////////////////////
5413
5414
void SubViewport::set_size(const Size2i &p_size) {
5415
ERR_MAIN_THREAD_GUARD;
5416
_internal_set_size(p_size);
5417
}
5418
5419
void SubViewport::set_size_force(const Size2i &p_size) {
5420
ERR_MAIN_THREAD_GUARD;
5421
// Use only for setting the size from the parent SubViewportContainer with enabled stretch mode.
5422
// Don't expose function to scripting.
5423
_internal_set_size(p_size, true);
5424
}
5425
5426
void SubViewport::_internal_set_size(const Size2i &p_size, bool p_force) {
5427
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
5428
if (!p_force && c && c->is_stretch_enabled()) {
5429
#ifdef DEBUG_ENABLED
5430
WARN_PRINT("Can't change the size of a `SubViewport` with a `SubViewportContainer` parent that has `stretch` enabled. Set `SubViewportContainer.stretch` to `false` to allow changing the size manually.");
5431
#endif // DEBUG_ENABLED
5432
return;
5433
}
5434
5435
_set_size(p_size, _get_size_2d_override(), true);
5436
5437
if (c) {
5438
c->update_minimum_size();
5439
c->queue_redraw();
5440
}
5441
}
5442
5443
Size2i SubViewport::get_size() const {
5444
ERR_READ_THREAD_GUARD_V(Size2());
5445
return _get_size();
5446
}
5447
5448
void SubViewport::set_size_2d_override(const Size2i &p_size) {
5449
ERR_MAIN_THREAD_GUARD;
5450
_set_size(_get_size(), p_size, true);
5451
}
5452
5453
Size2i SubViewport::get_size_2d_override() const {
5454
ERR_READ_THREAD_GUARD_V(Size2i());
5455
// Rounding will cause offset issues with the
5456
// exact positioning of subwindows, but changing the
5457
// type of size_2d_override would break compatibility.
5458
return Size2i((_get_size_2d_override() + Size2(0.5, 0.5)).floor());
5459
}
5460
5461
void SubViewport::set_size_2d_override_stretch(bool p_enable) {
5462
ERR_MAIN_THREAD_GUARD;
5463
if (p_enable == size_2d_override_stretch) {
5464
return;
5465
}
5466
5467
size_2d_override_stretch = p_enable;
5468
_set_size(_get_size(), _get_size_2d_override(), true);
5469
}
5470
5471
bool SubViewport::is_size_2d_override_stretch_enabled() const {
5472
ERR_READ_THREAD_GUARD_V(false);
5473
return size_2d_override_stretch;
5474
}
5475
5476
void SubViewport::set_update_mode(UpdateMode p_mode) {
5477
ERR_MAIN_THREAD_GUARD;
5478
update_mode = p_mode;
5479
RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::ViewportUpdateMode(p_mode));
5480
}
5481
5482
SubViewport::UpdateMode SubViewport::get_update_mode() const {
5483
ERR_READ_THREAD_GUARD_V(UPDATE_DISABLED);
5484
return update_mode;
5485
}
5486
5487
void SubViewport::set_clear_mode(ClearMode p_mode) {
5488
ERR_MAIN_THREAD_GUARD;
5489
clear_mode = p_mode;
5490
RS::get_singleton()->viewport_set_clear_mode(get_viewport_rid(), RS::ViewportClearMode(p_mode));
5491
}
5492
5493
SubViewport::ClearMode SubViewport::get_clear_mode() const {
5494
ERR_READ_THREAD_GUARD_V(CLEAR_MODE_ALWAYS);
5495
return clear_mode;
5496
}
5497
5498
DisplayServer::WindowID SubViewport::get_window_id() const {
5499
ERR_READ_THREAD_GUARD_V(DisplayServer::INVALID_WINDOW_ID);
5500
return DisplayServer::INVALID_WINDOW_ID;
5501
}
5502
5503
Transform2D SubViewport::get_screen_transform_internal(bool p_absolute_position) const {
5504
ERR_READ_THREAD_GUARD_V(Transform2D());
5505
Transform2D container_transform;
5506
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
5507
if (c) {
5508
if (c->is_stretch_enabled()) {
5509
container_transform.scale(Vector2(c->get_stretch_shrink(), c->get_stretch_shrink()));
5510
}
5511
container_transform = c->get_viewport()->get_screen_transform_internal(p_absolute_position) * c->get_global_transform_with_canvas() * container_transform;
5512
} else {
5513
WARN_PRINT_ONCE("SubViewport is not a child of a SubViewportContainer. get_screen_transform doesn't return the actual screen position.");
5514
}
5515
return container_transform * get_final_transform();
5516
}
5517
5518
Transform2D SubViewport::get_popup_base_transform() const {
5519
ERR_READ_THREAD_GUARD_V(Transform2D());
5520
if (is_embedding_subwindows()) {
5521
return Transform2D();
5522
}
5523
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
5524
if (!c) {
5525
return get_final_transform();
5526
}
5527
Transform2D container_transform;
5528
if (c->is_stretch_enabled()) {
5529
container_transform.scale(Vector2(c->get_stretch_shrink(), c->get_stretch_shrink()));
5530
}
5531
return c->get_screen_transform() * container_transform * get_final_transform();
5532
}
5533
5534
Viewport *SubViewport::get_section_root_viewport() const {
5535
if (Object::cast_to<SubViewportContainer>(get_parent()) && get_parent()->get_viewport()) {
5536
return get_parent()->get_viewport()->get_section_root_viewport();
5537
}
5538
SubViewport *vp = const_cast<SubViewport *>(this);
5539
return vp;
5540
}
5541
5542
bool SubViewport::is_attached_in_viewport() const {
5543
return Object::cast_to<SubViewportContainer>(get_parent());
5544
}
5545
5546
void SubViewport::_notification(int p_what) {
5547
ERR_MAIN_THREAD_GUARD;
5548
switch (p_what) {
5549
case NOTIFICATION_ENTER_TREE: {
5550
RS::get_singleton()->viewport_set_active(get_viewport_rid(), true);
5551
5552
SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
5553
if (parent_svc) {
5554
parent_svc->recalc_force_viewport_sizes();
5555
}
5556
} break;
5557
5558
case NOTIFICATION_EXIT_TREE: {
5559
RS::get_singleton()->viewport_set_active(get_viewport_rid(), false);
5560
} break;
5561
}
5562
}
5563
5564
void SubViewport::_bind_methods() {
5565
ClassDB::bind_method(D_METHOD("set_size", "size"), &SubViewport::set_size);
5566
ClassDB::bind_method(D_METHOD("get_size"), &SubViewport::get_size);
5567
5568
ClassDB::bind_method(D_METHOD("set_size_2d_override", "size"), &SubViewport::set_size_2d_override);
5569
ClassDB::bind_method(D_METHOD("get_size_2d_override"), &SubViewport::get_size_2d_override);
5570
5571
ClassDB::bind_method(D_METHOD("set_size_2d_override_stretch", "enable"), &SubViewport::set_size_2d_override_stretch);
5572
ClassDB::bind_method(D_METHOD("is_size_2d_override_stretch_enabled"), &SubViewport::is_size_2d_override_stretch_enabled);
5573
5574
ClassDB::bind_method(D_METHOD("set_update_mode", "mode"), &SubViewport::set_update_mode);
5575
ClassDB::bind_method(D_METHOD("get_update_mode"), &SubViewport::get_update_mode);
5576
5577
ClassDB::bind_method(D_METHOD("set_clear_mode", "mode"), &SubViewport::set_clear_mode);
5578
ClassDB::bind_method(D_METHOD("get_clear_mode"), &SubViewport::get_clear_mode);
5579
5580
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size");
5581
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size_2d_override", PROPERTY_HINT_NONE, "suffix:px"), "set_size_2d_override", "get_size_2d_override");
5582
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "size_2d_override_stretch"), "set_size_2d_override_stretch", "is_size_2d_override_stretch_enabled");
5583
ADD_GROUP("Render Target", "render_target_");
5584
ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_clear_mode", PROPERTY_HINT_ENUM, "Always,Never,Next Frame"), "set_clear_mode", "get_clear_mode");
5585
ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_update_mode", PROPERTY_HINT_ENUM, "Disabled,Once,When Visible,When Parent Visible,Always"), "set_update_mode", "get_update_mode");
5586
5587
BIND_ENUM_CONSTANT(CLEAR_MODE_ALWAYS);
5588
BIND_ENUM_CONSTANT(CLEAR_MODE_NEVER);
5589
BIND_ENUM_CONSTANT(CLEAR_MODE_ONCE);
5590
5591
BIND_ENUM_CONSTANT(UPDATE_DISABLED);
5592
BIND_ENUM_CONSTANT(UPDATE_ONCE);
5593
BIND_ENUM_CONSTANT(UPDATE_WHEN_VISIBLE);
5594
BIND_ENUM_CONSTANT(UPDATE_WHEN_PARENT_VISIBLE);
5595
BIND_ENUM_CONSTANT(UPDATE_ALWAYS);
5596
}
5597
5598
void SubViewport::_validate_property(PropertyInfo &p_property) const {
5599
if (!Engine::get_singleton()->is_editor_hint()) {
5600
return;
5601
}
5602
if (p_property.name == "size") {
5603
SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
5604
if (parent_svc && parent_svc->is_stretch_enabled()) {
5605
p_property.usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY;
5606
} else {
5607
p_property.usage = PROPERTY_USAGE_DEFAULT;
5608
}
5609
}
5610
}
5611
5612
SubViewport::SubViewport() {
5613
RS::get_singleton()->viewport_set_size(get_viewport_rid(), get_size().width, get_size().height);
5614
}
5615
5616
/////////////////////////////////
5617
5618
#if DEBUG_ENABLED
5619
template <class T>
5620
bool Viewport::CameraOverride<T>::is_enabled() const {
5621
return enabled;
5622
}
5623
5624
template <class T>
5625
void Viewport::CameraOverride<T>::enable(Viewport *p_viewport, const T *p_current_camera) {
5626
if (enabled) {
5627
return;
5628
}
5629
5630
T *override_camera = memnew(T);
5631
override_camera->set_name(vformat("Override%s", T ::get_class_static()));
5632
p_viewport->add_child(override_camera, false, Node::INTERNAL_MODE_BACK);
5633
5634
override_camera->make_current();
5635
set_overridden_camera(p_current_camera);
5636
5637
// Call to make the override camera current must happen before we enable the override to prevent the override mechanism from kicking in.
5638
enabled = true;
5639
}
5640
5641
template <class T>
5642
void Viewport::CameraOverride<T>::disable(T *p_current_camera) {
5643
if (!enabled) {
5644
return;
5645
}
5646
5647
// Call to make the overridden camera current must happen after we disable the override to prevent the override mechanism from kicking in.
5648
enabled = false;
5649
5650
T *overridden_camera = get_overridden_camera();
5651
if (overridden_camera) {
5652
overridden_camera->make_current();
5653
} else {
5654
p_current_camera->clear_current();
5655
}
5656
5657
p_current_camera->queue_free();
5658
overridden_camera_id = ObjectID();
5659
}
5660
5661
template <class T>
5662
void Viewport::CameraOverride<T>::set_overridden_camera(const T *p_camera) {
5663
overridden_camera_id = p_camera ? p_camera->get_instance_id() : ObjectID();
5664
}
5665
5666
template <class T>
5667
T *Viewport::CameraOverride<T>::get_overridden_camera() const {
5668
return ObjectDB::get_instance<T>(overridden_camera_id);
5669
}
5670
5671
// Explicit template instantiation to allow template definitions inside cpp file
5672
// and prevent instantiation using other than the desired camera types.
5673
template class Viewport::CameraOverride<Camera2D>;
5674
#ifndef _3D_DISABLED
5675
template class Viewport::CameraOverride<Camera3D>;
5676
#endif // _3D_DISABLED
5677
#endif // DEBUG_ENABLED
5678
5679