Jelajahi Sumber

Implemented drag and drop across windows, both OS and embedded.

Juan Linietsky 5 tahun lalu
induk
melakukan
9a5d15a2dc

+ 20 - 0
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_

+ 8 - 1
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);

+ 33 - 2
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_

+ 8 - 1
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);

+ 167 - 76
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();

+ 2 - 0
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;

+ 2 - 1
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
 

+ 4 - 0
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));
 

+ 6 - 1
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 {