diff options
author | RĂ©mi Verschelde <rverschelde@gmail.com> | 2020-03-26 17:23:11 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-26 17:23:11 +0100 |
commit | 5f1107aa30295e686be6f41cb6d17fc2cff1e036 (patch) | |
tree | 7bce4c680e6686c9d29be8b479be5b39205ce7a3 /scene/main | |
parent | a2da99f40cf2123c0906c734a2eb01e9b65a45a2 (diff) | |
parent | be07f86f85ab70a48b310b42faa64e72a74ca694 (diff) |
Merge pull request #37317 from akien-mga/display-server-rebased
Separate DisplayServer from OS and add multiple windows support
Diffstat (limited to 'scene/main')
-rw-r--r-- | scene/main/node.cpp | 4 | ||||
-rw-r--r-- | scene/main/node.h | 17 | ||||
-rw-r--r-- | scene/main/scene_tree.cpp | 383 | ||||
-rw-r--r-- | scene/main/scene_tree.h | 56 | ||||
-rw-r--r-- | scene/main/viewport.cpp | 1615 | ||||
-rw-r--r-- | scene/main/viewport.h | 213 | ||||
-rw-r--r-- | scene/main/window.cpp | 1406 | ||||
-rw-r--r-- | scene/main/window.h | 266 |
8 files changed, 2848 insertions, 1112 deletions
diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 973dff07d2..b882b9ead6 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2950,9 +2950,9 @@ void Node::_bind_methods() { BIND_CONSTANT(NOTIFICATION_WM_MOUSE_EXIT); BIND_CONSTANT(NOTIFICATION_WM_FOCUS_IN); BIND_CONSTANT(NOTIFICATION_WM_FOCUS_OUT); - BIND_CONSTANT(NOTIFICATION_WM_QUIT_REQUEST); + BIND_CONSTANT(NOTIFICATION_WM_CLOSE_REQUEST); BIND_CONSTANT(NOTIFICATION_WM_GO_BACK_REQUEST); - BIND_CONSTANT(NOTIFICATION_WM_UNFOCUS_REQUEST); + BIND_CONSTANT(NOTIFICATION_WM_SIZE_CHANGED); BIND_CONSTANT(NOTIFICATION_OS_MEMORY_WARNING); BIND_CONSTANT(NOTIFICATION_TRANSLATION_CHANGED); BIND_CONSTANT(NOTIFICATION_WM_ABOUT); diff --git a/scene/main/node.h b/scene/main/node.h index d1f75b71ec..cf25a92be6 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -244,13 +244,16 @@ public: NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26, NOTIFICATION_POST_ENTER_TREE = 27, //keep these linked to node - NOTIFICATION_WM_MOUSE_ENTER = MainLoop::NOTIFICATION_WM_MOUSE_ENTER, - NOTIFICATION_WM_MOUSE_EXIT = MainLoop::NOTIFICATION_WM_MOUSE_EXIT, - NOTIFICATION_WM_FOCUS_IN = MainLoop::NOTIFICATION_WM_FOCUS_IN, - NOTIFICATION_WM_FOCUS_OUT = MainLoop::NOTIFICATION_WM_FOCUS_OUT, - NOTIFICATION_WM_QUIT_REQUEST = MainLoop::NOTIFICATION_WM_QUIT_REQUEST, - NOTIFICATION_WM_GO_BACK_REQUEST = MainLoop::NOTIFICATION_WM_GO_BACK_REQUEST, - NOTIFICATION_WM_UNFOCUS_REQUEST = MainLoop::NOTIFICATION_WM_UNFOCUS_REQUEST, + + NOTIFICATION_WM_MOUSE_ENTER = 1002, + NOTIFICATION_WM_MOUSE_EXIT = 1003, + NOTIFICATION_WM_FOCUS_IN = 1004, + NOTIFICATION_WM_FOCUS_OUT = 1005, + NOTIFICATION_WM_CLOSE_REQUEST = 1006, + NOTIFICATION_WM_GO_BACK_REQUEST = 1007, + NOTIFICATION_WM_SIZE_CHANGED = 1008, + NOTIFICATION_WM_DPI_CHANGE = 1009, + NOTIFICATION_OS_MEMORY_WARNING = MainLoop::NOTIFICATION_OS_MEMORY_WARNING, NOTIFICATION_TRANSLATION_CHANGED = MainLoop::NOTIFICATION_TRANSLATION_CHANGED, NOTIFICATION_WM_ABOUT = MainLoop::NOTIFICATION_WM_ABOUT, diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index f472de220b..fad10524b9 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -31,6 +31,7 @@ #include "scene_tree.h" #include "core/debugger/engine_debugger.h" +#include "core/input/input_filter.h" #include "core/io/marshalls.h" #include "core/io/resource_loader.h" #include "core/message_queue.h" @@ -39,7 +40,6 @@ #include "core/os/os.h" #include "core/print_string.h" #include "core/project_settings.h" -#include "main/input_default.h" #include "node.h" #include "scene/debugger/scene_debugger.h" #include "scene/resources/dynamic_font.h" @@ -47,10 +47,11 @@ #include "scene/resources/mesh.h" #include "scene/resources/packed_scene.h" #include "scene/scene_string_names.h" +#include "servers/display_server.h" #include "servers/navigation_server.h" #include "servers/physics_2d_server.h" #include "servers/physics_server.h" -#include "viewport.h" +#include "window.h" #include <stdio.h> @@ -397,69 +398,6 @@ void SceneTree::set_group(const StringName &p_group, const String &p_name, const set_group_flags(0, p_group, p_name, p_value); } -void SceneTree::set_input_as_handled() { - - input_handled = true; -} - -void SceneTree::input_text(const String &p_text) { - - root_lock++; - - call_group_flags(GROUP_CALL_REALTIME, "_viewports", "_vp_input_text", p_text); //special one for GUI, as controls use their own process check - - root_lock--; -} - -bool SceneTree::is_input_handled() { - return input_handled; -} - -void SceneTree::input_event(const Ref<InputEvent> &p_event) { - - if (Engine::get_singleton()->is_editor_hint() && (Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventJoypadMotion>(*p_event))) - return; //avoid joy input on editor - - current_event++; - root_lock++; - - input_handled = false; - - // Don't make const ref unless you can find and fix what caused GH-34691. - Ref<InputEvent> ev = p_event; - - MainLoop::input_event(ev); - - call_group_flags(GROUP_CALL_REALTIME, "_viewports", "_vp_input", ev); //special one for GUI, as controls use their own process check - - if (EngineDebugger::is_active()) { - //quit from game window using F8 - Ref<InputEventKey> k = ev; - if (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == KEY_F8) { - EngineDebugger::get_singleton()->send_message("request_quit", Array()); - } - } - - _flush_ugc(); - root_lock--; - //MessageQueue::get_singleton()->flush(); //flushing here causes UI and other places slowness - - root_lock++; - - if (!input_handled) { - call_group_flags(GROUP_CALL_REALTIME, "_viewports", "_vp_unhandled_input", ev); //special one for GUI, as controls use their own process check - _flush_ugc(); - // input_handled = true; - no reason to set this as handled - root_lock--; - //MessageQueue::get_singleton()->flush(); //flushing here causes UI and other places slowness - } else { - // input_handled = true; - no reason to set this as handled - root_lock--; - } - - _call_idle_callbacks(); -} - void SceneTree::init() { initialized = true; root->_set_tree(this); @@ -493,14 +431,6 @@ bool SceneTree::iteration(float p_time) { return _quit; } -void SceneTree::_update_font_oversampling(float p_ratio) { - - if (use_font_oversampling) { - DynamicFontAtSize::font_oversampling = p_ratio; - DynamicFont::update_oversampling(); - } -} - bool SceneTree::idle(float p_time) { //print_line("ram: "+itos(OS::get_singleton()->get_static_memory_usage())+" sram: "+itos(OS::get_singleton()->get_dynamic_memory_usage())); @@ -526,15 +456,6 @@ bool SceneTree::idle(float p_time) { _notify_group_pause("idle_process_internal", Node::NOTIFICATION_INTERNAL_PROCESS); _notify_group_pause("idle_process", Node::NOTIFICATION_PROCESS); - Size2 win_size = Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height); - - if (win_size != last_screen_size) { - - last_screen_size = win_size; - _update_root_rect(); - emit_signal("screen_resized"); - } - _flush_ugc(); MessageQueue::get_singleton()->flush(); //small little hack flush_transform_notifications(); //transforms after world update, to avoid unnecessary enter/exit notifications @@ -642,59 +563,36 @@ void SceneTree::quit(int p_exit_code) { _quit = true; } -void SceneTree::_notification(int p_notification) { - - switch (p_notification) { - - case NOTIFICATION_WM_QUIT_REQUEST: { - - get_root()->propagate_notification(p_notification); - - if (accept_quit) { - _quit = true; - break; - } - } break; - - case NOTIFICATION_WM_GO_BACK_REQUEST: { - - get_root()->propagate_notification(p_notification); +void SceneTree::_main_window_close() { - if (quit_on_go_back) { - _quit = true; - break; - } - } break; + if (accept_quit) { + _quit = true; + } +} +void SceneTree::_main_window_go_back() { + if (quit_on_go_back) { + _quit = true; + } +} - case NOTIFICATION_WM_FOCUS_IN: { +void SceneTree::_main_window_focus_in() { + InputFilter *id = InputFilter::get_singleton(); + if (id) { + id->ensure_touch_mouse_raised(); + } +} - InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); - if (id) { - id->ensure_touch_mouse_raised(); - } +void SceneTree::_notification(int p_notification) { - get_root()->propagate_notification(p_notification); - } break; + switch (p_notification) { case NOTIFICATION_TRANSLATION_CHANGED: { if (!Engine::get_singleton()->is_editor_hint()) { get_root()->propagate_notification(p_notification); } } break; - - case NOTIFICATION_WM_UNFOCUS_REQUEST: { - - notify_group_flags(GROUP_CALL_REALTIME | GROUP_CALL_MULTILEVEL, "input", NOTIFICATION_WM_UNFOCUS_REQUEST); - - get_root()->propagate_notification(p_notification); - - } break; - case NOTIFICATION_OS_MEMORY_WARNING: case NOTIFICATION_OS_IME_UPDATE: - case NOTIFICATION_WM_MOUSE_ENTER: - case NOTIFICATION_WM_MOUSE_EXIT: - case NOTIFICATION_WM_FOCUS_OUT: case NOTIFICATION_WM_ABOUT: case NOTIFICATION_CRASH: case NOTIFICATION_APP_RESUMED: @@ -910,7 +808,7 @@ bool SceneTree::is_paused() const { return pause; } -void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p_method, const Ref<InputEvent> &p_input) { +void SceneTree::_notify_group_pause(const StringName &p_group, int p_notification) { Map<StringName, Group>::Element *E = group_map.find(p_group); if (!E) @@ -919,7 +817,7 @@ void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p if (g.nodes.empty()) return; - _update_group_order(g); + _update_group_order(g, p_notification == Node::NOTIFICATION_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PROCESS || p_notification == Node::NOTIFICATION_PHYSICS_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); //copy, so copy on write happens in case something is removed from process while being called //performance is not lost because only if something is added/removed the vector is copied. @@ -928,15 +826,9 @@ void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p int node_count = nodes_copy.size(); Node **nodes = nodes_copy.ptrw(); - Variant arg = p_input; - const Variant *v[1] = { &arg }; - call_lock++; - for (int i = node_count - 1; i >= 0; i--) { - - if (input_handled) - break; + for (int i = 0; i < node_count; i++) { Node *n = nodes[i]; if (call_lock && call_skip.has(n)) @@ -944,8 +836,10 @@ void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p if (!n->can_process()) continue; + if (!n->can_process_notification(p_notification)) + continue; - n->call_multilevel(p_method, (const Variant **)v, 1); + n->notification(p_notification); //ERR_FAIL_COND(node_count != g.nodes.size()); } @@ -954,7 +848,18 @@ void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p call_skip.clear(); } -void SceneTree::_notify_group_pause(const StringName &p_group, int p_notification) { +/* +void SceneMainLoop::_update_listener_2d() { + + if (listener_2d.is_valid()) { + + SpatialSound2DServer::get_singleton()->listener_set_space( listener_2d, world_2d->get_sound_space() ); + } + +} +*/ + +void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p_method, const Ref<InputEvent> &p_input, Viewport *p_viewport) { Map<StringName, Group>::Element *E = group_map.find(p_group); if (!E) @@ -963,7 +868,7 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio if (g.nodes.empty()) return; - _update_group_order(g, p_notification == Node::NOTIFICATION_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PROCESS || p_notification == Node::NOTIFICATION_PHYSICS_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); + _update_group_order(g); //copy, so copy on write happens in case something is removed from process while being called //performance is not lost because only if something is added/removed the vector is copied. @@ -972,9 +877,15 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio int node_count = nodes_copy.size(); Node **nodes = nodes_copy.ptrw(); + Variant arg = p_input; + const Variant *v[1] = { &arg }; + call_lock++; - for (int i = 0; i < node_count; i++) { + for (int i = node_count - 1; i >= 0; i--) { + + if (p_viewport->is_input_handled()) + break; Node *n = nodes[i]; if (call_lock && call_skip.has(n)) @@ -982,10 +893,8 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio if (!n->can_process()) continue; - if (!n->can_process_notification(p_notification)) - continue; - n->notification(p_notification); + n->call_multilevel(p_method, (const Variant **)v, 1); //ERR_FAIL_COND(node_count != g.nodes.size()); } @@ -993,18 +902,6 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio if (call_lock == 0) call_skip.clear(); } - -/* -void SceneMainLoop::_update_listener_2d() { - - if (listener_2d.is_valid()) { - - SpatialSound2DServer::get_singleton()->listener_set_space( listener_2d, world_2d->get_sound_space() ); - } - -} -*/ - Variant SceneTree::_call_group_flags(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { r_error.error = Callable::CallError::CALL_OK; @@ -1129,129 +1026,6 @@ int SceneTree::get_node_count() const { return node_count; } -void SceneTree::_update_root_rect() { - - if (stretch_mode == STRETCH_MODE_DISABLED) { - - _update_font_oversampling(1.0); - root->set_size((last_screen_size / stretch_shrink).floor()); - root->set_attach_to_screen_rect(Rect2(Point2(), last_screen_size)); - root->set_size_override_stretch(false); - root->set_size_override(false, Size2()); - root->update_canvas_items(); - return; //user will take care - } - - //actual screen video mode - Size2 video_mode = Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height); - Size2 desired_res = stretch_min; - - Size2 viewport_size; - Size2 screen_size; - - float viewport_aspect = desired_res.aspect(); - float video_mode_aspect = video_mode.aspect(); - - if (use_font_oversampling && stretch_aspect == STRETCH_ASPECT_IGNORE) { - WARN_PRINT("Font oversampling only works with the resize modes 'Keep Width', 'Keep Height', and 'Expand'."); - } - - if (stretch_aspect == STRETCH_ASPECT_IGNORE || Math::is_equal_approx(viewport_aspect, video_mode_aspect)) { - //same aspect or ignore aspect - viewport_size = desired_res; - screen_size = video_mode; - } else if (viewport_aspect < video_mode_aspect) { - // screen ratio is smaller vertically - - if (stretch_aspect == STRETCH_ASPECT_KEEP_HEIGHT || stretch_aspect == STRETCH_ASPECT_EXPAND) { - - //will stretch horizontally - viewport_size.x = desired_res.y * video_mode_aspect; - viewport_size.y = desired_res.y; - screen_size = video_mode; - - } else { - //will need black bars - viewport_size = desired_res; - screen_size.x = video_mode.y * viewport_aspect; - screen_size.y = video_mode.y; - } - } else { - //screen ratio is smaller horizontally - if (stretch_aspect == STRETCH_ASPECT_KEEP_WIDTH || stretch_aspect == STRETCH_ASPECT_EXPAND) { - - //will stretch horizontally - viewport_size.x = desired_res.x; - viewport_size.y = desired_res.x / video_mode_aspect; - screen_size = video_mode; - - } else { - //will need black bars - viewport_size = desired_res; - screen_size.x = video_mode.x; - screen_size.y = video_mode.x / viewport_aspect; - } - } - - screen_size = screen_size.floor(); - viewport_size = viewport_size.floor(); - - Size2 margin; - Size2 offset; - //black bars and margin - if (stretch_aspect != STRETCH_ASPECT_EXPAND && screen_size.x < video_mode.x) { - margin.x = Math::round((video_mode.x - screen_size.x) / 2.0); - VisualServer::get_singleton()->black_bars_set_margins(margin.x, 0, margin.x, 0); - offset.x = Math::round(margin.x * viewport_size.y / screen_size.y); - } else if (stretch_aspect != STRETCH_ASPECT_EXPAND && screen_size.y < video_mode.y) { - margin.y = Math::round((video_mode.y - screen_size.y) / 2.0); - VisualServer::get_singleton()->black_bars_set_margins(0, margin.y, 0, margin.y); - offset.y = Math::round(margin.y * viewport_size.x / screen_size.x); - } else { - VisualServer::get_singleton()->black_bars_set_margins(0, 0, 0, 0); - } - - switch (stretch_mode) { - case STRETCH_MODE_DISABLED: { - // Already handled above - _update_font_oversampling(1.0); - } break; - case STRETCH_MODE_2D: { - - _update_font_oversampling(screen_size.x / viewport_size.x); //screen / viewport radio drives oversampling - root->set_size((screen_size / stretch_shrink).floor()); - root->set_attach_to_screen_rect(Rect2(margin, screen_size)); - root->set_size_override_stretch(true); - root->set_size_override(true, (viewport_size / stretch_shrink).floor()); - root->update_canvas_items(); //force them to update just in case - - } break; - case STRETCH_MODE_VIEWPORT: { - - _update_font_oversampling(1.0); - root->set_size((viewport_size / stretch_shrink).floor()); - root->set_attach_to_screen_rect(Rect2(margin, screen_size)); - root->set_size_override_stretch(false); - root->set_size_override(false, Size2()); - root->update_canvas_items(); //force them to update just in case - - if (use_font_oversampling) { - WARN_PRINT("Font oversampling does not work in 'Viewport' stretch mode, only '2D'."); - } - - } break; - } -} - -void SceneTree::set_screen_stretch(StretchMode p_mode, StretchAspect p_aspect, const Size2 &p_minsize, real_t p_shrink) { - - stretch_mode = p_mode; - stretch_aspect = p_aspect; - stretch_min = p_minsize; - stretch_shrink = p_shrink; - _update_root_rect(); -} - void SceneTree::set_edited_scene_root(Node *p_node) { #ifdef TOOLS_ENABLED edited_scene_root = p_node; @@ -1330,18 +1104,6 @@ void SceneTree::add_current_scene(Node *p_current) { root->add_child(p_current); } -void SceneTree::drop_files(const Vector<String> &p_files, int p_from_screen) { - - emit_signal("files_dropped", p_files, p_from_screen); - MainLoop::drop_files(p_files, p_from_screen); -} - -void SceneTree::global_menu_action(const Variant &p_id, const Variant &p_meta) { - - emit_signal("global_menu_action", p_id, p_meta); - MainLoop::global_menu_action(p_id, p_meta); -} - Ref<SceneTreeTimer> SceneTree::create_timer(float p_delay_sec, bool p_process_pause) { Ref<SceneTreeTimer> stt; @@ -1469,8 +1231,6 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pause", "enable"), &SceneTree::set_pause); ClassDB::bind_method(D_METHOD("is_paused"), &SceneTree::is_paused); - ClassDB::bind_method(D_METHOD("set_input_as_handled"), &SceneTree::set_input_as_handled); - ClassDB::bind_method(D_METHOD("is_input_handled"), &SceneTree::is_input_handled); ClassDB::bind_method(D_METHOD("create_timer", "time_sec", "pause_mode_process"), &SceneTree::create_timer, DEFVAL(true)); @@ -1478,8 +1238,6 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_frame"), &SceneTree::get_frame); ClassDB::bind_method(D_METHOD("quit", "exit_code"), &SceneTree::quit, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("set_screen_stretch", "mode", "aspect", "minsize", "shrink"), &SceneTree::set_screen_stretch, DEFVAL(1)); - ClassDB::bind_method(D_METHOD("queue_delete", "obj"), &SceneTree::queue_delete); MethodInfo mi; @@ -1529,15 +1287,11 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_refuse_new_network_connections", "refuse"), &SceneTree::set_refuse_new_network_connections); ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &SceneTree::is_refusing_new_network_connections); - ClassDB::bind_method(D_METHOD("set_use_font_oversampling", "enable"), &SceneTree::set_use_font_oversampling); - ClassDB::bind_method(D_METHOD("is_using_font_oversampling"), &SceneTree::is_using_font_oversampling); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_collisions_hint"), "set_debug_collisions_hint", "is_debugging_collisions_hint"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_navigation_hint"), "set_debug_navigation_hint", "is_debugging_navigation_hint"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused"), "set_pause", "is_paused"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections"); ADD_PROPERTY_DEFAULT("refuse_new_network_connections", false); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_font_oversampling"), "set_use_font_oversampling", "is_using_font_oversampling"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "edited_scene_root", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_edited_scene_root", "get_edited_scene_root"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "current_scene", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_current_scene", "get_current_scene"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer"); @@ -1549,14 +1303,12 @@ void SceneTree::_bind_methods() { ADD_SIGNAL(MethodInfo("node_added", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("node_removed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("node_renamed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("screen_resized")); ADD_SIGNAL(MethodInfo("node_configuration_warning_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("idle_frame")); ADD_SIGNAL(MethodInfo("physics_frame")); ADD_SIGNAL(MethodInfo("files_dropped", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files"), PropertyInfo(Variant::INT, "screen"))); - ADD_SIGNAL(MethodInfo("global_menu_action", PropertyInfo(Variant::NIL, "id"), PropertyInfo(Variant::NIL, "meta"))); ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("connected_to_server")); @@ -1567,16 +1319,6 @@ void SceneTree::_bind_methods() { BIND_ENUM_CONSTANT(GROUP_CALL_REVERSE); BIND_ENUM_CONSTANT(GROUP_CALL_REALTIME); BIND_ENUM_CONSTANT(GROUP_CALL_UNIQUE); - - BIND_ENUM_CONSTANT(STRETCH_MODE_DISABLED); - BIND_ENUM_CONSTANT(STRETCH_MODE_2D); - BIND_ENUM_CONSTANT(STRETCH_MODE_VIEWPORT); - - BIND_ENUM_CONSTANT(STRETCH_ASPECT_IGNORE); - BIND_ENUM_CONSTANT(STRETCH_ASPECT_KEEP); - BIND_ENUM_CONSTANT(STRETCH_ASPECT_KEEP_WIDTH); - BIND_ENUM_CONSTANT(STRETCH_ASPECT_KEEP_HEIGHT); - BIND_ENUM_CONSTANT(STRETCH_ASPECT_EXPAND); } SceneTree *SceneTree::singleton = NULL; @@ -1596,19 +1338,6 @@ void SceneTree::add_idle_callback(IdleCallback p_callback) { idle_callbacks[idle_callback_count++] = p_callback; } -void SceneTree::set_use_font_oversampling(bool p_oversampling) { - - if (use_font_oversampling == p_oversampling) - return; - - use_font_oversampling = p_oversampling; - _update_root_rect(); -} - -bool SceneTree::is_using_font_oversampling() const { - return use_font_oversampling; -} - void SceneTree::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { if (p_function == "change_scene") { @@ -1648,7 +1377,6 @@ SceneTree::SceneTree() { accept_quit = true; quit_on_go_back = true; initialized = false; - use_font_oversampling = false; #ifdef DEBUG_ENABLED debug_collisions_hint = false; debug_navigation_hint = false; @@ -1665,7 +1393,6 @@ SceneTree::SceneTree() { idle_process_time = 1; root = NULL; - input_handled = false; pause = false; current_frame = 0; current_event = 0; @@ -1680,9 +1407,8 @@ SceneTree::SceneTree() { //create with mainloop - root = memnew(Viewport); + root = memnew(Window); root->set_name("root"); - root->set_handle_input_locally(false); if (!root->get_world().is_valid()) root->set_world(Ref<World>(memnew(World))); @@ -1730,15 +1456,12 @@ SceneTree::SceneTree() { } } - stretch_mode = STRETCH_MODE_DISABLED; - stretch_aspect = STRETCH_ASPECT_IGNORE; - stretch_shrink = 1; - - last_screen_size = Size2(OS::get_singleton()->get_window_size().width, OS::get_singleton()->get_window_size().height); - _update_root_rect(); - root->set_physics_object_picking(GLOBAL_DEF("physics/common/enable_object_picking", true)); + root->connect("close_requested", callable_mp(this, &SceneTree::_main_window_close)); + root->connect("go_back_requested", callable_mp(this, &SceneTree::_main_window_go_back)); + root->connect("focus_entered", callable_mp(this, &SceneTree::_main_window_focus_in)); + #ifdef TOOLS_ENABLED edited_scene_root = NULL; #endif diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 2f805d074f..e6b2575f98 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -39,9 +39,11 @@ #include "scene/resources/world.h" #include "scene/resources/world_2d.h" +#undef Window + class PackedScene; class Node; -class Viewport; +class Window; class Material; class Mesh; class SceneDebugger; @@ -76,22 +78,6 @@ class SceneTree : public MainLoop { public: typedef void (*IdleCallback)(); - enum StretchMode { - - STRETCH_MODE_DISABLED, - STRETCH_MODE_2D, - STRETCH_MODE_VIEWPORT, - }; - - enum StretchAspect { - - STRETCH_ASPECT_IGNORE, - STRETCH_ASPECT_KEEP, - STRETCH_ASPECT_KEEP_WIDTH, - STRETCH_ASPECT_KEEP_HEIGHT, - STRETCH_ASPECT_EXPAND, - }; - private: struct Group { @@ -101,7 +87,7 @@ private: Group() { changed = false; }; }; - Viewport *root; + Window *root; uint64_t tree_version; float physics_process_time; @@ -119,15 +105,12 @@ private: Map<StringName, Group> group_map; bool _quit; bool initialized; - bool input_handled; - Size2 last_screen_size; StringName tree_changed_name; StringName node_added_name; StringName node_removed_name; StringName node_renamed_name; - bool use_font_oversampling; int64_t current_frame; int64_t current_event; int node_count; @@ -147,14 +130,6 @@ private: int call_lock; Set<Node *> call_skip; //skip erased nodes - StretchMode stretch_mode; - StretchAspect stretch_aspect; - Size2i stretch_min; - real_t stretch_shrink; - - void _update_font_oversampling(float p_ratio); - void _update_root_rect(); - List<ObjectID> delete_queue; Map<UGCall, Vector<Variant>> unique_group_calls; @@ -208,7 +183,6 @@ private: void make_group_changed(const StringName &p_group); void _notify_group_pause(const StringName &p_group, int p_notification); - void _call_input_pause(const StringName &p_group, const StringName &p_method, const Ref<InputEvent> &p_input); Variant _call_group_flags(const Variant **p_args, int p_argcount, Callable::CallError &r_error); Variant _call_group(const Variant **p_args, int p_argcount, Callable::CallError &r_error); @@ -232,6 +206,13 @@ private: static int idle_callback_count; void _call_idle_callbacks(); + void _main_window_focus_in(); + void _main_window_close(); + void _main_window_go_back(); + + //used by viewport + void _call_input_pause(const StringName &p_group, const StringName &p_method, const Ref<InputEvent> &p_input, Viewport *p_viewport); + protected: void _notification(int p_notification); static void _bind_methods(); @@ -249,7 +230,7 @@ public: GROUP_CALL_MULTILEVEL = 8, }; - _FORCE_INLINE_ Viewport *get_root() const { return root; } + _FORCE_INLINE_ Window *get_root() const { return root; } void call_group_flags(uint32_t p_call_flags, const StringName &p_group, const StringName &p_function, VARIANT_ARG_LIST); void notify_group_flags(uint32_t p_call_flags, const StringName &p_group, int p_notification); @@ -261,8 +242,6 @@ public: void flush_transform_notifications(); - virtual void input_text(const String &p_text); - virtual void input_event(const Ref<InputEvent> &p_event); virtual void init(); virtual bool iteration(float p_time); @@ -275,8 +254,6 @@ public: void quit(int p_exit_code = -1); - void set_input_as_handled(); - bool is_input_handled(); _FORCE_INLINE_ float get_physics_process_time() const { return physics_process_time; } _FORCE_INLINE_ float get_idle_process_time() const { return idle_process_time; } @@ -335,11 +312,6 @@ public: void get_nodes_in_group(const StringName &p_group, List<Node *> *p_list); bool has_group(const StringName &p_identifier) const; - void set_screen_stretch(StretchMode p_mode, StretchAspect p_aspect, const Size2 &p_minsize, real_t p_shrink = 1); - - void set_use_font_oversampling(bool p_oversampling); - bool is_using_font_oversampling() const; - //void change_scene(const String& p_path); //Node *get_loaded_scene(); @@ -359,8 +331,6 @@ public: static SceneTree *get_singleton() { return singleton; } - void drop_files(const Vector<String> &p_files, int p_from_screen = 0); - void global_menu_action(const Variant &p_id, const Variant &p_meta); void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const; //network API @@ -388,8 +358,6 @@ public: ~SceneTree(); }; -VARIANT_ENUM_CAST(SceneTree::StretchMode); -VARIANT_ENUM_CAST(SceneTree::StretchAspect); VARIANT_ENUM_CAST(SceneTree::GroupCallFlags); #endif diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 8ef3bdd04e..e49674663a 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -32,7 +32,7 @@ #include "core/core_string_names.h" #include "core/debugger/engine_debugger.h" -#include "core/os/input.h" +#include "core/input/input_filter.h" #include "core/os/os.h" #include "core/project_settings.h" #include "scene/2d/collision_object_2d.h" @@ -49,8 +49,10 @@ #include "scene/gui/popup_menu.h" #include "scene/main/canvas_layer.h" #include "scene/main/timer.h" +#include "scene/main/window.h" #include "scene/resources/mesh.h" #include "scene/scene_string_names.h" +#include "servers/display_server.h" #include "servers/physics_2d_server.h" void ViewportTexture::setup_local_to_scene() { @@ -141,7 +143,7 @@ void ViewportTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("set_viewport_path_in_scene", "path"), &ViewportTexture::set_viewport_path_in_scene); ClassDB::bind_method(D_METHOD("get_viewport_path_in_scene"), &ViewportTexture::get_viewport_path_in_scene); - 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"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewport_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "SubViewport", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT), "set_viewport_path_in_scene", "get_viewport_path_in_scene"); } ViewportTexture::ViewportTexture() { @@ -166,9 +168,9 @@ ViewportTexture::~ViewportTexture() { ///////////////////////////////////// -class TooltipPanel : public PanelContainer { +class TooltipPanel : public PopupPanel { - GDCLASS(TooltipPanel, PanelContainer); + GDCLASS(TooltipPanel, PopupPanel); public: TooltipPanel(){}; @@ -184,39 +186,25 @@ public: Viewport::GUI::GUI() { + embed_subwindows_hint = false; + embedding_subwindows = false; + dragging = false; mouse_focus = NULL; + forced_mouse_focus = false; mouse_click_grabber = NULL; mouse_focus_mask = 0; key_focus = NULL; mouse_over = NULL; + drag_mouse_over = NULL; tooltip = NULL; tooltip_popup = NULL; tooltip_label = NULL; - subwindow_visibility_dirty = false; - subwindow_order_dirty = false; } ///////////////////////////////////// -void Viewport::_update_stretch_transform() { - - if (size_override_stretch && size_override) { - - stretch_transform = Transform2D(); - Size2 scale = size / (size_override_size + size_override_margin * 2); - stretch_transform.scale(scale); - stretch_transform.elements[2] = size_override_margin * scale; - - } else { - - stretch_transform = Transform2D(); - } - - _update_global_transform(); -} - void Viewport::update_worlds() { if (!is_inside_tree()) @@ -249,6 +237,191 @@ void Viewport::_collision_object_input_event(CollisionObject *p_object, Camera * physics_last_id = id; } +void Viewport::_sub_window_update_order() { + + for (int i = 0; i < gui.sub_windows.size(); i++) { + VS::get_singleton()->canvas_item_set_draw_index(gui.sub_windows[i].canvas_item, i); + } +} + +void Viewport::_sub_window_register(Window *p_window) { + + ERR_FAIL_COND(!is_inside_tree()); + for (int i = 0; i < gui.sub_windows.size(); i++) { + ERR_FAIL_COND(gui.sub_windows[i].window == p_window); + } + + if (gui.sub_windows.size() == 0) { + subwindow_canvas = VS::get_singleton()->canvas_create(); + VS::get_singleton()->viewport_attach_canvas(viewport, subwindow_canvas); + VS::get_singleton()->viewport_set_canvas_stacking(viewport, subwindow_canvas, SUBWINDOW_CANVAS_LAYER, 0); + } + SubWindow sw; + sw.canvas_item = VS::get_singleton()->canvas_item_create(); + VS::get_singleton()->canvas_item_set_parent(sw.canvas_item, subwindow_canvas); + sw.window = p_window; + gui.sub_windows.push_back(sw); + + _sub_window_grab_focus(p_window); + + VisualServer::get_singleton()->viewport_set_parent_viewport(p_window->viewport, viewport); +} + +void Viewport::_sub_window_update(Window *p_window) { + + int index = -1; + for (int i = 0; i < gui.sub_windows.size(); i++) { + if (gui.sub_windows[i].window == p_window) { + index = i; + break; + } + } + + ERR_FAIL_COND(index == -1); + + const SubWindow &sw = gui.sub_windows[index]; + + Transform2D pos; + pos.set_origin(p_window->get_position()); + VS::get_singleton()->canvas_item_clear(sw.canvas_item); + Rect2i r = Rect2i(p_window->get_position(), sw.window->get_size()); + + if (!p_window->get_flag(Window::FLAG_BORDERLESS)) { + Ref<StyleBox> panel = p_window->get_theme_stylebox("panel_window"); + panel->draw(sw.canvas_item, r); + + // Draw the title bar text. + Ref<Font> title_font = p_window->get_theme_font("title_font"); + Color title_color = p_window->get_theme_color("title_color"); + int title_height = p_window->get_theme_constant("title_height"); + int font_height = title_font->get_height() - title_font->get_descent() * 2; + int x = (r.size.width - title_font->get_string_size(p_window->get_title()).x) / 2; + int y = (-title_height + font_height) / 2; + + int close_h_ofs = p_window->get_theme_constant("close_h_ofs"); + int close_v_ofs = p_window->get_theme_constant("close_v_ofs"); + + title_font->draw(sw.canvas_item, r.position + Point2(x, y), p_window->get_title(), title_color, r.size.width - panel->get_minimum_size().x - close_h_ofs); + + bool hl = gui.subwindow_focused == sw.window && gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE && gui.subwindow_drag_close_inside; + + Ref<Texture2D> close_icon = p_window->get_theme_icon(hl ? "close_highlight" : "close"); + close_icon->draw(sw.canvas_item, r.position + Vector2(r.size.width - close_h_ofs, -close_v_ofs)); + } + + VS::get_singleton()->canvas_item_add_texture_rect(sw.canvas_item, r, sw.window->get_texture()->get_rid()); +} + +void Viewport::_sub_window_grab_focus(Window *p_window) { + + if (p_window == nullptr) { + //release current focus + if (gui.subwindow_focused) { + gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT); + gui.subwindow_focused = nullptr; + gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED; + } + + Window *this_window = Object::cast_to<Window>(this); + if (this_window) { + this_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + } + + return; + } + + int index = -1; + for (int i = 0; i < gui.sub_windows.size(); i++) { + if (gui.sub_windows[i].window == p_window) { + index = i; + break; + } + } + + ERR_FAIL_COND(index == -1); + + if (p_window->get_flag(Window::FLAG_NO_FOCUS)) { + //can only move to foreground, but no focus granted + SubWindow sw = gui.sub_windows[index]; + gui.sub_windows.remove(index); + gui.sub_windows.push_back(sw); + index = gui.sub_windows.size() - 1; + _sub_window_update_order(); + return; //i guess not... + } + + if (gui.subwindow_focused) { + if (gui.subwindow_focused == p_window) { + return; //nothing to do + } + gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT); + gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED; + } else { + Window *this_window = Object::cast_to<Window>(this); + if (this_window) { + this_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT); + } + } + + Window *old_focus = gui.subwindow_focused; + + gui.subwindow_focused = p_window; + + gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + + { //move to foreground + SubWindow sw = gui.sub_windows[index]; + gui.sub_windows.remove(index); + gui.sub_windows.push_back(sw); + index = gui.sub_windows.size() - 1; + _sub_window_update_order(); + } + + if (old_focus) { + _sub_window_update(old_focus); + } + + _sub_window_update(p_window); +} + +void Viewport::_sub_window_remove(Window *p_window) { + + for (int i = 0; i < gui.sub_windows.size(); i++) { + if (gui.sub_windows[i].window == p_window) { + VS::get_singleton()->free(gui.sub_windows[i].canvas_item); + gui.sub_windows.remove(i); + break; + } + } + + if (gui.sub_windows.size() == 0) { + VS::get_singleton()->free(subwindow_canvas); + subwindow_canvas = RID(); + } + + if (gui.subwindow_focused == p_window) { + Window *parent_visible = p_window->get_parent_visible_window(); + + gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED; + + gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT); + + if (parent_visible && parent_visible != this) { + + gui.subwindow_focused = parent_visible; + gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + } else { + gui.subwindow_focused = nullptr; + Window *this_window = Object::cast_to<Window>(this); + if (this_window) { + this_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + } + } + } + + VisualServer::get_singleton()->viewport_set_parent_viewport(p_window->viewport, p_window->parent ? p_window->parent->viewport : RID()); +} + void Viewport::_own_world_changed() { ERR_FAIL_COND(world.is_null()); ERR_FAIL_COND(own_world.is_null()); @@ -276,6 +449,8 @@ void Viewport::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { + gui.embedding_subwindows = gui.embed_subwindows_hint; + if (get_parent()) { parent = get_parent()->get_viewport(); VisualServer::get_singleton()->viewport_set_parent_viewport(viewport, parent->get_viewport_rid()); @@ -310,7 +485,6 @@ void Viewport::_notification(int p_what) { //VisualServer::get_singleton()->instance_geometry_set_flag(contact_3d_debug_instance, VS::INSTANCE_FLAG_VISIBLE_IN_ALL_ROOMS, true); } - VS::get_singleton()->viewport_set_active(viewport, true); } break; case NOTIFICATION_READY: { #ifndef _3D_DISABLED @@ -371,6 +545,7 @@ void Viewport::_notification(int p_what) { remove_from_group("_viewports"); VS::get_singleton()->viewport_set_active(viewport, false); + VisualServer::get_singleton()->viewport_set_parent_viewport(viewport, RID()); } break; case NOTIFICATION_INTERNAL_PROCESS: { @@ -408,7 +583,7 @@ void Viewport::_notification(int p_what) { VS::get_singleton()->multimesh_set_visible_instances(contact_3d_debug_multimesh, point_count); } - if (physics_object_picking && (to_screen_rect == Rect2() || Input::get_singleton()->get_mouse_mode() != Input::MOUSE_MODE_CAPTURED)) { + if (physics_object_picking && (to_screen_rect == Rect2i() || InputFilter::get_singleton()->get_mouse_mode() != InputFilter::MOUSE_MODE_CAPTURED)) { #ifndef _3D_DISABLED Vector2 last_pos(1e20, 1e20); @@ -433,6 +608,7 @@ void Viewport::_notification(int p_what) { if (!has_mouse_event) { Ref<InputEventMouseMotion> mm; mm.instance(); + mm->set_device(InputEvent::DEVICE_ID_INTERNAL); mm->set_global_position(physics_last_mousepos); mm->set_position(physics_last_mousepos); @@ -680,13 +856,12 @@ void Viewport::_notification(int p_what) { } } break; - case SceneTree::NOTIFICATION_WM_MOUSE_EXIT: - case SceneTree::NOTIFICATION_WM_FOCUS_OUT: { + case NOTIFICATION_WM_MOUSE_EXIT: + case NOTIFICATION_WM_FOCUS_OUT: { _drop_physics_mouseover(); - if (gui.mouse_focus) { - //if mouse is being pressed, send a release event + if (gui.mouse_focus && !gui.forced_mouse_focus) { _drop_mouse_focus(); } } break; @@ -698,16 +873,6 @@ RID Viewport::get_viewport_rid() const { return viewport; } -void Viewport::set_use_arvr(bool p_use_arvr) { - arvr = p_use_arvr; - - VS::get_singleton()->viewport_set_use_arvr(viewport, arvr); -} - -bool Viewport::use_arvr() { - return arvr; -} - void Viewport::update_canvas_items() { if (!is_inside_tree()) return; @@ -715,40 +880,52 @@ void Viewport::update_canvas_items() { _update_canvas_items(this); } -void Viewport::set_size(const Size2 &p_size) { +void Viewport::_set_size(const Size2i &p_size, const Size2i &p_size_override, const Rect2i &p_to_screen_rect, const Transform2D &p_stretch_transform, bool p_allocated) { - if (size == p_size.floor()) + if (size == p_size && size_allocated == p_allocated && stretch_transform == p_stretch_transform && p_size_override == size_override && to_screen_rect != p_to_screen_rect) return; - size = p_size.floor(); - VS::get_singleton()->viewport_set_size(viewport, size.width, size.height); + size = p_size; + size_allocated = p_allocated; + size_override = p_size_override; + stretch_transform = p_stretch_transform; + to_screen_rect = p_to_screen_rect; + + if (p_allocated) { + VS::get_singleton()->viewport_set_size(viewport, size.width, size.height); + } else { + VS::get_singleton()->viewport_set_size(viewport, 0, 0); + } + _update_global_transform(); - _update_stretch_transform(); + update_canvas_items(); emit_signal("size_changed"); } +Size2i Viewport::_get_size() const { + return size; +} +bool Viewport::_is_size_allocated() const { + return size_allocated; +} + Rect2 Viewport::get_visible_rect() const { Rect2 r; if (size == Size2()) { - r = Rect2(Point2(), OS::get_singleton()->get_window_size()); + r = Rect2(Point2(), DisplayServer::get_singleton()->window_get_size()); } else { r = Rect2(Point2(), size); } - if (size_override) { - r.size = size_override_size; + if (size_override != Size2i()) { + r.size = size_override; } return r; } -Size2 Viewport::get_size() const { - - return size; -} - void Viewport::_update_listener() { /* if (is_inside_tree() && audio_listener && (camera || listener) && (!get_parent() || (Object::cast_to<Control>(get_parent()) && Object::cast_to<Control>(get_parent())->is_visible_in_tree()))) { @@ -943,7 +1120,9 @@ void Viewport::_camera_set(Camera *p_camera) { if (camera) { camera->notification(Camera::NOTIFICATION_LOST_CURRENT); } + camera = p_camera; + if (!camera_override) { if (camera) VisualServer::get_singleton()->viewport_attach_camera(viewport, camera->get_camera()); @@ -1278,70 +1457,11 @@ void Viewport::_update_canvas_items(Node *p_node) { } } -void Viewport::set_size_override(bool p_enable, const Size2 &p_size, const Vector2 &p_margin) { - - if (size_override == p_enable && p_size == size_override_size) - return; - - size_override = p_enable; - if (p_size.x >= 0 || p_size.y >= 0) { - size_override_size = p_size; - } - size_override_margin = p_margin; - - _update_stretch_transform(); - emit_signal("size_changed"); -} - -Size2 Viewport::get_size_override() const { - - return size_override_size; -} -bool Viewport::is_size_override_enabled() const { - - return size_override; -} -void Viewport::set_size_override_stretch(bool p_enable) { - - if (p_enable == size_override_stretch) - return; - - size_override_stretch = p_enable; - - _update_stretch_transform(); -} - -bool Viewport::is_size_override_stretch_enabled() const { - - return size_override_stretch; -} - -void Viewport::set_update_mode(UpdateMode p_mode) { - - update_mode = p_mode; - VS::get_singleton()->viewport_set_update_mode(viewport, VS::ViewportUpdateMode(p_mode)); -} -Viewport::UpdateMode Viewport::get_update_mode() const { - - return update_mode; -} - Ref<ViewportTexture> Viewport::get_texture() const { return default_texture; } -void Viewport::set_clear_mode(ClearMode p_mode) { - - clear_mode = p_mode; - VS::get_singleton()->viewport_set_clear_mode(viewport, VS::ViewportClearMode(p_mode)); -} - -Viewport::ClearMode Viewport::get_clear_mode() const { - - return clear_mode; -} - void Viewport::set_shadow_atlas_size(int p_size) { if (shadow_atlas_size == p_size) @@ -1379,7 +1499,7 @@ Transform2D Viewport::_get_input_pre_xform() const { Transform2D pre_xf; - if (to_screen_rect != Rect2()) { + if (to_screen_rect.size.x != 0 && to_screen_rect.size.y != 0) { pre_xf.elements[2] = -to_screen_rect.position; pre_xf.scale(size / to_screen_rect.size); @@ -1388,118 +1508,22 @@ Transform2D Viewport::_get_input_pre_xform() const { return pre_xf; } -Vector2 Viewport::_get_window_offset() const { - - if (get_parent() && get_parent()->has_method("get_global_position")) { - return get_parent()->call("get_global_position"); - } - return Vector2(); -} - Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) { - Vector2 vp_ofs = _get_window_offset(); Transform2D ai = get_final_transform().affine_inverse() * _get_input_pre_xform(); - return ev->xformed_by(ai, -vp_ofs); -} - -void Viewport::_vp_input_text(const String &p_text) { - - if (gui.key_focus) { - gui.key_focus->call("set_text", p_text); - } -} - -void Viewport::_vp_input(const Ref<InputEvent> &p_ev) { - - if (disable_input) - return; - -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_a_parent_of(this)) { - return; - } -#endif - - if (to_screen_rect == Rect2()) - return; //if render target, can't get input events - - //this one handles system input, p_ev are in system coordinates - //they are converted to viewport coordinates - - Ref<InputEvent> ev = _make_input_local(p_ev); - input(ev); -} - -void Viewport::_vp_unhandled_input(const Ref<InputEvent> &p_ev) { - - if (disable_input) - return; -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_a_parent_of(this)) { - return; - } -#endif - - /* - if (parent_control && !parent_control->is_visible_in_tree()) - return; - */ - - if (to_screen_rect == Rect2()) - return; //if render target, can't get input events - - //this one handles system input, p_ev are in system coordinates - //they are converted to viewport coordinates - - Ref<InputEvent> ev = _make_input_local(p_ev); - unhandled_input(ev); + return ev->xformed_by(ai); } Vector2 Viewport::get_mouse_position() const { - return (get_final_transform().affine_inverse() * _get_input_pre_xform()).xform(Input::get_singleton()->get_mouse_position() - _get_window_offset()); + return gui.last_mouse_pos; } void Viewport::warp_mouse(const Vector2 &p_pos) { Vector2 gpos = (get_final_transform().affine_inverse() * _get_input_pre_xform()).affine_inverse().xform(p_pos); - Input::get_singleton()->warp_mouse_position(gpos); -} - -void Viewport::_gui_prepare_subwindows() { - - if (gui.subwindow_visibility_dirty) { - - gui.subwindows.clear(); - for (List<Control *>::Element *E = gui.all_known_subwindows.front(); E; E = E->next()) { - if (E->get()->is_visible_in_tree()) { - gui.subwindows.push_back(E->get()); - } - } - - gui.subwindow_visibility_dirty = false; - gui.subwindow_order_dirty = true; - } - - _gui_sort_subwindows(); -} - -void Viewport::_gui_sort_subwindows() { - - if (!gui.subwindow_order_dirty) - return; - - gui.modal_stack.sort_custom<Control::CComparator>(); - gui.subwindows.sort_custom<Control::CComparator>(); - - gui.subwindow_order_dirty = false; -} - -void Viewport::_gui_sort_modal_stack() { - - gui.modal_stack.sort_custom<Control::CComparator>(); + InputFilter::get_singleton()->warp_mouse_position(gpos); } void Viewport::_gui_sort_roots() { @@ -1575,47 +1599,49 @@ void Viewport::_gui_show_tooltip() { Control *rp = which; - gui.tooltip_popup = which->make_custom_tooltip(tooltip); - - if (!gui.tooltip_popup) { - gui.tooltip_popup = memnew(TooltipPanel); + Control *base_tooltip = which->make_custom_tooltip(tooltip); + if (!base_tooltip) { gui.tooltip_label = memnew(TooltipLabel); - gui.tooltip_popup->add_child(gui.tooltip_label); - - Ref<StyleBox> ttp = gui.tooltip_label->get_stylebox("panel", "TooltipPanel"); - - gui.tooltip_label->set_anchor_and_margin(MARGIN_LEFT, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_LEFT)); - gui.tooltip_label->set_anchor_and_margin(MARGIN_TOP, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_TOP)); - gui.tooltip_label->set_anchor_and_margin(MARGIN_RIGHT, Control::ANCHOR_END, -ttp->get_margin(MARGIN_RIGHT)); - gui.tooltip_label->set_anchor_and_margin(MARGIN_BOTTOM, Control::ANCHOR_END, -ttp->get_margin(MARGIN_BOTTOM)); gui.tooltip_label->set_text(tooltip); + base_tooltip = gui.tooltip_label; } + base_tooltip->set_anchors_and_margins_preset(Control::PRESET_WIDE); + + TooltipPanel *panel = memnew(TooltipPanel); + panel->set_transient(false); + panel->set_flag(Window::FLAG_NO_FOCUS, true); + panel->set_wrap_controls(true); + panel->add_child(base_tooltip); + + gui.tooltip_popup = panel; + rp->add_child(gui.tooltip_popup); - gui.tooltip_popup->force_parent_owned(); - gui.tooltip_popup->set_as_toplevel(true); - if (gui.tooltip) // Avoids crash when rapidly switching controls. - gui.tooltip_popup->set_scale(gui.tooltip->get_global_transform().get_scale()); + + //if (gui.tooltip) // Avoids crash when rapidly switching controls. + // gui.tooltip_popup->set_scale(gui.tooltip->get_global_transform().get_scale()); Point2 tooltip_offset = ProjectSettings::get_singleton()->get("display/mouse_cursor/tooltip_position_offset"); - Rect2 r(gui.tooltip_pos + tooltip_offset, gui.tooltip_popup->get_minimum_size()); - Rect2 vr = gui.tooltip_popup->get_viewport_rect(); - if (r.size.x * gui.tooltip_popup->get_scale().x + r.position.x > vr.size.x) - r.position.x = vr.size.x - r.size.x * gui.tooltip_popup->get_scale().x; - else if (r.position.x < 0) - r.position.x = 0; - - if (r.size.y * gui.tooltip_popup->get_scale().y + r.position.y > vr.size.y) - r.position.y = vr.size.y - r.size.y * gui.tooltip_popup->get_scale().y; - else if (r.position.y < 0) - r.position.y = 0; - - gui.tooltip_popup->set_global_position(r.position); + Rect2 r(gui.tooltip_pos + tooltip_offset, gui.tooltip_popup->get_contents_minimum_size()); + + Rect2i vr = gui.tooltip_popup->get_parent_visible_window()->get_usable_parent_rect(); + + if (r.size.x + r.position.x > vr.size.x + vr.position.x) + r.position.x = vr.position.x + vr.size.x - r.size.x; + else if (r.position.x < vr.position.x) + r.position.x = vr.position.x; + + if (r.size.y + r.position.y > vr.size.y + vr.position.y) + r.position.y = vr.position.y + vr.size.y - r.size.y; + else if (r.position.y < vr.position.y) + r.position.y = vr.position.y; + + gui.tooltip_popup->set_position(r.position); gui.tooltip_popup->set_size(r.size); - gui.tooltip_popup->raise(); gui.tooltip_popup->show(); + gui.tooltip_popup->child_controls_changed(); } void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_input) { @@ -1704,26 +1730,7 @@ void Viewport::_gui_call_notification(Control *p_control, int p_what) { } Control *Viewport::_gui_find_control(const Point2 &p_global) { - _gui_prepare_subwindows(); - - for (List<Control *>::Element *E = gui.subwindows.back(); E; E = E->prev()) { - - Control *sw = E->get(); - if (!sw->is_visible_in_tree()) - continue; - - Transform2D xform; - CanvasItem *pci = sw->get_parent_item(); - if (pci) - xform = pci->get_global_transform_with_canvas(); - else - xform = sw->get_canvas_transform(); - - Control *ret = _gui_find_control_at_pos(sw, p_global, xform, gui.focus_inv_xform); - if (ret) - return ret; - } - + //aca va subwindows _gui_sort_roots(); for (List<Control *>::Element *E = gui.roots.back(); E; E = E->prev()) { @@ -1752,8 +1759,6 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_ if (Object::cast_to<Viewport>(p_node)) return NULL; - //subwindows first!! - if (!p_node->is_visible()) { //return _find_next_visible_control_at_pos(p_node,p_global,r_inv_xform); return NULL; //canvas item hidden, discard @@ -1770,9 +1775,6 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_ for (int i = p_node->get_child_count() - 1; i >= 0; i--) { - if (p_node == gui.tooltip_popup) - continue; - CanvasItem *ci = Object::cast_to<CanvasItem>(p_node->get_child(i)); if (!ci || ci->is_set_as_toplevel()) continue; @@ -1858,39 +1860,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { bool is_handled = false; - _gui_sort_modal_stack(); - while (!gui.modal_stack.empty()) { - - Control *top = gui.modal_stack.back()->get(); - Vector2 pos2 = top->get_global_transform_with_canvas().affine_inverse().xform(mpos); - if (!top->has_point(pos2)) { - - if (top->data.modal_exclusive || top->data.modal_frame == Engine::get_singleton()->get_frames_drawn()) { - //cancel event, sorry, modal exclusive EATS UP ALL - //alternative, you can't pop out a window the same frame it was made modal (fixes many issues) - set_input_as_handled(); - - return; // no one gets the event if exclusive NO ONE - } - - if (mb->get_button_index() == BUTTON_WHEEL_UP || mb->get_button_index() == BUTTON_WHEEL_DOWN || mb->get_button_index() == BUTTON_WHEEL_LEFT || mb->get_button_index() == BUTTON_WHEEL_RIGHT) { - //cancel scroll wheel events, only clicks should trigger focus changes. - set_input_as_handled(); - return; - } - - top->notification(Control::NOTIFICATION_MODAL_CLOSE); - top->_modal_stack_remove(); - top->hide(); - - if (!top->pass_on_modal_close_click()) { - is_handled = true; - } - } else { - break; - } - } - if (is_handled) { set_input_as_handled(); return; @@ -1992,11 +1961,8 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == BUTTON_LEFT) { - if (gui.mouse_over) { - Size2 pos = mpos; - pos = gui.focus_inv_xform.xform(pos); - - _gui_drop(gui.mouse_over, pos, false); + if (gui.drag_mouse_over) { + _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, false); } if (gui.drag_preview && mb->get_button_index() == BUTTON_LEFT) { @@ -2006,6 +1972,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.drag_data = Variant(); gui.dragging = false; + gui.drag_mouse_over = nullptr; _propagate_viewport_notification(this, NOTIFICATION_DRAG_END); //change mouse accordingly } @@ -2029,6 +1996,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { //disable mouse focus if needed before calling input, this makes popups on mouse press event work better, as the release will never be received otherwise if (gui.mouse_focus_mask == 0) { gui.mouse_focus = NULL; + gui.forced_mouse_focus = false; } if (mouse_focus && mouse_focus->can_process()) { @@ -2074,6 +2042,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (gui.drag_data.get_type() != Variant::NIL) { gui.mouse_focus = NULL; + gui.forced_mouse_focus = false; gui.mouse_focus_mask = 0; break; } else { @@ -2113,45 +2082,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { over = _gui_find_control(mpos); } - if (gui.drag_data.get_type() == Variant::NIL && over && !gui.modal_stack.empty()) { - - Control *top = gui.modal_stack.back()->get(); - - if (over != top && !top->is_a_parent_of(over)) { - - PopupMenu *popup_menu = Object::cast_to<PopupMenu>(top); - MenuButton *popup_menu_parent = NULL; - MenuButton *menu_button = Object::cast_to<MenuButton>(over); - - if (popup_menu) { - popup_menu_parent = Object::cast_to<MenuButton>(popup_menu->get_parent()); - if (!popup_menu_parent) { - // Go through the parents to see if there's a MenuButton at the end. - while (Object::cast_to<PopupMenu>(popup_menu->get_parent())) { - popup_menu = Object::cast_to<PopupMenu>(popup_menu->get_parent()); - } - popup_menu_parent = Object::cast_to<MenuButton>(popup_menu->get_parent()); - } - } - - // If the mouse is over a menu button, this menu will open automatically - // if there is already a pop-up menu open at the same hierarchical level. - if (popup_menu_parent && menu_button && popup_menu_parent->is_switch_on_hover() && - !menu_button->is_disabled() && menu_button->is_switch_on_hover() && - (popup_menu_parent->get_parent()->is_a_parent_of(menu_button) || - menu_button->get_parent()->is_a_parent_of(popup_menu))) { - - popup_menu->notification(Control::NOTIFICATION_MODAL_CLOSE); - popup_menu->_modal_stack_remove(); - popup_menu->hide(); - - menu_button->pressed(); - } else { - over = NULL; //nothing can be found outside the modal stack - } - } - } - if (over != gui.mouse_over) { if (gui.mouse_over) { @@ -2167,103 +2097,195 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.mouse_over = over; - if (gui.drag_preview) { - gui.drag_preview->set_position(mpos); - } + DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)InputFilter::get_singleton()->get_default_cursor_shape(); - if (!over) { - OS::get_singleton()->set_cursor_shape((OS::CursorShape)Input::get_singleton()->get_default_cursor_shape()); - return; - } + if (over) { - Transform2D localizer = over->get_global_transform_with_canvas().affine_inverse(); - Size2 pos = localizer.xform(mpos); - Vector2 speed = localizer.basis_xform(mm->get_speed()); - Vector2 rel = localizer.basis_xform(mm->get_relative()); + Transform2D localizer = over->get_global_transform_with_canvas().affine_inverse(); + Size2 pos = localizer.xform(mpos); + Vector2 speed = localizer.basis_xform(mm->get_speed()); + Vector2 rel = localizer.basis_xform(mm->get_relative()); - mm = mm->xformed_by(Transform2D()); //make a copy + mm = mm->xformed_by(Transform2D()); //make a copy - mm->set_global_position(mpos); - mm->set_speed(speed); - mm->set_relative(rel); + mm->set_global_position(mpos); + mm->set_speed(speed); + mm->set_relative(rel); - if (mm->get_button_mask() == 0) { - //nothing pressed + if (mm->get_button_mask() == 0) { + //nothing pressed - bool can_tooltip = true; + bool can_tooltip = true; - if (!gui.modal_stack.empty()) { - if (gui.modal_stack.back()->get() != over && !gui.modal_stack.back()->get()->is_a_parent_of(over)) - can_tooltip = false; - } + bool is_tooltip_shown = false; + + if (gui.tooltip_popup) { + if (can_tooltip && gui.tooltip) { + String tooltip = _gui_get_tooltip(over, gui.tooltip->get_global_transform().xform_inv(mpos)); - bool is_tooltip_shown = false; + if (tooltip.length() == 0) + _gui_cancel_tooltip(); + else if (gui.tooltip_label) { + if (tooltip == gui.tooltip_label->get_text()) { + is_tooltip_shown = true; + } + } else { - if (gui.tooltip_popup) { - if (can_tooltip && gui.tooltip) { - String tooltip = _gui_get_tooltip(over, gui.tooltip->get_global_transform().xform_inv(mpos)); + Variant t = gui.tooltip_popup->call("get_tooltip_text"); - if (tooltip.length() == 0) - _gui_cancel_tooltip(); - else if (gui.tooltip_label) { - if (tooltip == gui.tooltip_label->get_text()) { - is_tooltip_shown = true; + if (t.get_type() == Variant::STRING) { + if (tooltip == String(t)) { + is_tooltip_shown = true; + } + } else { + is_tooltip_shown = true; //well, nothing to compare against, likely using custom control, so if it changes there is nothing we can do + } } - } else if (tooltip == String(gui.tooltip_popup->call("get_tooltip_text"))) { - is_tooltip_shown = true; - } - } else - _gui_cancel_tooltip(); + } else + _gui_cancel_tooltip(); + } + + if (can_tooltip && !is_tooltip_shown) { + + gui.tooltip = over; + gui.tooltip_pos = over->get_screen_transform().xform(pos); //(parent_xform * get_transform()).affine_inverse().xform(pos); + gui.tooltip_timer = gui.tooltip_delay; + } } - if (can_tooltip && !is_tooltip_shown) { + //pos = gui.focus_inv_xform.xform(pos); - gui.tooltip = over; - gui.tooltip_pos = mpos; //(parent_xform * get_transform()).affine_inverse().xform(pos); - gui.tooltip_timer = gui.tooltip_delay; + mm->set_position(pos); + + Control::CursorShape cursor_shape = Control::CURSOR_ARROW; + { + Control *c = over; + Vector2 cpos = pos; + while (c) { + cursor_shape = c->get_cursor_shape(cpos); + cpos = c->get_transform().xform(cpos); + if (cursor_shape != Control::CURSOR_ARROW) + break; + if (c->data.mouse_filter == Control::MOUSE_FILTER_STOP) + break; + if (c->is_set_as_toplevel()) + break; + c = c->get_parent_control(); + } } + + ds_cursor_shape = (DisplayServer::CursorShape)cursor_shape; + + if (over && over->can_process()) { + _gui_call_input(over, mm); + } + + set_input_as_handled(); } - //pos = gui.focus_inv_xform.xform(pos); + if (gui.drag_data.get_type() != Variant::NIL) { + //handle dragandrop - mm->set_position(pos); + if (gui.drag_preview) { + gui.drag_preview->set_position(mpos); + } - Control::CursorShape cursor_shape = Control::CURSOR_ARROW; - { - Control *c = over; - Vector2 cpos = pos; - while (c) { - cursor_shape = c->get_cursor_shape(cpos); - cpos = c->get_transform().xform(cpos); - if (cursor_shape != Control::CURSOR_ARROW) - break; - if (c->data.mouse_filter == Control::MOUSE_FILTER_STOP) - break; - if (c->is_set_as_toplevel()) - break; - c = c->get_parent_control(); + gui.drag_mouse_over = over; + gui.drag_mouse_over_pos = Vector2(); + + //find the window this is above of + + //see if there is an embedder + Viewport *embedder = nullptr; + Vector2 viewport_pos; + + if (is_embedding_subwindows()) { + embedder = this; + viewport_pos = mpos; + } else { + //not an embeder, but may be a subwindow of an embedder + Window *w = Object::cast_to<Window>(this); + if (w) { + if (w->is_embedded()) { + embedder = w->_get_embedder(); + + Transform2D ai = (get_final_transform().affine_inverse() * _get_input_pre_xform()).affine_inverse(); + + viewport_pos = ai.xform(mpos) + w->get_position(); //to parent coords + } + } } - } - OS::get_singleton()->set_cursor_shape((OS::CursorShape)cursor_shape); + Viewport *viewport_under = nullptr; - if (over && over->can_process()) { - _gui_call_input(over, mm); - } + if (embedder) { + //use embedder logic - set_input_as_handled(); + for (int i = embedder->gui.sub_windows.size() - 1; i >= 0; i--) { + Window *sw = embedder->gui.sub_windows[i].window; + Rect2 swrect = Rect2i(sw->get_position(), sw->get_size()); + if (!sw->get_flag(Window::FLAG_BORDERLESS)) { + int title_height = sw->get_theme_constant("title_height"); + swrect.position.y -= title_height; + swrect.size.y += title_height; + } - if (gui.drag_data.get_type() != Variant::NIL && mm->get_button_mask() & BUTTON_MASK_LEFT) { + if (swrect.has_point(viewport_pos)) { + viewport_under = sw; + viewport_pos -= sw->get_position(); + } + } - bool can_drop = _gui_drop(over, pos, true); + if (!viewport_under) { + //not in a subwindow, likely in embedder + viewport_under = embedder; + } + } else { + //use displayserver logic + Vector2i screen_mouse_pos = DisplayServer::get_singleton()->mouse_get_position(); + + DisplayServer::WindowID window_id = DisplayServer::get_singleton()->get_window_at_screen_position(screen_mouse_pos); + + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + ObjectID object_under = DisplayServer::get_singleton()->window_get_attached_instance_id(window_id); + + if (object_under != ObjectID()) { //fetch window + Window *w = Object::cast_to<Window>(ObjectDB::get_instance(object_under)); + if (w) { + viewport_under = w; + viewport_pos = screen_mouse_pos - w->get_position(); + } + } + } + } + + if (viewport_under) { + Transform2D ai = (viewport_under->get_final_transform().affine_inverse() * viewport_under->_get_input_pre_xform()); + viewport_pos = ai.xform(viewport_pos); + //find control under at pos + gui.drag_mouse_over = viewport_under->_gui_find_control(viewport_pos); + if (gui.drag_mouse_over) { + Transform2D localizer = gui.drag_mouse_over->get_global_transform_with_canvas().affine_inverse(); + gui.drag_mouse_over_pos = localizer.xform(viewport_pos); + + if (mm->get_button_mask() & BUTTON_MASK_LEFT) { + + bool can_drop = _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, true); + + if (!can_drop) { + ds_cursor_shape = DisplayServer::CURSOR_FORBIDDEN; + } else { + ds_cursor_shape = DisplayServer::CURSOR_CAN_DROP; + } + } + } - if (!can_drop) { - OS::get_singleton()->set_cursor_shape(OS::CURSOR_FORBIDDEN); } else { - OS::get_singleton()->set_cursor_shape(OS::CURSOR_CAN_DROP); + gui.drag_mouse_over = nullptr; } - //change mouse accordingly i guess } + + DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape); } Ref<InputEventScreenTouch> touch_event = p_event; @@ -2275,14 +2297,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Control *over = _gui_find_control(pos); if (over) { - if (!gui.modal_stack.empty()) { - - Control *top = gui.modal_stack.back()->get(); - if (over != top && !top->is_a_parent_of(over)) { - - return; - } - } if (over->can_process()) { touch_event = touch_event->xformed_by(Transform2D()); //make a copy @@ -2348,14 +2362,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } if (over) { - if (!gui.modal_stack.empty()) { - - Control *top = gui.modal_stack.back()->get(); - if (over != top && !top->is_a_parent_of(over)) { - - return; - } - } if (over->can_process()) { Transform2D localizer = over->get_global_transform_with_canvas().affine_inverse(); @@ -2399,21 +2405,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } } - if (p_event->is_pressed() && p_event->is_action("ui_cancel") && !gui.modal_stack.empty()) { - - _gui_sort_modal_stack(); - Control *top = gui.modal_stack.back()->get(); - if (!top->data.modal_exclusive) { - - top->notification(Control::NOTIFICATION_MODAL_CLOSE); - top->_modal_stack_remove(); - top->hide(); - // Close modal, set input as handled - set_input_as_handled(); - return; - } - } - Control *from = gui.key_focus ? gui.key_focus : NULL; //hmm //keyboard focus @@ -2426,7 +2417,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (from && p_event->is_pressed()) { Control *next = NULL; - Input *input = Input::get_singleton(); + InputFilter *input = InputFilter::get_singleton(); if (p_event->is_action_pressed("ui_focus_next") && input->is_action_just_pressed("ui_focus_next")) { @@ -2472,61 +2463,10 @@ List<Control *>::Element *Viewport::_gui_add_root_control(Control *p_control) { return gui.roots.push_back(p_control); } -List<Control *>::Element *Viewport::_gui_add_subwindow_control(Control *p_control) { - - p_control->connect("visibility_changed", callable_mp(this, &Viewport::_subwindow_visibility_changed)); - - if (p_control->is_visible_in_tree()) { - gui.subwindow_order_dirty = true; - gui.subwindows.push_back(p_control); - } - - return gui.all_known_subwindows.push_back(p_control); -} - -void Viewport::_gui_set_subwindow_order_dirty() { - gui.subwindow_order_dirty = true; -} - void Viewport::_gui_set_root_order_dirty() { gui.roots_order_dirty = true; } -void Viewport::_gui_remove_modal_control(List<Control *>::Element *MI) { - - gui.modal_stack.erase(MI); -} - -void Viewport::_gui_remove_from_modal_stack(List<Control *>::Element *MI, ObjectID p_prev_focus_owner) { - - //transfer the focus stack to the next - - List<Control *>::Element *next = MI->next(); - - gui.modal_stack.erase(MI); - - if (p_prev_focus_owner.is_valid()) { - - // for previous window in stack, pass the focus so it feels more - // natural - - if (!next) { //top of stack - - Object *pfo = ObjectDB::get_instance(p_prev_focus_owner); - Control *pfoc = Object::cast_to<Control>(pfo); - if (!pfoc) - return; - - if (!pfoc->is_inside_tree() || !pfoc->is_visible_in_tree()) - return; - pfoc->grab_focus(); - } else { - - next->get()->_modal_set_prev_focus_owner(p_prev_focus_owner); - } - } -} - void Viewport::_gui_force_drag(Control *p_base, const Variant &p_data, Control *p_control) { ERR_FAIL_COND_MSG(p_data.get_type() == Variant::NIL, "Drag data must be a value."); @@ -2563,21 +2503,6 @@ void Viewport::_gui_remove_root_control(List<Control *>::Element *RI) { gui.roots.erase(RI); } -void Viewport::_gui_remove_subwindow_control(List<Control *>::Element *SI) { - - ERR_FAIL_COND(!SI); - - Control *control = SI->get(); - - control->disconnect("visibility_changed", callable_mp(this, &Viewport::_subwindow_visibility_changed)); - - List<Control *>::Element *E = gui.subwindows.find(control); - if (E) - gui.subwindows.erase(E); - - gui.all_known_subwindows.erase(SI); -} - void Viewport::_gui_unfocus_control(Control *p_control) { if (gui.key_focus == p_control) { @@ -2605,6 +2530,8 @@ void Viewport::_gui_hid_control(Control *p_control) { _gui_remove_focus(); if (gui.mouse_over == p_control) gui.mouse_over = NULL; + if (gui.drag_mouse_over == p_control) + gui.drag_mouse_over = NULL; if (gui.tooltip == p_control) _gui_cancel_tooltip(); } @@ -2613,6 +2540,7 @@ void Viewport::_gui_remove_control(Control *p_control) { if (gui.mouse_focus == p_control) { gui.mouse_focus = NULL; + gui.forced_mouse_focus = false; gui.mouse_focus_mask = 0; } if (gui.last_mouse_focus == p_control) { @@ -2622,11 +2550,10 @@ void Viewport::_gui_remove_control(Control *p_control) { gui.key_focus = NULL; if (gui.mouse_over == p_control) gui.mouse_over = NULL; + if (gui.drag_mouse_over == p_control) + gui.drag_mouse_over = NULL; if (gui.tooltip == p_control) gui.tooltip = NULL; - if (gui.tooltip_popup == p_control) { - _gui_cancel_tooltip(); - } } void Viewport::_gui_remove_focus() { @@ -2638,11 +2565,6 @@ void Viewport::_gui_remove_focus() { } } -bool Viewport::_gui_is_modal_on_top(const Control *p_control) { - - return (gui.modal_stack.size() && gui.modal_stack.back()->get() == p_control); -} - bool Viewport::_gui_control_has_focus(const Control *p_control) { return gui.key_focus == p_control; @@ -2672,6 +2594,7 @@ void Viewport::_drop_mouse_focus() { Control *c = gui.mouse_focus; int mask = gui.mouse_focus_mask; gui.mouse_focus = NULL; + gui.forced_mouse_focus = false; gui.mouse_focus_mask = 0; for (int i = 0; i < 3; i++) { @@ -2714,22 +2637,6 @@ void Viewport::_drop_physics_mouseover() { #endif } -List<Control *>::Element *Viewport::_gui_show_modal(Control *p_control) { - - List<Control *>::Element *node = gui.modal_stack.push_back(p_control); - if (gui.key_focus) - p_control->_modal_set_prev_focus_owner(gui.key_focus->get_instance_id()); - else - p_control->_modal_set_prev_focus_owner(ObjectID()); - - if (gui.mouse_focus && !p_control->is_a_parent_of(gui.mouse_focus) && !gui.mouse_click_grabber) { - - _drop_mouse_focus(); - } - - return node; -} - Control *Viewport::_gui_get_focus_owner() { return gui.key_focus; @@ -2798,44 +2705,390 @@ void Viewport::_post_gui_grab_click_focus() { /////////////////////////////// -void Viewport::input(const Ref<InputEvent> &p_event) { +void Viewport::input_text(const String &p_text) { + + if (gui.subwindow_focused) { + gui.subwindow_focused->input_text(p_text); + return; + } + + if (gui.key_focus) { + gui.key_focus->call("set_text", p_text); + } +} +Viewport::SubWindowResize Viewport::_sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point) { + + if (p_subwindow->get_flag(Window::FLAG_BORDERLESS)) { + return SUB_WINDOW_RESIZE_DISABLED; + } + + Rect2i r = Rect2i(p_subwindow->get_position(), p_subwindow->get_size()); + + int title_height = p_subwindow->get_theme_constant("title_height"); + + r.position.y -= title_height; + r.size.y += title_height; + + if (r.has_point(p_point)) { + return SUB_WINDOW_RESIZE_DISABLED; //it's inside, so no resize + } + + 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); + 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); + + int limit = p_subwindow->get_theme_constant("resize_margin"); + + if (ABS(dist_x) > limit) { + return SUB_WINDOW_RESIZE_DISABLED; + } + + if (ABS(dist_y) > limit) { + return SUB_WINDOW_RESIZE_DISABLED; + } + + if (dist_x < 0 && dist_y < 0) { + return SUB_WINDOW_RESIZE_TOP_LEFT; + } + + if (dist_x == 0 && dist_y < 0) { + return SUB_WINDOW_RESIZE_TOP; + } + + if (dist_x > 0 && dist_y < 0) { + return SUB_WINDOW_RESIZE_TOP_RIGHT; + } + + if (dist_x < 0 && dist_y == 0) { + return SUB_WINDOW_RESIZE_LEFT; + } + + if (dist_x > 0 && dist_y == 0) { + return SUB_WINDOW_RESIZE_RIGHT; + } + + if (dist_x < 0 && dist_y > 0) { + return SUB_WINDOW_RESIZE_BOTTOM_LEFT; + } + + if (dist_x == 0 && dist_y > 0) { + return SUB_WINDOW_RESIZE_BOTTOM; + } + + if (dist_x > 0 && dist_y > 0) { + return SUB_WINDOW_RESIZE_BOTTOM_RIGHT; + } + + return SUB_WINDOW_RESIZE_DISABLED; +} +bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { + + if (gui.subwindow_drag != SUB_WINDOW_DRAG_DISABLED) { + + ERR_FAIL_COND_V(gui.subwindow_focused == nullptr, false); + + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + + if (gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE) { + if (gui.subwindow_drag_close_rect.has_point(mb->get_position())) { + //close window + gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_CLOSE_REQUEST); + } + } + gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED; + if (gui.subwindow_focused != nullptr) { //may have been erased + _sub_window_update(gui.subwindow_focused); + } + } + + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + + if (gui.subwindow_drag == SUB_WINDOW_DRAG_MOVE) { + Vector2 diff = mm->get_position() - gui.subwindow_drag_from; + Rect2i new_rect(gui.subwindow_drag_pos + diff, gui.subwindow_focused->get_size()); + gui.subwindow_focused->_rect_changed_callback(new_rect); + } + if (gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE) { + gui.subwindow_drag_close_inside = gui.subwindow_drag_close_rect.has_point(mm->get_position()); + } + if (gui.subwindow_drag == SUB_WINDOW_DRAG_RESIZE) { + Vector2i diff = mm->get_position() - gui.subwindow_drag_from; + Size2i min_size = gui.subwindow_focused->get_min_size(); + if (gui.subwindow_focused->is_wrapping_controls()) { + Size2i cms = gui.subwindow_focused->get_contents_minimum_size(); + min_size.x = MAX(cms.x, min_size.x); + min_size.y = MAX(cms.y, min_size.y); + } + min_size.x = MAX(min_size.x, 1); + min_size.y = MAX(min_size.y, 1); + + Rect2i r = gui.subwindow_resize_from_rect; + + Size2i limit = r.size - min_size; + + switch (gui.subwindow_resize_mode) { + case SUB_WINDOW_RESIZE_TOP_LEFT: { + + diff.x = MIN(diff.x, limit.x); + diff.y = MIN(diff.y, limit.y); + r.position += diff; + r.size -= diff; + } break; + case SUB_WINDOW_RESIZE_TOP: { + diff.x = 0; + diff.y = MIN(diff.y, limit.y); + r.position += diff; + r.size -= diff; + } break; + case SUB_WINDOW_RESIZE_TOP_RIGHT: { + diff.x = MAX(diff.x, -limit.x); + diff.y = MIN(diff.y, limit.y); + r.position.y += diff.y; + r.size.y -= diff.y; + r.size.x += diff.x; + } break; + case SUB_WINDOW_RESIZE_LEFT: { + diff.x = MIN(diff.x, limit.x); + diff.y = 0; + r.position += diff; + r.size -= diff; + + } break; + case SUB_WINDOW_RESIZE_RIGHT: { + diff.x = MAX(diff.x, -limit.x); + r.size.x += diff.x; + } break; + case SUB_WINDOW_RESIZE_BOTTOM_LEFT: { + diff.x = MIN(diff.x, limit.x); + diff.y = MAX(diff.y, -limit.y); + r.position.x += diff.x; + r.size.x -= diff.x; + r.size.y += diff.y; + + } break; + case SUB_WINDOW_RESIZE_BOTTOM: { + diff.y = MAX(diff.y, -limit.y); + r.size.y += diff.y; + } break; + case SUB_WINDOW_RESIZE_BOTTOM_RIGHT: { + diff.x = MAX(diff.x, -limit.x); + diff.y = MAX(diff.y, -limit.y); + r.size += diff; + + } break; + default: { + } + } + + gui.subwindow_focused->_rect_changed_callback(r); + } + + if (gui.subwindow_focused) { //may have been erased + _sub_window_update(gui.subwindow_focused); + } + } + + return true; //handled + } + Ref<InputEventMouseButton> mb = p_event; + //if the event is a mouse button, we need to check whether another window was clicked + + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + + bool click_on_window = false; + for (int i = gui.sub_windows.size() - 1; i >= 0; i--) { + SubWindow &sw = gui.sub_windows.write[i]; + + //clicked inside window? + + Rect2i r = Rect2i(sw.window->get_position(), sw.window->get_size()); + + if (!sw.window->get_flag(Window::FLAG_BORDERLESS)) { + //check top bar + int title_height = sw.window->get_theme_constant("title_height"); + Rect2i title_bar = r; + title_bar.position.y -= title_height; + title_bar.size.y = title_height; + + if (title_bar.has_point(mb->get_position())) { + + click_on_window = true; + + int close_h_ofs = sw.window->get_theme_constant("close_h_ofs"); + int close_v_ofs = sw.window->get_theme_constant("close_v_ofs"); + Ref<Texture2D> close_icon = sw.window->get_theme_icon("close"); + + Rect2 close_rect; + close_rect.position = Vector2(r.position.x + r.size.x - close_v_ofs, r.position.y - close_h_ofs); + close_rect.size = close_icon->get_size(); + + if (gui.subwindow_focused != sw.window) { + //refocus + _sub_window_grab_focus(sw.window); + } + + if (close_rect.has_point(mb->get_position())) { + + gui.subwindow_drag = SUB_WINDOW_DRAG_CLOSE; + gui.subwindow_drag_close_inside = true; //starts inside + gui.subwindow_drag_close_rect = close_rect; + } else { + + gui.subwindow_drag = SUB_WINDOW_DRAG_MOVE; + } + + gui.subwindow_drag_from = mb->get_position(); + gui.subwindow_drag_pos = sw.window->get_position(); + + _sub_window_update(sw.window); + } else { + gui.subwindow_resize_mode = _sub_window_get_resize_margin(sw.window, mb->get_position()); + if (gui.subwindow_resize_mode != SUB_WINDOW_RESIZE_DISABLED) { + gui.subwindow_resize_from_rect = r; + gui.subwindow_drag_from = mb->get_position(); + gui.subwindow_drag = SUB_WINDOW_DRAG_RESIZE; + click_on_window = true; + } + } + } + if (!click_on_window && r.has_point(mb->get_position())) { + //clicked, see if it needs to fetch focus + if (gui.subwindow_focused != sw.window) { + //refocus + _sub_window_grab_focus(sw.window); + } + + click_on_window = true; + } + + if (click_on_window) { + break; + } + } + + if (!click_on_window && gui.subwindow_focused) { + //no window found and clicked, remove focus + _sub_window_grab_focus(nullptr); + } + } + + if (gui.subwindow_focused) { + + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid()) { + + SubWindowResize resize = _sub_window_get_resize_margin(gui.subwindow_focused, mm->get_position()); + if (resize != SUB_WINDOW_RESIZE_DISABLED) { + + DisplayServer::CursorShape shapes[SUB_WINDOW_RESIZE_MAX] = { + DisplayServer::CURSOR_ARROW, + DisplayServer::CURSOR_FDIAGSIZE, + DisplayServer::CURSOR_VSIZE, + DisplayServer::CURSOR_BDIAGSIZE, + DisplayServer::CURSOR_HSIZE, + DisplayServer::CURSOR_HSIZE, + DisplayServer::CURSOR_BDIAGSIZE, + DisplayServer::CURSOR_VSIZE, + DisplayServer::CURSOR_FDIAGSIZE + }; + + DisplayServer::get_singleton()->cursor_set_shape(shapes[resize]); + + return true; //reserved for showing the resize cursor + } + } + } + + if (gui.subwindow_drag != SUB_WINDOW_DRAG_DISABLED) { + return true; // dragging, don't pass the event + } + + if (!gui.subwindow_focused) { + return false; + } + + Transform2D window_ofs; + window_ofs.set_origin(-gui.subwindow_focused->get_position()); + + Ref<InputEvent> ev = p_event->xformed_by(window_ofs); + + gui.subwindow_focused->_window_input(ev); + + return true; +} + +void Viewport::input(const Ref<InputEvent> &p_event, bool p_local_coords) { ERR_FAIL_COND(!is_inside_tree()); + if (disable_input) + return; + + if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_a_parent_of(this)) { + return; + } + local_input_handled = false; + Ref<InputEvent> ev; + if (!p_local_coords) { + ev = _make_input_local(p_event); + } else { + ev = p_event; + } + + if (is_embedding_subwindows() && _sub_windows_forward_input(p_event)) { + set_input_as_handled(); + return; + } + if (!is_input_handled()) { - get_tree()->_call_input_pause(input_group, "_input", p_event); //not a bug, must happen before GUI, order is _input -> gui input -> _unhandled input + get_tree()->_call_input_pause(input_group, "_input", ev, this); //not a bug, must happen before GUI, order is _input -> gui input -> _unhandled input } if (!is_input_handled()) { - _gui_input_event(p_event); + _gui_input_event(ev); } - //get_tree()->call_group(SceneTree::GROUP_CALL_REVERSE|SceneTree::GROUP_CALL_REALTIME|SceneTree::GROUP_CALL_MULIILEVEL,gui_input_group,"_gui_input",p_event); //special one for GUI, as controls use their own process check + //get_tree()->call_group(SceneTree::GROUP_CALL_REVERSE|SceneTree::GROUP_CALL_REALTIME|SceneTree::GROUP_CALL_MULIILEVEL,gui_input_group,"_gui_input",ev); //special one for GUI, as controls use their own process check } -void Viewport::unhandled_input(const Ref<InputEvent> &p_event) { +void Viewport::unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coords) { ERR_FAIL_COND(!is_inside_tree()); - get_tree()->_call_input_pause(unhandled_input_group, "_unhandled_input", p_event); + if (disable_input) + return; + + if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_a_parent_of(this)) { + return; + } + + Ref<InputEvent> ev; + if (!p_local_coords) { + ev = _make_input_local(p_event); + } else { + ev = p_event; + } + + get_tree()->_call_input_pause(unhandled_input_group, "_unhandled_input", ev, this); //call_group(GROUP_CALL_REVERSE|GROUP_CALL_REALTIME|GROUP_CALL_MULIILEVEL,"unhandled_input","_unhandled_input",ev); - if (!get_tree()->input_handled && Object::cast_to<InputEventKey>(*p_event) != NULL) { - get_tree()->_call_input_pause(unhandled_key_input_group, "_unhandled_key_input", p_event); + if (!is_input_handled() && Object::cast_to<InputEventKey>(*ev) != NULL) { + get_tree()->_call_input_pause(unhandled_key_input_group, "_unhandled_key_input", ev, this); //call_group(GROUP_CALL_REVERSE|GROUP_CALL_REALTIME|GROUP_CALL_MULIILEVEL,"unhandled_key_input","_unhandled_key_input",ev); } - if (physics_object_picking && !get_tree()->input_handled) { + if (physics_object_picking && !is_input_handled()) { - if (Input::get_singleton()->get_mouse_mode() != Input::MOUSE_MODE_CAPTURED && - (Object::cast_to<InputEventMouseButton>(*p_event) || - Object::cast_to<InputEventMouseMotion>(*p_event) || - Object::cast_to<InputEventScreenDrag>(*p_event) || - Object::cast_to<InputEventScreenTouch>(*p_event) || - Object::cast_to<InputEventKey>(*p_event) //to remember state + if (InputFilter::get_singleton()->get_mouse_mode() != InputFilter::MOUSE_MODE_CAPTURED && + (Object::cast_to<InputEventMouseButton>(*ev) || + Object::cast_to<InputEventMouseMotion>(*ev) || + Object::cast_to<InputEventScreenDrag>(*ev) || + Object::cast_to<InputEventScreenTouch>(*ev) || + Object::cast_to<InputEventKey>(*ev) //to remember state )) { - physics_picking_events.push_back(p_event); + physics_picking_events.push_back(ev); } } } @@ -2877,30 +3130,6 @@ bool Viewport::is_using_own_world() const { return own_world.is_valid(); } -void Viewport::set_attach_to_screen_rect(const Rect2 &p_rect) { - - VS::get_singleton()->viewport_attach_to_screen(viewport, p_rect); - to_screen_rect = p_rect; -} - -Rect2 Viewport::get_attach_to_screen_rect() const { - - return to_screen_rect; -} - -void Viewport::set_use_render_direct_to_screen(bool p_render_direct_to_screen) { - - if (p_render_direct_to_screen == render_direct_to_screen) - return; - - render_direct_to_screen = p_render_direct_to_screen; - VS::get_singleton()->viewport_set_render_direct_to_screen(viewport, p_render_direct_to_screen); -} - -bool Viewport::is_using_render_direct_to_screen() const { - return render_direct_to_screen; -} - void Viewport::set_physics_object_picking(bool p_enable) { physics_object_picking = p_enable; @@ -2925,11 +3154,6 @@ Vector2 Viewport::get_camera_rect_size() const { return size; } -bool Viewport::gui_has_modal_stack() const { - - return gui.modal_stack.size(); -} - void Viewport::set_disable_input(bool p_disable) { disable_input = p_disable; } @@ -2943,10 +3167,6 @@ Variant Viewport::gui_get_drag_data() const { return gui.drag_data; } -Control *Viewport::get_modal_stack_top() const { - return gui.modal_stack.size() ? gui.modal_stack.back()->get() : NULL; -} - String Viewport::get_configuration_warning() const { /*if (get_parent() && !Object::cast_to<Control>(get_parent()) && !render_target) { @@ -3017,7 +3237,17 @@ void Viewport::set_input_as_handled() { local_input_handled = true; } else { ERR_FAIL_COND(!is_inside_tree()); - get_tree()->set_input_as_handled(); + Viewport *vp = this; + while (true) { + if (Object::cast_to<Window>(vp)) { + break; + } + if (!vp->get_parent()) { + break; + } + vp = vp->get_parent()->get_viewport(); + } + vp->set_input_as_handled(); } } @@ -3025,8 +3255,17 @@ bool Viewport::is_input_handled() const { if (handle_input_locally) { return local_input_handled; } else { - ERR_FAIL_COND_V(!is_inside_tree(), false); - return get_tree()->is_input_handled(); + const Viewport *vp = this; + while (true) { + if (Object::cast_to<Window>(vp)) { + break; + } + if (!vp->get_parent()) { + break; + } + vp = vp->get_parent()->get_viewport(); + } + return vp->is_input_handled(); } } @@ -3086,13 +3325,47 @@ void Viewport::_propagate_update_default_repeat(Node *p_node) { } } -void Viewport::_bind_methods() { +DisplayServer::WindowID Viewport::get_window_id() const { + return DisplayServer::MAIN_WINDOW_ID; +} - ClassDB::bind_method(D_METHOD("set_use_arvr", "use"), &Viewport::set_use_arvr); - ClassDB::bind_method(D_METHOD("use_arvr"), &Viewport::use_arvr); +Viewport *Viewport::get_parent_viewport() const { + ERR_FAIL_COND_V(!is_inside_tree(), nullptr); + if (!get_parent()) { + return nullptr; //root viewport + } + + return get_parent()->get_viewport(); +} + +void Viewport::set_embed_subwindows_hint(bool p_embed) { + gui.embed_subwindows_hint = p_embed; +} +bool Viewport::get_embed_subwindows_hint() const { + return gui.embed_subwindows_hint; +} +bool Viewport::is_embedding_subwindows() const { + return gui.embed_subwindows_hint; +} + +void Viewport::pass_mouse_focus_to(Viewport *p_viewport, Control *p_control) { + ERR_FAIL_NULL(p_viewport); + ERR_FAIL_NULL(p_control); + + if (gui.mouse_focus) { + p_viewport->gui.mouse_focus = p_control; + p_viewport->gui.mouse_focus_mask = gui.mouse_focus_mask; + p_viewport->gui.key_focus = p_control; + p_viewport->gui.forced_mouse_focus = true; + + gui.mouse_focus = nullptr; + gui.forced_mouse_focus = false; + gui.mouse_focus_mask = 0; + } +} + +void Viewport::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_size", "size"), &Viewport::set_size); - ClassDB::bind_method(D_METHOD("get_size"), &Viewport::get_size); ClassDB::bind_method(D_METHOD("set_world_2d", "world_2d"), &Viewport::set_world_2d); ClassDB::bind_method(D_METHOD("get_world_2d"), &Viewport::get_world_2d); ClassDB::bind_method(D_METHOD("find_world_2d"), &Viewport::find_world_2d); @@ -3111,22 +3384,6 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transparent_background", "enable"), &Viewport::set_transparent_background); ClassDB::bind_method(D_METHOD("has_transparent_background"), &Viewport::has_transparent_background); - ClassDB::bind_method(D_METHOD("_vp_input"), &Viewport::_vp_input); - ClassDB::bind_method(D_METHOD("_vp_input_text", "text"), &Viewport::_vp_input_text); - ClassDB::bind_method(D_METHOD("_vp_unhandled_input"), &Viewport::_vp_unhandled_input); - - ClassDB::bind_method(D_METHOD("set_size_override", "enable", "size", "margin"), &Viewport::set_size_override, DEFVAL(Size2(-1, -1)), DEFVAL(Size2(0, 0))); - ClassDB::bind_method(D_METHOD("get_size_override"), &Viewport::get_size_override); - ClassDB::bind_method(D_METHOD("is_size_override_enabled"), &Viewport::is_size_override_enabled); - ClassDB::bind_method(D_METHOD("set_size_override_stretch", "enabled"), &Viewport::set_size_override_stretch); - ClassDB::bind_method(D_METHOD("is_size_override_stretch_enabled"), &Viewport::is_size_override_stretch_enabled); - - ClassDB::bind_method(D_METHOD("set_clear_mode", "mode"), &Viewport::set_clear_mode); - ClassDB::bind_method(D_METHOD("get_clear_mode"), &Viewport::get_clear_mode); - - ClassDB::bind_method(D_METHOD("set_update_mode", "mode"), &Viewport::set_update_mode); - ClassDB::bind_method(D_METHOD("get_update_mode"), &Viewport::get_update_mode); - ClassDB::bind_method(D_METHOD("set_msaa", "msaa"), &Viewport::set_msaa); ClassDB::bind_method(D_METHOD("get_msaa"), &Viewport::get_msaa); @@ -3141,8 +3398,9 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("get_physics_object_picking"), &Viewport::get_physics_object_picking); ClassDB::bind_method(D_METHOD("get_viewport_rid"), &Viewport::get_viewport_rid); - ClassDB::bind_method(D_METHOD("input", "local_event"), &Viewport::input); - ClassDB::bind_method(D_METHOD("unhandled_input", "local_event"), &Viewport::unhandled_input); + ClassDB::bind_method(D_METHOD("input_text", "text"), &Viewport::input_text); + ClassDB::bind_method(D_METHOD("input", "event", "in_local_coords"), &Viewport::input, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("unhandled_input", "event", "in_local_coords"), &Viewport::unhandled_input, DEFVAL(false)); ClassDB::bind_method(D_METHOD("update_worlds"), &Viewport::update_worlds); @@ -3156,19 +3414,13 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_as_audio_listener_2d", "enable"), &Viewport::set_as_audio_listener_2d); ClassDB::bind_method(D_METHOD("is_audio_listener_2d"), &Viewport::is_audio_listener_2d); - ClassDB::bind_method(D_METHOD("set_attach_to_screen_rect", "rect"), &Viewport::set_attach_to_screen_rect); - ClassDB::bind_method(D_METHOD("set_use_render_direct_to_screen", "enable"), &Viewport::set_use_render_direct_to_screen); - ClassDB::bind_method(D_METHOD("is_using_render_direct_to_screen"), &Viewport::is_using_render_direct_to_screen); ClassDB::bind_method(D_METHOD("get_mouse_position"), &Viewport::get_mouse_position); ClassDB::bind_method(D_METHOD("warp_mouse", "to_position"), &Viewport::warp_mouse); - ClassDB::bind_method(D_METHOD("gui_has_modal_stack"), &Viewport::gui_has_modal_stack); ClassDB::bind_method(D_METHOD("gui_get_drag_data"), &Viewport::gui_get_drag_data); ClassDB::bind_method(D_METHOD("gui_is_dragging"), &Viewport::gui_is_dragging); - ClassDB::bind_method(D_METHOD("get_modal_stack_top"), &Viewport::get_modal_stack_top); - ClassDB::bind_method(D_METHOD("set_disable_input", "disable"), &Viewport::set_disable_input); ClassDB::bind_method(D_METHOD("is_input_disabled"), &Viewport::is_input_disabled); @@ -3194,13 +3446,13 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_default_canvas_item_texture_filter", "mode"), &Viewport::set_default_canvas_item_texture_filter); ClassDB::bind_method(D_METHOD("get_default_canvas_item_texture_filter"), &Viewport::get_default_canvas_item_texture_filter); + ClassDB::bind_method(D_METHOD("set_embed_subwindows_hint", "enable"), &Viewport::set_embed_subwindows_hint); + ClassDB::bind_method(D_METHOD("get_embed_subwindows_hint"), &Viewport::get_embed_subwindows_hint); + ClassDB::bind_method(D_METHOD("is_embedding_subwindows"), &Viewport::is_embedding_subwindows); + ClassDB::bind_method(D_METHOD("set_default_canvas_item_texture_repeat", "mode"), &Viewport::set_default_canvas_item_texture_repeat); ClassDB::bind_method(D_METHOD("get_default_canvas_item_texture_repeat"), &Viewport::get_default_canvas_item_texture_repeat); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "arvr"), "set_use_arvr", "use_arvr"); - - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "size_override_stretch"), "set_size_override_stretch", "is_size_override_stretch_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world"), "set_use_own_world", "is_using_own_world"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world", PROPERTY_HINT_RESOURCE_TYPE, "World"), "set_world", "get_world"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_2d", PROPERTY_HINT_RESOURCE_TYPE, "World2D", 0), "set_world_2d", "get_world_2d"); @@ -3208,11 +3460,7 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handle_input_locally"), "set_handle_input_locally", "is_handling_input_locally"); ADD_GROUP("Rendering", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x,AndroidVR 2x,AndroidVR 4x"), "set_msaa", "get_msaa"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "render_direct_to_screen"), "set_use_render_direct_to_screen", "is_using_render_direct_to_screen"); ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw"); - ADD_GROUP("Render Target", "render_target_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_clear_mode", PROPERTY_HINT_ENUM, "Always,Never,Next Frame"), "set_clear_mode", "get_clear_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_update_mode", PROPERTY_HINT_ENUM, "Disabled,Once,When Visible,Always"), "set_update_mode", "get_update_mode"); ADD_GROUP("Canvas Items", "canvas_item_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,MipmapLinear,MipmapNearest"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter"); 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"); @@ -3224,6 +3472,7 @@ void Viewport::_bind_methods() { ADD_GROUP("GUI", "gui_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_disable_input"), "set_disable_input", "is_input_disabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_snap_controls_to_pixels"), "set_snap_controls_to_pixels", "is_snap_controls_to_pixels_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_embed_subwindows"), "set_embed_subwindows_hint", "get_embed_subwindows_hint"); ADD_GROUP("Shadow Atlas", "shadow_atlas_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_atlas_size"), "set_shadow_atlas_size", "get_shadow_atlas_size"); ADD_PROPERTYI(PropertyInfo(Variant::INT, "shadow_atlas_quad_0", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_shadow_atlas_quadrant_subdiv", "get_shadow_atlas_quadrant_subdiv", 0); @@ -3236,11 +3485,6 @@ void Viewport::_bind_methods() { ADD_SIGNAL(MethodInfo("size_changed")); ADD_SIGNAL(MethodInfo("gui_focus_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); - BIND_ENUM_CONSTANT(UPDATE_DISABLED); - BIND_ENUM_CONSTANT(UPDATE_ONCE); - BIND_ENUM_CONSTANT(UPDATE_WHEN_VISIBLE); - BIND_ENUM_CONSTANT(UPDATE_ALWAYS); - BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED); BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_1); BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_4); @@ -3277,10 +3521,6 @@ void Viewport::_bind_methods() { BIND_ENUM_CONSTANT(MSAA_8X); BIND_ENUM_CONSTANT(MSAA_16X); - BIND_ENUM_CONSTANT(CLEAR_MODE_ALWAYS); - BIND_ENUM_CONSTANT(CLEAR_MODE_NEVER); - BIND_ENUM_CONSTANT(CLEAR_MODE_ONLY_NEXT_FRAME); - BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST); BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR); BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS); @@ -3293,13 +3533,6 @@ void Viewport::_bind_methods() { BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX); } -void Viewport::_subwindow_visibility_changed() { - - // unfortunately, we don't know the sender, i.e. which subwindow changed; - // so we have to check them all. - gui.subwindow_visibility_dirty = true; -} - Viewport::Viewport() { world_2d = Ref<World2D>(memnew(World2D)); @@ -3307,8 +3540,6 @@ Viewport::Viewport() { viewport = VisualServer::get_singleton()->viewport_create(); texture_rid = VisualServer::get_singleton()->viewport_get_texture(viewport); - render_direct_to_screen = false; - default_texture.instance(); default_texture->vp = const_cast<Viewport *>(this); viewport_textures.insert(default_texture.ptr()); @@ -3324,14 +3555,10 @@ Viewport::Viewport() { camera = NULL; override_canvas_transform = false; canvas_layers.insert(NULL); // This eases picking code (interpreted as the canvas of the Viewport) - arvr = false; - size_override = false; - size_override_stretch = false; - size_override_size = Size2(1, 1); + gen_mipmaps = false; //clear=true; - update_mode = UPDATE_WHEN_VISIBLE; physics_object_picking = false; physics_has_last_mousepos = false; @@ -3368,12 +3595,14 @@ Viewport::Viewport() { gui.canvas_sort_index = 0; gui.roots_order_dirty = false; gui.mouse_focus = NULL; + gui.forced_mouse_focus = false; gui.last_mouse_focus = NULL; + gui.subwindow_focused = nullptr; + gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED; msaa = MSAA_DISABLED; debug_draw = DEBUG_DRAW_DISABLED; - clear_mode = CLEAR_MODE_ALWAYS; snap_controls_to_pixels = true; physics_last_mouse_state.alt = false; @@ -3384,6 +3613,8 @@ Viewport::Viewport() { local_input_handled = false; handle_input_locally = true; + size_allocated = false; + default_canvas_item_texture_filter = DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR; default_canvas_item_texture_repeat = DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; } @@ -3398,3 +3629,95 @@ Viewport::~Viewport() { //SpatialSoundServer::get_singleton()->free(internal_listener); //SpatialSound2DServer::get_singleton()->free(internal_listener_2d); } + +///////////////////////////////// + +void SubViewport::set_use_arvr(bool p_use_arvr) { + arvr = p_use_arvr; + + VS::get_singleton()->viewport_set_use_arvr(get_viewport_rid(), arvr); +} + +bool SubViewport::is_using_arvr() { + return arvr; +} + +void SubViewport::set_size(const Size2i &p_size) { + _set_size(p_size, Size2i(), Rect2i(), Transform2D(), true); +} +Size2i SubViewport::get_size() const { + return _get_size(); +} + +void SubViewport::set_update_mode(UpdateMode p_mode) { + + update_mode = p_mode; + VS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), VS::ViewportUpdateMode(p_mode)); +} +SubViewport::UpdateMode SubViewport::get_update_mode() const { + + return update_mode; +} + +void SubViewport::set_clear_mode(ClearMode p_mode) { + + clear_mode = p_mode; + VS::get_singleton()->viewport_set_clear_mode(get_viewport_rid(), VS::ViewportClearMode(p_mode)); +} + +SubViewport::ClearMode SubViewport::get_clear_mode() const { + + return clear_mode; +} + +DisplayServer::WindowID SubViewport::get_window_id() const { + return DisplayServer::INVALID_WINDOW_ID; +} + +void SubViewport::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + VS::get_singleton()->viewport_set_active(get_viewport_rid(), true); + } + if (p_what == NOTIFICATION_EXIT_TREE) { + VS::get_singleton()->viewport_set_active(get_viewport_rid(), false); + } +} + +void SubViewport::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_use_arvr", "use"), &SubViewport::set_use_arvr); + ClassDB::bind_method(D_METHOD("is_using_arvr"), &SubViewport::is_using_arvr); + + ClassDB::bind_method(D_METHOD("set_size", "size"), &SubViewport::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &SubViewport::get_size); + + ClassDB::bind_method(D_METHOD("set_update_mode", "mode"), &SubViewport::set_update_mode); + ClassDB::bind_method(D_METHOD("get_update_mode"), &SubViewport::get_update_mode); + + ClassDB::bind_method(D_METHOD("set_clear_mode", "mode"), &SubViewport::set_clear_mode); + ClassDB::bind_method(D_METHOD("get_clear_mode"), &SubViewport::get_clear_mode); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "arvr"), "set_use_arvr", "is_using_arvr"); + ADD_GROUP("Render Target", "render_target_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_clear_mode", PROPERTY_HINT_ENUM, "Always,Never,Next Frame"), "set_clear_mode", "get_clear_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_update_mode", PROPERTY_HINT_ENUM, "Disabled,Once,When Visible,Always"), "set_update_mode", "get_update_mode"); + + BIND_ENUM_CONSTANT(UPDATE_DISABLED); + BIND_ENUM_CONSTANT(UPDATE_ONCE); + BIND_ENUM_CONSTANT(UPDATE_WHEN_VISIBLE); + BIND_ENUM_CONSTANT(UPDATE_WHEN_PARENT_VISIBLE); + BIND_ENUM_CONSTANT(UPDATE_ALWAYS); + + BIND_ENUM_CONSTANT(CLEAR_MODE_ALWAYS); + BIND_ENUM_CONSTANT(CLEAR_MODE_NEVER); + BIND_ENUM_CONSTANT(CLEAR_MODE_ONLY_NEXT_FRAME); +} + +SubViewport::SubViewport() { + arvr = false; + update_mode = UPDATE_WHEN_VISIBLE; + clear_mode = CLEAR_MODE_ALWAYS; +} + +SubViewport::~SubViewport() { +} diff --git a/scene/main/viewport.h b/scene/main/viewport.h index e511ce1b17..6b68bc0c94 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -35,6 +35,7 @@ #include "scene/main/node.h" #include "scene/resources/texture.h" #include "scene/resources/world_2d.h" +#include "servers/display_server.h" #include "servers/visual_server.h" class Camera; @@ -88,13 +89,6 @@ class Viewport : public Node { GDCLASS(Viewport, Node); public: - enum UpdateMode { - UPDATE_DISABLED, - UPDATE_ONCE, //then goes to disabled - UPDATE_WHEN_VISIBLE, // default - UPDATE_ALWAYS - }; - enum ShadowAtlasQuadrantSubdiv { SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED, SHADOW_ATLAS_QUADRANT_SUBDIV_1, @@ -142,13 +136,6 @@ public: DEBUG_DRAW_ROUGHNESS_LIMITER }; - enum ClearMode { - - CLEAR_MODE_ALWAYS, - CLEAR_MODE_NEVER, - CLEAR_MODE_ONLY_NEXT_FRAME - }; - enum DefaultCanvasItemTextureFilter { DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST, DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR, @@ -164,6 +151,10 @@ public: DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX, }; + enum { + SUBWINDOW_CANVAS_LAYER = 1024 + }; + private: friend class ViewportTexture; @@ -172,8 +163,6 @@ private: Listener *listener; Set<Listener *> listeners; - bool arvr; - struct CameraOverrideData { Transform transform; enum Projection { @@ -198,6 +187,7 @@ private: RID viewport; RID current_canvas; + RID subwindow_canvas; bool audio_listener; RID internal_listener; @@ -212,23 +202,17 @@ private: Transform2D global_canvas_transform; Transform2D stretch_transform; - Size2 size; - Rect2 to_screen_rect; - bool render_direct_to_screen; + Size2i size; + Size2i size_override; + bool size_allocated; RID contact_2d_debug; RID contact_3d_debug_multimesh; RID contact_3d_debug_instance; - bool size_override; - bool size_override_stretch; - Size2 size_override_size; - Size2 size_override_margin; - Rect2 last_vp_rect; bool transparent_bg; - ClearMode clear_mode; bool filter; bool gen_mipmaps; @@ -264,6 +248,7 @@ private: Ref<World> world; Ref<World> own_world; + Rect2i to_screen_rect; StringName input_group; StringName gui_input_group; StringName unhandled_input_group; @@ -276,10 +261,8 @@ private: void _propagate_exit_world(Node *p_node); void _propagate_viewport_notification(Node *p_node, int p_what); - void _update_stretch_transform(); void _update_global_transform(); - UpdateMode update_mode; RID texture_rid; DebugDraw debug_draw; @@ -291,9 +274,35 @@ private: Ref<ViewportTexture> default_texture; Set<ViewportTexture *> viewport_textures; + enum SubWindowDrag { + SUB_WINDOW_DRAG_DISABLED, + SUB_WINDOW_DRAG_MOVE, + SUB_WINDOW_DRAG_CLOSE, + SUB_WINDOW_DRAG_RESIZE, + }; + + enum SubWindowResize { + SUB_WINDOW_RESIZE_DISABLED, + SUB_WINDOW_RESIZE_TOP_LEFT, + SUB_WINDOW_RESIZE_TOP, + SUB_WINDOW_RESIZE_TOP_RIGHT, + SUB_WINDOW_RESIZE_LEFT, + SUB_WINDOW_RESIZE_RIGHT, + SUB_WINDOW_RESIZE_BOTTOM_LEFT, + SUB_WINDOW_RESIZE_BOTTOM, + SUB_WINDOW_RESIZE_BOTTOM_RIGHT, + SUB_WINDOW_RESIZE_MAX + }; + + struct SubWindow { + Window *window; + RID canvas_item; + }; + struct GUI { // info used when this is a window + bool forced_mouse_focus; //used for menu buttons bool key_event_accepted; Control *mouse_focus; Control *last_mouse_focus; @@ -301,8 +310,10 @@ private: int mouse_focus_mask; Control *key_focus; Control *mouse_over; + Control *drag_mouse_over; + Vector2 drag_mouse_over_pos; Control *tooltip; - Control *tooltip_popup; + Window *tooltip_popup; Label *tooltip_label; Point2 tooltip_pos; Point2 last_mouse_pos; @@ -312,16 +323,24 @@ private: Control *drag_preview; float tooltip_timer; float tooltip_delay; - List<Control *> modal_stack; Transform2D focus_inv_xform; - bool subwindow_order_dirty; - bool subwindow_visibility_dirty; - List<Control *> subwindows; // visible subwindows - List<Control *> all_known_subwindows; bool roots_order_dirty; List<Control *> roots; int canvas_sort_index; //for sorting items with canvas as root bool dragging; + bool embed_subwindows_hint; + bool embedding_subwindows; + + Window *subwindow_focused; + SubWindowDrag subwindow_drag; + Vector2 subwindow_drag_from; + Vector2 subwindow_drag_pos; + Rect2i subwindow_drag_close_rect; + bool subwindow_drag_close_inside; + SubWindowResize subwindow_resize_mode; + Rect2i subwindow_resize_from_rect; + + Vector<SubWindow> sub_windows; GUI(); } gui; @@ -337,10 +356,7 @@ private: void _gui_call_input(Control *p_control, const Ref<InputEvent> &p_input); void _gui_call_notification(Control *p_control, int p_what); - void _gui_prepare_subwindows(); - void _gui_sort_subwindows(); void _gui_sort_roots(); - void _gui_sort_modal_stack(); Control *_gui_find_control(const Point2 &p_global); Control *_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform, Transform2D &r_inv_xform); @@ -350,23 +366,13 @@ private: _FORCE_INLINE_ Transform2D _get_input_pre_xform() const; - void _vp_input(const Ref<InputEvent> &p_ev); - void _vp_input_text(const String &p_text); - void _vp_unhandled_input(const Ref<InputEvent> &p_ev); Ref<InputEvent> _make_input_local(const Ref<InputEvent> &ev); friend class Control; List<Control *>::Element *_gui_add_root_control(Control *p_control); - List<Control *>::Element *_gui_add_subwindow_control(Control *p_control); - - void _gui_set_subwindow_order_dirty(); - void _gui_set_root_order_dirty(); - void _gui_remove_modal_control(List<Control *>::Element *MI); - void _gui_remove_from_modal_stack(List<Control *>::Element *MI, ObjectID p_prev_focus_owner); void _gui_remove_root_control(List<Control *>::Element *RI); - void _gui_remove_subwindow_control(List<Control *>::Element *SI); String _gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_which = NULL); void _gui_cancel_tooltip(); @@ -378,9 +384,6 @@ private: void _gui_force_drag(Control *p_base, const Variant &p_data, Control *p_control); void _gui_set_drag_preview(Control *p_base, Control *p_control); - bool _gui_is_modal_on_top(const Control *p_control); - List<Control *>::Element *_gui_show_modal(Control *p_control); - void _gui_remove_focus(); void _gui_unfocus_control(Control *p_control); bool _gui_control_has_focus(const Control *p_control); @@ -391,8 +394,6 @@ private: Control *_gui_get_focus_owner(); - Vector2 _get_window_offset() const; - bool _gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check); friend class Listener; @@ -418,9 +419,26 @@ private: void _update_canvas_items(Node *p_node); + void _gui_set_root_order_dirty(); + void _own_world_changed(); + friend class Window; + + void _sub_window_update_order(); + void _sub_window_register(Window *p_window); + void _sub_window_update(Window *p_window); + void _sub_window_grab_focus(Window *p_window); + void _sub_window_remove(Window *p_window); + bool _sub_windows_forward_input(const Ref<InputEvent> &p_event); + SubWindowResize _sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point); + protected: + void _set_size(const Size2i &p_size, const Size2i &p_size_override, const Rect2i &p_to_screen_rect, const Transform2D &p_stretch_transform, bool p_allocated); + + Size2i _get_size() const; + bool _is_size_allocated() const; + void _notification(int p_what); static void _bind_methods(); virtual void _validate_property(PropertyInfo &property) const; @@ -438,19 +456,14 @@ public: void set_camera_override_perspective(float p_fovy_degrees, float p_z_near, float p_z_far); void set_camera_override_orthogonal(float p_size, float p_z_near, float p_z_far); - void set_use_arvr(bool p_use_arvr); - bool use_arvr(); - void set_as_audio_listener(bool p_enable); bool is_audio_listener() const; void set_as_audio_listener_2d(bool p_enable); bool is_audio_listener_2d() const; - void set_size(const Size2 &p_size); void update_canvas_items(); - Size2 get_size() const; Rect2 get_visible_rect() const; RID get_viewport_rid() const; @@ -479,18 +492,6 @@ public: void set_transparent_background(bool p_enable); bool has_transparent_background() const; - void set_size_override(bool p_enable, const Size2 &p_size = Size2(-1, -1), const Vector2 &p_margin = Vector2()); - Size2 get_size_override() const; - - bool is_size_override_enabled() const; - void set_size_override_stretch(bool p_enable); - bool is_size_override_stretch_enabled() const; - - void set_clear_mode(ClearMode p_mode); - ClearMode get_clear_mode() const; - - void set_update_mode(UpdateMode p_mode); - UpdateMode get_update_mode() const; Ref<ViewportTexture> get_texture() const; void set_shadow_atlas_size(int p_size); @@ -508,28 +509,20 @@ public: void set_use_own_world(bool p_world); bool is_using_own_world() const; - void input(const Ref<InputEvent> &p_event); - void unhandled_input(const Ref<InputEvent> &p_event); + void input_text(const String &p_text); + void input(const Ref<InputEvent> &p_event, bool p_local_coords = false); + void unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coords = false); void set_disable_input(bool p_disable); bool is_input_disabled() const; - void set_attach_to_screen_rect(const Rect2 &p_rect); - Rect2 get_attach_to_screen_rect() const; - - void set_use_render_direct_to_screen(bool p_render_direct_to_screen); - bool is_using_render_direct_to_screen() const; - Vector2 get_mouse_position() const; void warp_mouse(const Vector2 &p_pos); void set_physics_object_picking(bool p_enable); bool get_physics_object_picking(); - bool gui_has_modal_stack() const; - Variant gui_get_drag_data() const; - Control *get_modal_stack_top() const; void gui_reset_canvas_sort_index(); int gui_get_canvas_sort_index(); @@ -544,8 +537,6 @@ public: void set_snap_controls_to_pixels(bool p_enable); bool is_snap_controls_to_pixels_enabled() const; - void _subwindow_visibility_changed(); - void set_input_as_handled(); bool is_input_handled() const; @@ -560,15 +551,71 @@ public: void set_default_canvas_item_texture_repeat(DefaultCanvasItemTextureRepeat p_repeat); DefaultCanvasItemTextureRepeat get_default_canvas_item_texture_repeat() const; + virtual DisplayServer::WindowID get_window_id() const = 0; + + void set_embed_subwindows_hint(bool p_embed); + bool get_embed_subwindows_hint() const; + bool is_embedding_subwindows() const; + + Viewport *get_parent_viewport() const; + + void pass_mouse_focus_to(Viewport *p_viewport, Control *p_control); + Viewport(); ~Viewport(); }; -VARIANT_ENUM_CAST(Viewport::UpdateMode); +class SubViewport : public Viewport { + + GDCLASS(SubViewport, Viewport); + +public: + enum ClearMode { + + CLEAR_MODE_ALWAYS, + CLEAR_MODE_NEVER, + CLEAR_MODE_ONLY_NEXT_FRAME + }; + + enum UpdateMode { + UPDATE_DISABLED, + UPDATE_ONCE, //then goes to disabled + UPDATE_WHEN_VISIBLE, // default + UPDATE_WHEN_PARENT_VISIBLE, + UPDATE_ALWAYS + }; + +private: + UpdateMode update_mode; + ClearMode clear_mode; + bool arvr; + +protected: + static void _bind_methods(); + virtual DisplayServer::WindowID get_window_id() const; + void _notification(int p_what); + +public: + void set_size(const Size2i &p_size); + Size2i get_size() const; + + void set_use_arvr(bool p_use_arvr); + bool is_using_arvr(); + + void set_update_mode(UpdateMode p_mode); + UpdateMode get_update_mode() const; + + void set_clear_mode(ClearMode p_mode); + ClearMode get_clear_mode() const; + + SubViewport(); + ~SubViewport(); +}; +VARIANT_ENUM_CAST(SubViewport::UpdateMode); VARIANT_ENUM_CAST(Viewport::ShadowAtlasQuadrantSubdiv); VARIANT_ENUM_CAST(Viewport::MSAA); VARIANT_ENUM_CAST(Viewport::DebugDraw); -VARIANT_ENUM_CAST(Viewport::ClearMode); +VARIANT_ENUM_CAST(SubViewport::ClearMode); VARIANT_ENUM_CAST(Viewport::RenderInfo); VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureFilter); VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureRepeat); diff --git a/scene/main/window.cpp b/scene/main/window.cpp new file mode 100644 index 0000000000..a2a49aea75 --- /dev/null +++ b/scene/main/window.cpp @@ -0,0 +1,1406 @@ +/*************************************************************************/ +/* window.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "window.h" + +#include "core/debugger/engine_debugger.h" +#include "core/os/keyboard.h" +#include "scene/gui/control.h" +#include "scene/resources/dynamic_font.h" +#include "scene/scene_string_names.h" + +void Window::set_title(const String &p_title) { + title = p_title; + + if (embedder) { + embedder->_sub_window_update(this); + + } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { + + DisplayServer::get_singleton()->window_set_title(p_title, window_id); + } +} +String Window::get_title() const { + return title; +} + +void Window::set_current_screen(int p_screen) { + current_screen = p_screen; + if (window_id == DisplayServer::INVALID_WINDOW_ID) + return; + DisplayServer::get_singleton()->window_set_current_screen(p_screen, window_id); +} +int Window::get_current_screen() const { + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + current_screen = DisplayServer::get_singleton()->window_get_current_screen(window_id); + } + return current_screen; +} + +void Window::set_position(const Point2i &p_position) { + + position = p_position; + + if (embedder) { + embedder->_sub_window_update(this); + + } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { + + DisplayServer::get_singleton()->window_set_position(p_position, window_id); + } +} +Point2i Window::get_position() const { + return position; +} + +void Window::set_size(const Size2i &p_size) { + size = p_size; + _update_window_size(); +} +Size2i Window::get_size() const { + + return size; +} + +Size2i Window::get_real_size() const { + + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + return DisplayServer::get_singleton()->window_get_real_size(window_id); + } + return size; +} + +void Window::set_max_size(const Size2i &p_max_size) { + max_size = p_max_size; + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_max_size(max_size, window_id); + } + _update_window_size(); +} + +Size2i Window::get_max_size() const { + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + max_size = DisplayServer::get_singleton()->window_get_max_size(window_id); + } + return max_size; +} + +void Window::set_min_size(const Size2i &p_min_size) { + min_size = p_min_size; + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_min_size(min_size, window_id); + } + _update_window_size(); +} + +Size2i Window::get_min_size() const { + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + min_size = DisplayServer::get_singleton()->window_get_min_size(window_id); + } + return min_size; +} + +void Window::set_mode(Mode p_mode) { + + mode = p_mode; + + if (embedder) { + embedder->_sub_window_update(this); + + } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { + + DisplayServer::get_singleton()->window_set_mode(DisplayServer::WindowMode(p_mode), window_id); + } +} + +Window::Mode Window::get_mode() const { + + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + mode = (Mode)DisplayServer::get_singleton()->window_get_mode(window_id); + } + return mode; +} + +void Window::set_flag(Flags p_flag, bool p_enabled) { + ERR_FAIL_INDEX(p_flag, FLAG_MAX); + flags[p_flag] = p_enabled; + + if (embedder) { + embedder->_sub_window_update(this); + + } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { + + DisplayServer::get_singleton()->window_set_flag(DisplayServer::WindowFlags(p_flag), p_enabled, window_id); + } +} + +bool Window::get_flag(Flags p_flag) const { + ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + flags[p_flag] = DisplayServer::get_singleton()->window_get_flag(DisplayServer::WindowFlags(p_flag), window_id); + } + return flags[p_flag]; +} + +bool Window::is_maximize_allowed() const { + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + return DisplayServer::get_singleton()->window_is_maximize_allowed(window_id); + } + return true; +} + +void Window::request_attention() { + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_request_attention(window_id); + } +} +void Window::move_to_foreground() { + + if (embedder) { + embedder->_sub_window_grab_focus(this); + + } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_move_to_foreground(window_id); + } +} + +bool Window::can_draw() const { + if (!is_inside_tree()) { + return false; + } + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + return DisplayServer::get_singleton()->window_can_draw(window_id); + } + + return visible; +} + +void Window::set_ime_active(bool p_active) { + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_ime_active(p_active, window_id); + } +} +void Window::set_ime_position(const Point2i &p_pos) { + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_ime_position(p_pos, window_id); + } +} + +bool Window::is_embedded() const { + ERR_FAIL_COND_V(!is_inside_tree(), false); + + return _get_embedder() != nullptr; +} + +void Window::_make_window() { + ERR_FAIL_COND(window_id != DisplayServer::INVALID_WINDOW_ID); + + uint32_t f = 0; + for (int i = 0; i < FLAG_MAX; i++) { + if (flags[i]) { + f |= (1 << i); + } + } + + window_id = DisplayServer::get_singleton()->create_sub_window(DisplayServer::WindowMode(mode), f, Rect2i(position, size)); + ERR_FAIL_COND(window_id == DisplayServer::INVALID_WINDOW_ID); + DisplayServer::get_singleton()->window_set_current_screen(current_screen, window_id); + DisplayServer::get_singleton()->window_set_max_size(max_size, window_id); + DisplayServer::get_singleton()->window_set_min_size(min_size, window_id); + DisplayServer::get_singleton()->window_set_title(title, window_id); + DisplayServer::get_singleton()->window_attach_instance_id(get_instance_id(), window_id); + + _update_window_size(); + + if (transient_parent && transient_parent->window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_transient(window_id, transient_parent->window_id); + } + + for (Set<Window *>::Element *E = transient_children.front(); E; E = E->next()) { + if (E->get()->window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_transient(E->get()->window_id, transient_parent->window_id); + } + } + + VS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), VS::VIEWPORT_UPDATE_WHEN_VISIBLE); +} +void Window::_update_from_window() { + + ERR_FAIL_COND(window_id == DisplayServer::INVALID_WINDOW_ID); + mode = (Mode)DisplayServer::get_singleton()->window_get_mode(window_id); + for (int i = 0; i < FLAG_MAX; i++) { + flags[i] = DisplayServer::get_singleton()->window_get_flag(DisplayServer::WindowFlags(i), window_id); + } +} + +void Window::_clear_window() { + ERR_FAIL_COND(window_id == DisplayServer::INVALID_WINDOW_ID); + + if (transient_parent && transient_parent->window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_transient(window_id, DisplayServer::INVALID_WINDOW_ID); + } + + for (Set<Window *>::Element *E = transient_children.front(); E; E = E->next()) { + if (E->get()->window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_transient(E->get()->window_id, DisplayServer::INVALID_WINDOW_ID); + } + } + + _update_from_window(); + + DisplayServer::get_singleton()->delete_sub_window(window_id); + window_id = DisplayServer::INVALID_WINDOW_ID; + + _update_viewport_size(); + VS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), VS::VIEWPORT_UPDATE_DISABLED); +} + +void Window::_rect_changed_callback(const Rect2i &p_callback) { + + //we must always accept this as the truth + if (size == p_callback.size && position == p_callback.position) { + return; + } + position = p_callback.position; + + if (size != p_callback.size) { + size = p_callback.size; + _update_viewport_size(); + } +} + +void Window::_propagate_window_notification(Node *p_node, int p_notification) { + p_node->notification(p_notification); + for (int i = 0; i < p_node->get_child_count(); i++) { + Node *child = p_node->get_child(i); + Window *window = Object::cast_to<Window>(child); + if (window) { + break; + } + _propagate_window_notification(child, p_notification); + } +} + +void Window::_event_callback(DisplayServer::WindowEvent p_event) { + + switch (p_event) { + case DisplayServer::WINDOW_EVENT_MOUSE_ENTER: { + _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER); + emit_signal("mouse_entered"); + } break; + case DisplayServer::WINDOW_EVENT_MOUSE_EXIT: { + _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_EXIT); + emit_signal("mouse_exited"); + } break; + case DisplayServer::WINDOW_EVENT_FOCUS_IN: { + focused = true; + _propagate_window_notification(this, NOTIFICATION_WM_FOCUS_IN); + emit_signal("focus_entered"); + + } break; + case DisplayServer::WINDOW_EVENT_FOCUS_OUT: { + focused = false; + _propagate_window_notification(this, NOTIFICATION_WM_FOCUS_OUT); + emit_signal("focus_exited"); + } break; + case DisplayServer::WINDOW_EVENT_CLOSE_REQUEST: { + if (exclusive_child != nullptr) { + break; //has an exclusive child, can't get events until child is closed + } + _propagate_window_notification(this, NOTIFICATION_WM_CLOSE_REQUEST); + emit_signal("close_requested"); + } break; + case DisplayServer::WINDOW_EVENT_GO_BACK_REQUEST: { + _propagate_window_notification(this, NOTIFICATION_WM_GO_BACK_REQUEST); + emit_signal("go_back_requested"); + } break; + case DisplayServer::WINDOW_EVENT_DPI_CHANGE: { + _propagate_window_notification(this, NOTIFICATION_WM_DPI_CHANGE); + emit_signal("dpi_changed"); + } break; + } +} + +void Window::show() { + set_visible(true); +} +void Window::hide() { + set_visible(false); +} + +void Window::set_visible(bool p_visible) { + + if (visible == p_visible) { + return; + } + + visible = p_visible; + + if (!is_inside_tree()) { + return; + } + + if (updating_child_controls) { + _update_child_controls(); + } + + ERR_FAIL_COND_MSG(get_parent() == nullptr, "Can't change visibility of main window."); + + Viewport *embedder_vp = _get_embedder(); + + if (!embedder_vp) { + if (!p_visible && window_id != DisplayServer::INVALID_WINDOW_ID) { + _clear_window(); + } + if (p_visible && window_id == DisplayServer::INVALID_WINDOW_ID) { + _make_window(); + _update_window_callbacks(); + } + } else { + if (visible) { + embedder = embedder_vp; + embedder->_sub_window_register(this); + VS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), VS::VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE); + } else { + embedder->_sub_window_remove(this); + embedder = nullptr; + VS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), VS::VIEWPORT_UPDATE_DISABLED); + } + _update_window_size(); + } + + if (!visible) { + focused = false; + } + notification(NOTIFICATION_VISIBILITY_CHANGED); + emit_signal(SceneStringNames::get_singleton()->visibility_changed); + + VS::get_singleton()->viewport_set_active(get_viewport_rid(), visible); +} + +void Window::_clear_transient() { + if (transient_parent) { + if (transient_parent->window_id != DisplayServer::INVALID_WINDOW_ID && window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_transient(window_id, DisplayServer::INVALID_WINDOW_ID); + } + transient_parent->transient_children.erase(this); + if (transient_parent->exclusive_child == this) { + transient_parent->exclusive_child = nullptr; + } + transient_parent = nullptr; + } +} + +void Window::_make_transient() { + if (!get_parent()) { + //main window, can't be transient + return; + } + //find transient parent + Viewport *vp = get_parent()->get_viewport(); + Window *window = nullptr; + while (vp) { + window = Object::cast_to<Window>(vp); + if (window) { + break; + } + if (!vp->get_parent()) { + break; + } + + vp = vp->get_parent()->get_viewport(); + } + + if (window) { + transient_parent = window; + window->transient_children.insert(this); + if (is_inside_tree() && is_visible() && exclusive) { + if (transient_parent->exclusive_child == nullptr) { + transient_parent->exclusive_child = this; + } else if (transient_parent->exclusive_child != this) { + ERR_PRINT("Making child transient exclusive, but parent has another exclusive child"); + } + } + } + + //see if we can make transient + if (transient_parent->window_id != DisplayServer::INVALID_WINDOW_ID && window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_transient(window_id, transient_parent->window_id); + } +} + +void Window::set_transient(bool p_transient) { + if (transient == p_transient) { + return; + } + + transient = p_transient; + + if (!is_inside_tree()) { + return; + } + + if (transient) { + _make_transient(); + } else { + _clear_transient(); + } +} +bool Window::is_transient() const { + return transient; +} + +void Window::set_exclusive(bool p_exclusive) { + + if (exclusive == p_exclusive) { + return; + } + + exclusive = p_exclusive; + + if (transient_parent) { + if (p_exclusive && is_inside_tree() && is_visible()) { + ERR_FAIL_COND_MSG(transient_parent->exclusive_child && transient_parent->exclusive_child != this, "Transient parent has another exclusive child."); + transient_parent->exclusive_child = this; + } else { + if (transient_parent->exclusive_child == this) { + transient_parent->exclusive_child = nullptr; + } + } + } +} + +bool Window::is_exclusive() const { + return exclusive; +} + +bool Window::is_visible() const { + return visible; +} + +void Window::_update_window_size() { + + Size2i size_limit; + if (wrap_controls) { + size_limit = get_contents_minimum_size(); + } + + size_limit.x = MAX(size_limit.x, min_size.x); + size_limit.y = MAX(size_limit.y, min_size.y); + + size.x = MAX(size_limit.x, size.x); + size.y = MAX(size_limit.y, size.y); + + if (max_size.x > 0 && max_size.x > min_size.x && max_size.x > size.x) { + size.x = max_size.x; + } + + if (max_size.y > 0 && max_size.y > min_size.y && max_size.y > size.y) { + size.y = max_size.y; + } + + if (embedder) { + embedder->_sub_window_update(this); + } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_size(size, window_id); + } + + //update the viewport + _update_viewport_size(); +} +void Window::_update_viewport_size() { + //update the viewport part + + Size2i final_size; + Size2i final_size_override; + Rect2i attach_to_screen_rect(Point2i(), size); + Transform2D stretch_transform; + float font_oversampling = 1.0; + + if (content_scale_mode == CONTENT_SCALE_MODE_DISABLED || content_scale_size.x == 0 || content_scale_size.y == 0) { + + stretch_transform = Transform2D(); + final_size = size; + + } else { + + //actual screen video mode + Size2 video_mode = size; + Size2 desired_res = content_scale_size; + + Size2 viewport_size; + Size2 screen_size; + + float viewport_aspect = desired_res.aspect(); + float video_mode_aspect = video_mode.aspect(); + + if (content_scale_aspect == CONTENT_SCALE_ASPECT_IGNORE || Math::is_equal_approx(viewport_aspect, video_mode_aspect)) { + //same aspect or ignore aspect + viewport_size = desired_res; + screen_size = video_mode; + } else if (viewport_aspect < video_mode_aspect) { + // screen ratio is smaller vertically + + if (content_scale_aspect == CONTENT_SCALE_ASPECT_KEEP_HEIGHT || content_scale_aspect == CONTENT_SCALE_ASPECT_EXPAND) { + + //will stretch horizontally + viewport_size.x = desired_res.y * video_mode_aspect; + viewport_size.y = desired_res.y; + screen_size = video_mode; + + } else { + //will need black bars + viewport_size = desired_res; + screen_size.x = video_mode.y * viewport_aspect; + screen_size.y = video_mode.y; + } + } else { + //screen ratio is smaller horizontally + if (content_scale_aspect == CONTENT_SCALE_ASPECT_KEEP_WIDTH || content_scale_aspect == CONTENT_SCALE_ASPECT_EXPAND) { + + //will stretch horizontally + viewport_size.x = desired_res.x; + viewport_size.y = desired_res.x / video_mode_aspect; + screen_size = video_mode; + + } else { + //will need black bars + viewport_size = desired_res; + screen_size.x = video_mode.x; + screen_size.y = video_mode.x / viewport_aspect; + } + } + + screen_size = screen_size.floor(); + viewport_size = viewport_size.floor(); + + Size2 margin; + Size2 offset; + //black bars and margin + if (content_scale_aspect != CONTENT_SCALE_ASPECT_EXPAND && screen_size.x < video_mode.x) { + margin.x = Math::round((video_mode.x - screen_size.x) / 2.0); + //VisualServer::get_singleton()->black_bars_set_margins(margin.x, 0, margin.x, 0); + offset.x = Math::round(margin.x * viewport_size.y / screen_size.y); + } else if (content_scale_aspect != CONTENT_SCALE_ASPECT_EXPAND && screen_size.y < video_mode.y) { + margin.y = Math::round((video_mode.y - screen_size.y) / 2.0); + //VisualServer::get_singleton()->black_bars_set_margins(0, margin.y, 0, margin.y); + offset.y = Math::round(margin.y * viewport_size.x / screen_size.x); + } else { + //VisualServer::get_singleton()->black_bars_set_margins(0, 0, 0, 0); + } + + switch (content_scale_mode) { + case CONTENT_SCALE_MODE_DISABLED: { + // Already handled above + //_update_font_oversampling(1.0); + } break; + case CONTENT_SCALE_MODE_OBJECTS: { + + final_size = screen_size; + final_size_override = viewport_size; + attach_to_screen_rect = Rect2(margin, screen_size); + font_oversampling = screen_size.x / viewport_size.x; + } break; + case CONTENT_SCALE_MODE_PIXELS: { + + final_size = viewport_size; + attach_to_screen_rect = Rect2(margin, screen_size); + + } break; + } + + Size2 scale = size / (Vector2(final_size) + margin * 2); + stretch_transform.scale(scale); + stretch_transform.elements[2] = margin * scale; + } + + bool allocate = is_inside_tree() && visible && (window_id != DisplayServer::INVALID_WINDOW_ID || embedder != nullptr); + + _set_size(final_size, final_size_override, attach_to_screen_rect, stretch_transform, allocate); + + if (window_id != DisplayServer::INVALID_WINDOW_ID) { + VisualServer::get_singleton()->viewport_attach_to_screen(get_viewport_rid(), attach_to_screen_rect, window_id); + } else { + VisualServer::get_singleton()->viewport_attach_to_screen(get_viewport_rid(), Rect2i(), DisplayServer::INVALID_WINDOW_ID); + } + + if (window_id == DisplayServer::MAIN_WINDOW_ID) { + + if (!use_font_oversampling) { + font_oversampling = 1.0; + } + if (DynamicFontAtSize::font_oversampling != font_oversampling) { + + DynamicFontAtSize::font_oversampling = font_oversampling; + DynamicFont::update_oversampling(); + } + } + + notification(NOTIFICATION_WM_SIZE_CHANGED); + + if (embedder) { + embedder->_sub_window_update(this); + } +} + +void Window::_update_window_callbacks() { + DisplayServer::get_singleton()->window_set_rect_changed_callback(callable_mp(this, &Window::_rect_changed_callback), window_id); + DisplayServer::get_singleton()->window_set_window_event_callback(callable_mp(this, &Window::_event_callback), window_id); + DisplayServer::get_singleton()->window_set_input_event_callback(callable_mp(this, &Window::_window_input), window_id); + DisplayServer::get_singleton()->window_set_input_text_callback(callable_mp(this, &Window::_window_input_text), window_id); + DisplayServer::get_singleton()->window_set_drop_files_callback(callable_mp(this, &Window::_window_drop_files), window_id); +} + +Viewport *Window::_get_embedder() const { + + Viewport *vp = get_parent_viewport(); + + while (vp) { + + if (vp->is_embedding_subwindows()) { + return vp; + } + + if (vp->get_parent()) { + vp = vp->get_parent()->get_viewport(); + } else { + vp = nullptr; + } + } + return nullptr; +} + +void Window::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + + bool embedded = false; + { + + embedder = _get_embedder(); + + if (embedder) { + embedded = true; + + if (!visible) { + embedder = nullptr; //not yet since not visible + } + } + } + + if (embedded) { + //create as embedded + if (embedder) { + embedder->_sub_window_register(this); + VS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), VS::VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE); + _update_window_size(); + } + + } else { + if (get_parent() == nullptr) { + //it's the root window! + visible = true; //always visible + window_id = DisplayServer::MAIN_WINDOW_ID; + DisplayServer::get_singleton()->window_attach_instance_id(get_instance_id(), window_id); + _update_from_window(); + //since this window already exists (created on start), we must update pos and size from it + { + position = DisplayServer::get_singleton()->window_get_position(window_id); + size = DisplayServer::get_singleton()->window_get_size(window_id); + } + _update_viewport_size(); //then feed back to the viewport + _update_window_callbacks(); + VS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), VS::VIEWPORT_UPDATE_WHEN_VISIBLE); + } else { + //create + if (visible) { + _make_window(); + _update_window_callbacks(); + } + } + } + + if (transient) { + _make_transient(); + } + if (visible) { + notification(NOTIFICATION_VISIBILITY_CHANGED); + emit_signal(SceneStringNames::get_singleton()->visibility_changed); + VS::get_singleton()->viewport_set_active(get_viewport_rid(), true); + } + } + + if (p_what == NOTIFICATION_READY) { + + if (wrap_controls) { + _update_child_controls(); + } + } + + if (p_what == NOTIFICATION_EXIT_TREE) { + + if (transient) { + _clear_transient(); + } + + if (!is_embedded() && window_id != DisplayServer::INVALID_WINDOW_ID) { + + if (window_id == DisplayServer::MAIN_WINDOW_ID) { + + VS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), VS::VIEWPORT_UPDATE_DISABLED); + _update_window_callbacks(); + } else { + _clear_window(); + } + } else { + + if (embedder) { + embedder->_sub_window_remove(this); + embedder = nullptr; + VS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), VS::VIEWPORT_UPDATE_DISABLED); + } + _update_viewport_size(); //called by clear and make, which does not happen here + } + + VS::get_singleton()->viewport_set_active(get_viewport_rid(), false); + } +} + +void Window::set_content_scale_size(const Size2i &p_size) { + ERR_FAIL_COND(p_size.x < 0); + ERR_FAIL_COND(p_size.y < 0); + content_scale_size = p_size; + _update_viewport_size(); +} + +Size2i Window::get_content_scale_size() const { + return content_scale_size; +} + +void Window::set_content_scale_mode(ContentScaleMode p_mode) { + content_scale_mode = p_mode; + _update_viewport_size(); +} +Window::ContentScaleMode Window::get_content_scale_mode() const { + return content_scale_mode; +} + +void Window::set_content_scale_aspect(ContentScaleAspect p_aspect) { + content_scale_aspect = p_aspect; + _update_viewport_size(); +} +Window::ContentScaleAspect Window::get_content_scale_aspect() const { + return content_scale_aspect; +} + +void Window::set_use_font_oversampling(bool p_oversampling) { + if (is_inside_tree() && window_id != DisplayServer::MAIN_WINDOW_ID) { + ERR_FAIL_MSG("Only the root window can set and use font oversampling."); + } + use_font_oversampling = p_oversampling; + _update_viewport_size(); +} +bool Window::is_using_font_oversampling() const { + return use_font_oversampling; +} + +DisplayServer::WindowID Window::get_window_id() const { + return window_id; +} + +void Window::set_wrap_controls(bool p_enable) { + wrap_controls = p_enable; + if (wrap_controls) { + child_controls_changed(); + } +} + +bool Window::is_wrapping_controls() const { + return wrap_controls; +} + +Size2 Window::_get_contents_minimum_size() const { + Size2 max; + + for (int i = 0; i < get_child_count(); i++) { + Control *c = Object::cast_to<Control>(get_child(i)); + if (c) { + Point2i pos = c->get_position(); + Size2i min = c->get_combined_minimum_size(); + + max.x = MAX(pos.x + min.x, max.x); + max.y = MAX(pos.y + min.y, max.y); + } + } + + return max; +} +void Window::_update_child_controls() { + + if (!updating_child_controls) { + return; + } + + _update_window_size(); + + updating_child_controls = false; +} +void Window::child_controls_changed() { + + if (!is_inside_tree() || !visible || updating_child_controls) { + return; + } + + updating_child_controls = true; + call_deferred("_update_child_controls"); +} + +void Window::_window_input(const Ref<InputEvent> &p_ev) { + if (Engine::get_singleton()->is_editor_hint() && (Object::cast_to<InputEventJoypadButton>(p_ev.ptr()) || Object::cast_to<InputEventJoypadMotion>(*p_ev))) + return; //avoid joy input on editor + + if (EngineDebugger::is_active()) { + //quit from game window using F8 + Ref<InputEventKey> k = p_ev; + if (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == KEY_F8) { + EngineDebugger::get_singleton()->send_message("request_quit", Array()); + } + } + + if (exclusive_child != nullptr) { + exclusive_child->grab_focus(); + + return; //has an exclusive child, can't get events until child is closed + } + + emit_signal(SceneStringNames::get_singleton()->window_input, p_ev); + input(p_ev); + if (!is_input_handled()) { + unhandled_input(p_ev); + } +} +void Window::_window_input_text(const String &p_text) { + input_text(p_text); +} +void Window::_window_drop_files(const Vector<String> &p_files) { + emit_signal("files_dropped", p_files); +} + +Viewport *Window::get_parent_viewport() const { + + if (get_parent()) { + return get_parent()->get_viewport(); + } else { + return nullptr; + } +} + +Window *Window::get_parent_visible_window() const { + + Viewport *vp = get_parent_viewport(); + Window *window = nullptr; + while (vp) { + window = Object::cast_to<Window>(vp); + if (window && window->visible) { + break; + } + if (!vp->get_parent()) { + break; + } + + vp = vp->get_parent()->get_viewport(); + } + return window; +} + +void Window::popup_on_parent(const Rect2i &p_parent_rect) { + + ERR_FAIL_COND(!is_inside_tree()); + ERR_FAIL_COND_MSG(window_id == DisplayServer::MAIN_WINDOW_ID, "Can't popup the main window."); + + if (!is_embedded()) { + Window *window = get_parent_visible_window(); + + if (!window) { + popup(p_parent_rect); + } else { + popup(Rect2i(window->get_position() + p_parent_rect.position, p_parent_rect.size)); + } + } else { + popup(p_parent_rect); + } +} + +void Window::popup_centered_clamped(const Size2i &p_size, float p_fallback_ratio) { + + ERR_FAIL_COND(!is_inside_tree()); + ERR_FAIL_COND_MSG(window_id == DisplayServer::MAIN_WINDOW_ID, "Can't popup the main window."); + + Rect2 parent_rect; + + if (is_embedded()) { + parent_rect = get_parent_viewport()->get_visible_rect(); + } else { + DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id(); + int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id); + parent_rect.position = DisplayServer::get_singleton()->screen_get_position(parent_screen); + parent_rect.size = DisplayServer::get_singleton()->screen_get_size(parent_screen); + } + + Vector2i size_ratio = parent_rect.size * p_fallback_ratio; + + Rect2i popup_rect; + popup_rect.size = Vector2i(MIN(size_ratio.x, p_size.x), MIN(size_ratio.y, p_size.y)); + popup_rect.position = (parent_rect.size - popup_rect.size) / 2; + + popup(popup_rect); +} + +void Window::popup_centered(const Size2i &p_minsize) { + ERR_FAIL_COND(!is_inside_tree()); + ERR_FAIL_COND_MSG(window_id == DisplayServer::MAIN_WINDOW_ID, "Can't popup the main window."); + + Rect2 parent_rect; + + if (is_embedded()) { + parent_rect = get_parent_viewport()->get_visible_rect(); + } else { + DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id(); + int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id); + parent_rect.position = DisplayServer::get_singleton()->screen_get_position(parent_screen); + parent_rect.size = DisplayServer::get_singleton()->screen_get_size(parent_screen); + } + + Rect2i popup_rect; + if (p_minsize == Size2i()) { + popup_rect.size = _get_contents_minimum_size(); + } else { + popup_rect.size = p_minsize; + } + popup_rect.position = (parent_rect.size - popup_rect.size) / 2; + + popup(popup_rect); +} + +void Window::popup_centered_ratio(float p_ratio) { + + ERR_FAIL_COND(!is_inside_tree()); + ERR_FAIL_COND_MSG(window_id == DisplayServer::MAIN_WINDOW_ID, "Can't popup the main window."); + + Rect2i parent_rect; + + if (is_embedded()) { + parent_rect = get_parent_viewport()->get_visible_rect(); + } else { + DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id(); + int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id); + parent_rect.position = DisplayServer::get_singleton()->screen_get_position(parent_screen); + parent_rect.size = DisplayServer::get_singleton()->screen_get_size(parent_screen); + } + + Rect2i popup_rect; + popup_rect.size = parent_rect.size * p_ratio; + popup_rect.position = (parent_rect.size - popup_rect.size) / 2; + + popup(popup_rect); +} + +void Window::popup(const Rect2i &p_screen_rect) { + + emit_signal("about_to_popup"); + + if (p_screen_rect != Rect2i()) { + set_position(p_screen_rect.position); + set_size(p_screen_rect.size); + } + + Rect2i adjust = _popup_adjust_rect(); + if (adjust != Rect2i()) { + set_position(adjust.position); + set_size(adjust.size); + } + + set_transient(true); + set_visible(true); + _post_popup(); + notification(NOTIFICATION_POST_POPUP); +} + +Size2 Window::get_contents_minimum_size() const { + return _get_contents_minimum_size(); +} + +void Window::grab_focus() { + if (embedder) { + embedder->_sub_window_grab_focus(this); + } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_move_to_foreground(window_id); + } +} + +bool Window::has_focus() const { + return focused; +} + +Rect2i Window::get_usable_parent_rect() const { + ERR_FAIL_COND_V(!is_inside_tree(), Rect2()); + Rect2i parent; + if (is_embedded()) { + parent = _get_embedder()->get_visible_rect(); + } else { + + const Window *w = is_visible() ? this : get_parent_visible_window(); + //find a parent that can contain us + ERR_FAIL_COND_V(!w, Rect2()); + + parent = DisplayServer::get_singleton()->screen_get_usable_rect(DisplayServer::get_singleton()->window_get_current_screen(w->get_window_id())); + } + return parent; +} + +void Window::add_child_notify(Node *p_child) { + + Control *child_c = Object::cast_to<Control>(p_child); + + if (child_c && child_c->data.theme.is_null() && (theme_owner || theme_owner_window)) { + Control::_propagate_theme_changed(child_c, theme_owner, theme_owner_window); //need to propagate here, since many controls may require setting up stuff + } + + Window *child_w = Object::cast_to<Window>(p_child); + + if (child_w && child_w->theme.is_null() && (theme_owner || theme_owner_window)) { + Control::_propagate_theme_changed(child_w, theme_owner, theme_owner_window); //need to propagate here, since many controls may require setting up stuff + } + + if (is_inside_tree() && wrap_controls) { + child_controls_changed(); + } +} + +void Window::remove_child_notify(Node *p_child) { + + Control *child_c = Object::cast_to<Control>(p_child); + + if (child_c && (child_c->data.theme_owner || child_c->data.theme_owner_window) && child_c->data.theme.is_null()) { + Control::_propagate_theme_changed(child_c, NULL, NULL); + } + + Window *child_w = Object::cast_to<Window>(p_child); + + if (child_w && (child_w->theme_owner || child_w->theme_owner_window) && child_w->theme.is_null()) { + Control::_propagate_theme_changed(child_w, NULL, NULL); + } + + if (is_inside_tree() && wrap_controls) { + child_controls_changed(); + } +} + +void Window::set_theme(const Ref<Theme> &p_theme) { + + if (theme == p_theme) + return; + + theme = p_theme; + + if (!p_theme.is_null()) { + + theme_owner = nullptr; + theme_owner_window = this; + Control::_propagate_theme_changed(this, nullptr, this); + } else { + + Control *parent_c = cast_to<Control>(get_parent()); + if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) { + Control::_propagate_theme_changed(this, parent_c->data.theme_owner, parent_c->data.theme_owner_window); + } else { + Window *parent_w = cast_to<Window>(get_parent()); + if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) { + Control::_propagate_theme_changed(this, parent_w->theme_owner, parent_w->theme_owner_window); + } else { + Control::_propagate_theme_changed(this, nullptr, nullptr); + } + } + } +} +Ref<Theme> Window::get_theme() const { + return theme; +} + +Ref<Texture2D> Window::get_theme_icon(const StringName &p_name, const StringName &p_type) const { + StringName type = p_type ? p_type : get_class_name(); + return Control::get_icons(theme_owner, theme_owner_window, p_name, type); +} +Ref<Shader> Window::get_theme_shader(const StringName &p_name, const StringName &p_type) const { + StringName type = p_type ? p_type : get_class_name(); + return Control::get_shaders(theme_owner, theme_owner_window, p_name, type); +} +Ref<StyleBox> Window::get_theme_stylebox(const StringName &p_name, const StringName &p_type) const { + StringName type = p_type ? p_type : get_class_name(); + return Control::get_styleboxs(theme_owner, theme_owner_window, p_name, type); +} +Ref<Font> Window::get_theme_font(const StringName &p_name, const StringName &p_type) const { + StringName type = p_type ? p_type : get_class_name(); + return Control::get_fonts(theme_owner, theme_owner_window, p_name, type); +} +Color Window::get_theme_color(const StringName &p_name, const StringName &p_type) const { + StringName type = p_type ? p_type : get_class_name(); + return Control::get_colors(theme_owner, theme_owner_window, p_name, type); +} +int Window::get_theme_constant(const StringName &p_name, const StringName &p_type) const { + StringName type = p_type ? p_type : get_class_name(); + return Control::get_constants(theme_owner, theme_owner_window, p_name, type); +} + +bool Window::has_theme_icon(const StringName &p_name, const StringName &p_type) const { + StringName type = p_type ? p_type : get_class_name(); + return Control::has_icons(theme_owner, theme_owner_window, p_name, type); +} +bool Window::has_theme_shader(const StringName &p_name, const StringName &p_type) const { + StringName type = p_type ? p_type : get_class_name(); + return Control::has_shaders(theme_owner, theme_owner_window, p_name, type); +} +bool Window::has_theme_stylebox(const StringName &p_name, const StringName &p_type) const { + StringName type = p_type ? p_type : get_class_name(); + return Control::has_styleboxs(theme_owner, theme_owner_window, p_name, type); +} +bool Window::has_theme_font(const StringName &p_name, const StringName &p_type) const { + StringName type = p_type ? p_type : get_class_name(); + return Control::has_fonts(theme_owner, theme_owner_window, p_name, type); +} +bool Window::has_theme_color(const StringName &p_name, const StringName &p_type) const { + StringName type = p_type ? p_type : get_class_name(); + return Control::has_colors(theme_owner, theme_owner_window, p_name, type); +} +bool Window::has_theme_constant(const StringName &p_name, const StringName &p_type) const { + StringName type = p_type ? p_type : get_class_name(); + return Control::has_constants(theme_owner, theme_owner_window, p_name, type); +} + +Rect2i Window::get_parent_rect() const { + ERR_FAIL_COND_V(!is_inside_tree(), Rect2i()); + if (is_embedded()) { + //viewport + Node *n = get_parent(); + ERR_FAIL_COND_V(!n, Rect2i()); + Viewport *p = n->get_viewport(); + ERR_FAIL_COND_V(!p, Rect2i()); + + return p->get_visible_rect(); + } else { + int x = get_position().x; + int closest_dist = 0x7FFFFFFF; + Rect2i closest_rect; + for (int i = 0; i < DisplayServer::get_singleton()->get_screen_count(); i++) { + Rect2i s(DisplayServer::get_singleton()->screen_get_position(i), DisplayServer::get_singleton()->screen_get_size(i)); + int d; + if (x >= s.position.x && x < s.size.x) { + //contained + closest_rect = s; + break; + } else if (x < s.position.x) { + d = s.position.x - x; + } else { + d = x - (s.position.x + s.size.x); + } + + if (d < closest_dist) { + closest_dist = d; + closest_rect = s; + } + } + return closest_rect; + } +} + +void Window::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title); + ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title); + + ClassDB::bind_method(D_METHOD("set_current_screen", "index"), &Window::set_current_screen); + ClassDB::bind_method(D_METHOD("get_current_screen"), &Window::get_current_screen); + + ClassDB::bind_method(D_METHOD("set_position", "position"), &Window::set_position); + ClassDB::bind_method(D_METHOD("get_position"), &Window::get_position); + + ClassDB::bind_method(D_METHOD("set_size", "size"), &Window::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &Window::get_size); + + ClassDB::bind_method(D_METHOD("get_real_size"), &Window::get_real_size); + + ClassDB::bind_method(D_METHOD("set_max_size", "max_size"), &Window::set_max_size); + ClassDB::bind_method(D_METHOD("get_max_size"), &Window::get_max_size); + + ClassDB::bind_method(D_METHOD("set_min_size", "min_size"), &Window::set_min_size); + ClassDB::bind_method(D_METHOD("get_min_size"), &Window::get_min_size); + + ClassDB::bind_method(D_METHOD("set_mode", "mode"), &Window::set_mode); + ClassDB::bind_method(D_METHOD("get_mode"), &Window::get_mode); + + ClassDB::bind_method(D_METHOD("set_flag", "flag", "enabled"), &Window::set_flag); + ClassDB::bind_method(D_METHOD("get_flag", "flag"), &Window::get_flag); + + ClassDB::bind_method(D_METHOD("is_maximize_allowed"), &Window::is_maximize_allowed); + + ClassDB::bind_method(D_METHOD("request_attention"), &Window::request_attention); + + ClassDB::bind_method(D_METHOD("move_to_foreground"), &Window::move_to_foreground); + + ClassDB::bind_method(D_METHOD("set_visible", "visible"), &Window::set_visible); + ClassDB::bind_method(D_METHOD("is_visible"), &Window::is_visible); + + ClassDB::bind_method(D_METHOD("hide"), &Window::hide); + ClassDB::bind_method(D_METHOD("show"), &Window::show); + + ClassDB::bind_method(D_METHOD("set_transient", "transient"), &Window::set_transient); + ClassDB::bind_method(D_METHOD("is_transient"), &Window::is_transient); + + ClassDB::bind_method(D_METHOD("set_exclusive", "exclusive"), &Window::set_exclusive); + ClassDB::bind_method(D_METHOD("is_exclusive"), &Window::is_exclusive); + + ClassDB::bind_method(D_METHOD("can_draw"), &Window::is_transient); + ClassDB::bind_method(D_METHOD("has_focus"), &Window::has_focus); + ClassDB::bind_method(D_METHOD("grab_focus"), &Window::grab_focus); + + ClassDB::bind_method(D_METHOD("set_ime_active"), &Window::set_ime_active); + ClassDB::bind_method(D_METHOD("set_ime_position"), &Window::set_ime_position); + + ClassDB::bind_method(D_METHOD("is_embedded"), &Window::is_embedded); + + ClassDB::bind_method(D_METHOD("set_content_scale_size", "size"), &Window::set_content_scale_size); + ClassDB::bind_method(D_METHOD("get_content_scale_size"), &Window::get_content_scale_size); + + ClassDB::bind_method(D_METHOD("set_content_scale_mode", "mode"), &Window::set_content_scale_mode); + ClassDB::bind_method(D_METHOD("get_content_scale_mode"), &Window::get_content_scale_mode); + + ClassDB::bind_method(D_METHOD("set_content_scale_aspect", "aspect"), &Window::set_content_scale_aspect); + ClassDB::bind_method(D_METHOD("get_content_scale_aspect"), &Window::get_content_scale_aspect); + + ClassDB::bind_method(D_METHOD("set_use_font_oversampling", "enable"), &Window::set_use_font_oversampling); + ClassDB::bind_method(D_METHOD("is_using_font_oversampling"), &Window::is_using_font_oversampling); + + ClassDB::bind_method(D_METHOD("set_wrap_controls", "enable"), &Window::set_wrap_controls); + ClassDB::bind_method(D_METHOD("is_wrapping_controls"), &Window::is_wrapping_controls); + ClassDB::bind_method(D_METHOD("child_controls_changed"), &Window::child_controls_changed); + + ClassDB::bind_method(D_METHOD("_update_child_controls"), &Window::_update_child_controls); + + ClassDB::bind_method(D_METHOD("set_theme", "theme"), &Window::set_theme); + ClassDB::bind_method(D_METHOD("get_theme"), &Window::get_theme); + + ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "type"), &Window::get_theme_icon, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "type"), &Window::get_theme_stylebox, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_font", "name", "type"), &Window::get_theme_font, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_color", "name", "type"), &Window::get_theme_color, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "type"), &Window::get_theme_constant, DEFVAL("")); + + ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "type"), &Window::has_theme_icon, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "type"), &Window::has_theme_stylebox, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_font", "name", "type"), &Window::has_theme_font, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_color", "name", "type"), &Window::has_theme_color, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "type"), &Window::has_theme_constant, DEFVAL("")); + + ClassDB::bind_method(D_METHOD("popup", "rect"), &Window::popup, DEFVAL(Rect2i())); + ClassDB::bind_method(D_METHOD("popup_on_parent", "parent_rect"), &Window::popup_on_parent); + ClassDB::bind_method(D_METHOD("popup_centered_ratio", "ratio"), &Window::popup_centered_ratio, DEFVAL(0.8)); + ClassDB::bind_method(D_METHOD("popup_centered", "minsize"), &Window::popup_centered, DEFVAL(Size2i())); + ClassDB::bind_method(D_METHOD("popup_centered_clamped", "minsize", "fallback_ratio"), &Window::popup_centered, DEFVAL(Size2i()), DEFVAL(0.75)); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "position"), "set_position", "get_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Windowed,Minimized,Maximized,FullScreen"), "set_mode", "get_mode"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_screen"), "set_current_screen", "get_current_screen"); + ADD_GROUP("Flags", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_controls"), "set_wrap_controls", "is_wrapping_controls"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transient"), "set_transient", "is_transient"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclusive"), "set_exclusive", "is_exclusive"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "unresizable"), "set_flag", "get_flag", FLAG_RESIZE_DISABLED); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "borderless"), "set_flag", "get_flag", FLAG_BORDERLESS); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "always_on_top"), "set_flag", "get_flag", FLAG_ALWAYS_ON_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent"), "set_flag", "get_flag", FLAG_TRANSPARENT); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "unfocusable"), "set_flag", "get_flag", FLAG_NO_FOCUS); + ADD_GROUP("Limits", ""); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "min_size"), "set_min_size", "get_min_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "max_size"), "set_max_size", "get_max_size"); + ADD_GROUP("Content Scale", "content_scale_"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "content_scale_size"), "set_content_scale_size", "get_content_scale_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_mode", PROPERTY_HINT_ENUM, "Disabled,Object,Pixels"), "set_content_scale_mode", "get_content_scale_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_aspect", PROPERTY_HINT_ENUM, "Ignore,Keep,KeepWidth,KeepHeight,Expand"), "set_content_scale_aspect", "get_content_scale_aspect"); + ADD_GROUP("Theme", ""); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme"); + + ADD_SIGNAL(MethodInfo("window_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); + ADD_SIGNAL(MethodInfo("files_dropped", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files"))); + ADD_SIGNAL(MethodInfo("mouse_entered")); + ADD_SIGNAL(MethodInfo("mouse_exited")); + ADD_SIGNAL(MethodInfo("focus_entered")); + ADD_SIGNAL(MethodInfo("focus_exited")); + ADD_SIGNAL(MethodInfo("close_requested")); + ADD_SIGNAL(MethodInfo("go_back_requested")); + ADD_SIGNAL(MethodInfo("visibility_changed")); + ADD_SIGNAL(MethodInfo("about_to_popup")); + + BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED); + + BIND_ENUM_CONSTANT(MODE_WINDOWED); + BIND_ENUM_CONSTANT(MODE_MINIMIZED); + BIND_ENUM_CONSTANT(MODE_MAXIMIZED); + BIND_ENUM_CONSTANT(MODE_FULLSCREEN); + + BIND_ENUM_CONSTANT(FLAG_RESIZE_DISABLED); + BIND_ENUM_CONSTANT(FLAG_BORDERLESS); + BIND_ENUM_CONSTANT(FLAG_ALWAYS_ON_TOP); + BIND_ENUM_CONSTANT(FLAG_TRANSPARENT); + BIND_ENUM_CONSTANT(FLAG_NO_FOCUS); + BIND_ENUM_CONSTANT(FLAG_MAX); + + BIND_ENUM_CONSTANT(CONTENT_SCALE_MODE_DISABLED); + BIND_ENUM_CONSTANT(CONTENT_SCALE_MODE_OBJECTS); + BIND_ENUM_CONSTANT(CONTENT_SCALE_MODE_PIXELS); + + BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_IGNORE); + BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_KEEP); + BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_KEEP_WIDTH); + BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_KEEP_HEIGHT); + BIND_ENUM_CONSTANT(CONTENT_SCALE_ASPECT_EXPAND); +} + +Window::Window() { + for (int i = 0; i < FLAG_MAX; i++) { + flags[i] = false; + } + content_scale_mode = CONTENT_SCALE_MODE_DISABLED; + content_scale_aspect = CONTENT_SCALE_ASPECT_IGNORE; + VS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), VS::VIEWPORT_UPDATE_DISABLED); +} +Window::~Window() { +} diff --git a/scene/main/window.h b/scene/main/window.h new file mode 100644 index 0000000000..be07762f20 --- /dev/null +++ b/scene/main/window.h @@ -0,0 +1,266 @@ +/*************************************************************************/ +/* window.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WINDOW_H +#define WINDOW_H + +#include "scene/main/viewport.h" +#include "scene/resources/theme.h" +#include "servers/display_server.h" + +class Control; +class Window : public Viewport { + GDCLASS(Window, Viewport) +public: + enum Mode { + MODE_WINDOWED = DisplayServer::WINDOW_MODE_WINDOWED, + MODE_MINIMIZED = DisplayServer::WINDOW_MODE_MINIMIZED, + MODE_MAXIMIZED = DisplayServer::WINDOW_MODE_MAXIMIZED, + MODE_FULLSCREEN = DisplayServer::WINDOW_MODE_FULLSCREEN + }; + + enum Flags { + FLAG_RESIZE_DISABLED = DisplayServer::WINDOW_FLAG_RESIZE_DISABLED, + FLAG_BORDERLESS = DisplayServer::WINDOW_FLAG_BORDERLESS, + FLAG_ALWAYS_ON_TOP = DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP, + FLAG_TRANSPARENT = DisplayServer::WINDOW_FLAG_TRANSPARENT, + FLAG_NO_FOCUS = DisplayServer::WINDOW_FLAG_NO_FOCUS, + FLAG_MAX = DisplayServer::WINDOW_FLAG_MAX, + }; + + enum ContentScaleMode { + CONTENT_SCALE_MODE_DISABLED, + CONTENT_SCALE_MODE_OBJECTS, + CONTENT_SCALE_MODE_PIXELS, + + }; + + enum ContentScaleAspect { + CONTENT_SCALE_ASPECT_IGNORE, + CONTENT_SCALE_ASPECT_KEEP, + CONTENT_SCALE_ASPECT_KEEP_WIDTH, + CONTENT_SCALE_ASPECT_KEEP_HEIGHT, + CONTENT_SCALE_ASPECT_EXPAND, + + }; + enum { + DEFAULT_WINDOW_SIZE = 100 + }; + +private: + DisplayServer::WindowID window_id = DisplayServer::INVALID_WINDOW_ID; + + String title; + mutable int current_screen = 0; + mutable Vector2i position; + mutable Size2i size = Size2i(DEFAULT_WINDOW_SIZE, DEFAULT_WINDOW_SIZE); + mutable Size2i min_size; + mutable Size2i max_size; + mutable Mode mode = MODE_WINDOWED; + mutable bool flags[FLAG_MAX]; + bool visible = true; + bool focused = false; + + bool use_font_oversampling = false; + bool transient = false; + bool exclusive = false; + bool wrap_controls = false; + bool updating_child_controls = false; + + void _update_child_controls(); + + Size2i content_scale_size; + ContentScaleMode content_scale_mode; + ContentScaleAspect content_scale_aspect; + + void _make_window(); + void _clear_window(); + void _update_from_window(); + + void _update_viewport_size(); + void _update_window_size(); + + void _propagate_window_notification(Node *p_node, int p_notification); + + void _update_window_callbacks(); + + void _clear_transient(); + void _make_transient(); + Window *transient_parent = nullptr; + Window *exclusive_child = nullptr; + Set<Window *> transient_children; + + friend class Control; + Ref<Theme> theme; + Control *theme_owner = nullptr; + Window *theme_owner_window = nullptr; + + Viewport *embedder = nullptr; + + friend class Viewport; //friend back, can call the methods below + + void _window_input(const Ref<InputEvent> &p_ev); + void _window_input_text(const String &p_text); + void _window_drop_files(const Vector<String> &p_files); + void _rect_changed_callback(const Rect2i &p_callback); + void _event_callback(DisplayServer::WindowEvent p_event); + +protected: + Viewport *_get_embedder() const; + + virtual Rect2i _popup_adjust_rect() const { return Rect2i(); } + + virtual void _post_popup() {} + virtual Size2 _get_contents_minimum_size() const; + static void _bind_methods(); + void _notification(int p_what); + + virtual void add_child_notify(Node *p_child); + virtual void remove_child_notify(Node *p_child); + +public: + enum { + + NOTIFICATION_VISIBILITY_CHANGED = 30, + NOTIFICATION_POST_POPUP = 31, + NOTIFICATION_THEME_CHANGED = 32, + }; + + void set_title(const String &p_title); + String get_title() const; + + void set_current_screen(int p_screen); + int get_current_screen() const; + + void set_position(const Point2i &p_position); + Point2i get_position() const; + + void set_size(const Size2i &p_size); + Size2i get_size() const; + + Size2i get_real_size() const; + + void set_max_size(const Size2i &p_max_size); + Size2i get_max_size() const; + + void set_min_size(const Size2i &p_min_size); + Size2i get_min_size() const; + + void set_mode(Mode p_mode); + Mode get_mode() const; + + void set_flag(Flags p_flag, bool p_enabled); + bool get_flag(Flags p_flag) const; + + bool is_maximize_allowed() const; + + void request_attention(); + void move_to_foreground(); + + void set_visible(bool p_visible); + bool is_visible() const; + + void show(); + void hide(); + + void set_transient(bool p_transient); + bool is_transient() const; + + void set_exclusive(bool p_exclusive); + bool is_exclusive() const; + + bool can_draw() const; + + void set_ime_active(bool p_active); + void set_ime_position(const Point2i &p_pos); + + bool is_embedded() const; + + void set_content_scale_size(const Size2i &p_size); + Size2i get_content_scale_size() const; + + void set_content_scale_mode(ContentScaleMode p_mode); + ContentScaleMode get_content_scale_mode() const; + + void set_content_scale_aspect(ContentScaleAspect p_aspect); + ContentScaleAspect get_content_scale_aspect() const; + + void set_use_font_oversampling(bool p_oversampling); + bool is_using_font_oversampling() const; + + void set_wrap_controls(bool p_enable); + bool is_wrapping_controls() const; + void child_controls_changed(); + + Window *get_parent_visible_window() const; + Viewport *get_parent_viewport() const; + void popup(const Rect2i &p_rect = Rect2i()); + void popup_on_parent(const Rect2i &p_parent_rect); + void popup_centered_ratio(float p_ratio = 0.8); + void popup_centered(const Size2i &p_minsize = Size2i()); + void popup_centered_clamped(const Size2i &p_size = Size2i(), float p_fallback_ratio = 0.75); + + void set_theme(const Ref<Theme> &p_theme); + Ref<Theme> get_theme() const; + + Size2 get_contents_minimum_size() const; + + void grab_focus(); + bool has_focus() const; + + Rect2i get_usable_parent_rect() const; + + Ref<Texture2D> get_theme_icon(const StringName &p_name, const StringName &p_type = StringName()) const; + Ref<Shader> get_theme_shader(const StringName &p_name, const StringName &p_type = StringName()) const; + Ref<StyleBox> get_theme_stylebox(const StringName &p_name, const StringName &p_type = StringName()) const; + Ref<Font> get_theme_font(const StringName &p_name, const StringName &p_type = StringName()) const; + Color get_theme_color(const StringName &p_name, const StringName &p_type = StringName()) const; + int get_theme_constant(const StringName &p_name, const StringName &p_type = StringName()) const; + + bool has_theme_icon(const StringName &p_name, const StringName &p_type = StringName()) const; + bool has_theme_shader(const StringName &p_name, const StringName &p_type = StringName()) const; + bool has_theme_stylebox(const StringName &p_name, const StringName &p_type = StringName()) const; + bool has_theme_font(const StringName &p_name, const StringName &p_type = StringName()) const; + bool has_theme_color(const StringName &p_name, const StringName &p_type = StringName()) const; + bool has_theme_constant(const StringName &p_name, const StringName &p_type = StringName()) const; + + Rect2i get_parent_rect() const; + virtual DisplayServer::WindowID get_window_id() const; + + Window(); + ~Window(); +}; + +VARIANT_ENUM_CAST(Window::Window::Mode); +VARIANT_ENUM_CAST(Window::Window::Flags); +VARIANT_ENUM_CAST(Window::ContentScaleMode); +VARIANT_ENUM_CAST(Window::ContentScaleAspect); + +#endif // WINDOW_H |