diff options
author | Juan Linietsky <reduzio@gmail.com> | 2020-03-24 20:15:35 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2020-03-26 15:49:46 +0100 |
commit | 9a5d15a2dcb08aa9f3732a0e8e41f2e81c2be365 (patch) | |
tree | 1de67674b96aa82e3b4fc33bcc394a545d4f639c | |
parent | 047e0b7de5ca981d955739074919646fade828fb (diff) |
Implemented drag and drop across windows, both OS and embedded.
-rw-r--r-- | platform/linuxbsd/display_server_x11.cpp | 20 | ||||
-rw-r--r-- | platform/linuxbsd/display_server_x11.h | 9 | ||||
-rw-r--r-- | platform/windows/display_server_windows.cpp | 35 | ||||
-rw-r--r-- | platform/windows/display_server_windows.h | 9 | ||||
-rw-r--r-- | scene/main/viewport.cpp | 243 | ||||
-rw-r--r-- | scene/main/viewport.h | 2 | ||||
-rw-r--r-- | scene/main/window.cpp | 3 | ||||
-rw-r--r-- | servers/display_server.cpp | 4 | ||||
-rw-r--r-- | servers/display_server.h | 7 |
9 files changed, 250 insertions, 82 deletions
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index 8e225d9b5d..ffe6af41e2 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -699,6 +699,26 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) { windows.erase(p_id); } +void DisplayServerX11::window_attach_instance_id(ObjectID p_instance, WindowID p_window) { + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + + wd.instance_id = p_instance; +} + +ObjectID DisplayServerX11::window_get_attached_instance_id(WindowID p_window) const { + + ERR_FAIL_COND_V(!windows.has(p_window), ObjectID()); + const WindowData &wd = windows[p_window]; + return wd.instance_id; +} + +DisplayServerX11::WindowID DisplayServerX11::get_window_at_screen_position(const Point2i &p_position) const { + + return INVALID_WINDOW_ID; +} + void DisplayServerX11::window_set_title(const String &p_title, WindowID p_window) { _THREAD_SAFE_METHOD_ diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h index bfce4dc88a..5747ac93ba 100644 --- a/platform/linuxbsd/display_server_x11.h +++ b/platform/linuxbsd/display_server_x11.h @@ -130,6 +130,8 @@ class DisplayServerX11 : public DisplayServer { WindowID transient_parent = INVALID_WINDOW_ID; Set<WindowID> transient_children; + ObjectID instance_id; + //better to guess on the fly, given WM can change it //WindowMode mode; bool fullscreen = false; //OS can't exit from this mode @@ -263,11 +265,16 @@ public: virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const; virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const; - virtual Vector<int> get_window_list() const; + virtual Vector<DisplayServer::WindowID> get_window_list() const; virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()); virtual void delete_sub_window(WindowID p_id); + virtual WindowID get_window_at_screen_position(const Point2i &p_position) const; + + virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID); + virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const; + virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID); virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID); virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID); diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index bbf305da3a..707337bc8d 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -435,17 +435,32 @@ bool DisplayServerWindows::screen_is_kept_on() const { return false; } -Vector<int> DisplayServerWindows::get_window_list() const { +Vector<DisplayServer::WindowID> DisplayServerWindows::get_window_list() const { _THREAD_SAFE_METHOD_ - Vector<int> ret; + Vector<DisplayServer::WindowID> ret; for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { ret.push_back(E->key()); } return ret; } +DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(const Point2i &p_position) const { + + POINT p; + p.x = p_position.x; + p.y = p_position.y; + HWND hwnd = WindowFromPoint(p); + for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { + if (E->get().hWnd == hwnd) { + return E->key(); + } + } + + return INVALID_WINDOW_ID; +} + DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) { _THREAD_SAFE_METHOD_ @@ -504,6 +519,22 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) { windows.erase(p_window); } +void DisplayServerWindows::window_attach_instance_id(ObjectID p_instance, WindowID p_window) { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + windows[p_window].instance_id = p_instance; +} + +ObjectID DisplayServerWindows::window_get_attached_instance_id(WindowID p_window) const { + + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND_V(!windows.has(p_window), ObjectID()); + return windows[p_window].instance_id; +} + void DisplayServerWindows::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) { _THREAD_SAFE_METHOD_ diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index c8ee39ecaf..47bb3f59a6 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -195,6 +195,8 @@ class DisplayServerWindows : public DisplayServer { Size2 window_rect; Point2 last_pos; + ObjectID instance_id; + // IME HIMC im_himc; Vector2 im_position; @@ -291,11 +293,16 @@ public: virtual void screen_set_keep_on(bool p_enable); //disable screensaver virtual bool screen_is_kept_on() const; - virtual Vector<int> get_window_list() const; + virtual Vector<DisplayServer::WindowID> get_window_list() const; virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()); virtual void delete_sub_window(WindowID p_window); + virtual WindowID get_window_at_screen_position(const Point2i &p_position) const; + + virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID); + virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const; + virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID); virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 309c9ee47d..e49674663a 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -196,6 +196,7 @@ Viewport::GUI::GUI() { mouse_focus_mask = 0; key_focus = NULL; mouse_over = NULL; + drag_mouse_over = NULL; tooltip = NULL; tooltip_popup = NULL; @@ -1960,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) { @@ -1974,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 } @@ -2098,107 +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) { - DisplayServer::get_singleton()->cursor_set_shape((DisplayServer::CursorShape)InputFilter::get_singleton()->get_default_cursor_shape()); - return; - } - - 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 + if (over) { - mm->set_global_position(mpos); - mm->set_speed(speed); - mm->set_relative(rel); + 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()); - if (mm->get_button_mask() == 0) { - //nothing pressed + mm = mm->xformed_by(Transform2D()); //make a copy - bool can_tooltip = true; + mm->set_global_position(mpos); + mm->set_speed(speed); + mm->set_relative(rel); - bool is_tooltip_shown = false; + if (mm->get_button_mask() == 0) { + //nothing pressed - if (gui.tooltip_popup) { - if (can_tooltip && gui.tooltip) { - String tooltip = _gui_get_tooltip(over, gui.tooltip->get_global_transform().xform_inv(mpos)); + bool can_tooltip = true; - if (tooltip.length() == 0) - _gui_cancel_tooltip(); - else if (gui.tooltip_label) { - if (tooltip == gui.tooltip_label->get_text()) { - is_tooltip_shown = true; - } - } else { + bool is_tooltip_shown = false; - Variant t = gui.tooltip_popup->call("get_tooltip_text"); + if (gui.tooltip_popup) { + if (can_tooltip && gui.tooltip) { + String tooltip = _gui_get_tooltip(over, gui.tooltip->get_global_transform().xform_inv(mpos)); - if (t.get_type() == Variant::STRING) { - if (tooltip == String(t)) { + if (tooltip.length() == 0) + _gui_cancel_tooltip(); + else if (gui.tooltip_label) { + if (tooltip == gui.tooltip_label->get_text()) { 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 + + Variant t = gui.tooltip_popup->call("get_tooltip_text"); + + 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 - _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 = over->get_screen_transform().xform(pos); //(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 + } + } } - } - DisplayServer::get_singleton()->cursor_set_shape((DisplayServer::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 (swrect.has_point(viewport_pos)) { + viewport_under = sw; + viewport_pos -= sw->get_position(); + } + } - if (gui.drag_data.get_type() != Variant::NIL && mm->get_button_mask() & BUTTON_MASK_LEFT) { + 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(); + } + } + } + } - bool can_drop = _gui_drop(over, pos, true); + 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) { - DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_FORBIDDEN); } else { - DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::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; @@ -2443,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(); } @@ -2461,6 +2550,8 @@ 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; } @@ -2852,7 +2943,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { _sub_window_update(sw.window); } else { - gui.subwindow_resize_mode = _sub_window_get_resize_margin(gui.subwindow_focused, mb->get_position()); + 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(); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index e9c5a05ce2..6b68bc0c94 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -310,6 +310,8 @@ private: int mouse_focus_mask; Control *key_focus; Control *mouse_over; + Control *drag_mouse_over; + Vector2 drag_mouse_over_pos; Control *tooltip; Window *tooltip_popup; Label *tooltip_label; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index ea6d4a46bd..f8a86b0957 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -228,6 +228,7 @@ void Window::_make_window() { 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(); @@ -715,6 +716,7 @@ void Window::_notification(int p_what) { //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 { @@ -869,7 +871,6 @@ void Window::child_controls_changed() { } 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 diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 8432842469..f2103a2856 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -263,6 +263,7 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("screen_is_kept_on"), &DisplayServer::screen_is_kept_on); ClassDB::bind_method(D_METHOD("get_window_list"), &DisplayServer::get_window_list); + ClassDB::bind_method(D_METHOD("get_window_at_screen_position", "position"), &DisplayServer::get_window_at_screen_position); ClassDB::bind_method(D_METHOD("create_sub_window", "mode", "rect"), &DisplayServer::create_sub_window, DEFVAL(Rect2i())); ClassDB::bind_method(D_METHOD("delete_sub_window", "window_id"), &DisplayServer::delete_sub_window); @@ -283,6 +284,9 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("window_set_input_text_callback", "callback", "window_id"), &DisplayServer::window_set_input_text_callback, DEFVAL(MAIN_WINDOW_ID)); ClassDB::bind_method(D_METHOD("window_set_drop_files_callback", "callback", "window_id"), &DisplayServer::window_set_drop_files_callback, DEFVAL(MAIN_WINDOW_ID)); + ClassDB::bind_method(D_METHOD("window_attach_instance_id", "instance_id", "window_id"), &DisplayServer::window_get_max_size, DEFVAL(MAIN_WINDOW_ID)); + ClassDB::bind_method(D_METHOD("window_get_attached_instance_id", "window_id"), &DisplayServer::window_get_max_size, DEFVAL(MAIN_WINDOW_ID)); + ClassDB::bind_method(D_METHOD("window_get_max_size", "window_id"), &DisplayServer::window_get_max_size, DEFVAL(MAIN_WINDOW_ID)); ClassDB::bind_method(D_METHOD("window_set_max_size", "max_size", "window_id"), &DisplayServer::window_set_max_size, DEFVAL(MAIN_WINDOW_ID)); diff --git a/servers/display_server.h b/servers/display_server.h index fd70c8919f..51acf637e5 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -166,7 +166,7 @@ public: typedef int WindowID; - virtual Vector<int> get_window_list() const = 0; + virtual Vector<DisplayServer::WindowID> get_window_list() const = 0; enum WindowFlags { WINDOW_FLAG_RESIZE_DISABLED, @@ -185,6 +185,11 @@ public: virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i & = Rect2i()); virtual void delete_sub_window(WindowID p_id); + virtual WindowID get_window_at_screen_position(const Point2i &p_position) const = 0; + + virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) = 0; + virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const = 0; + virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) = 0; enum WindowEvent { |