فهرست منبع

Support for transient windows

Juan Linietsky 5 سال پیش
والد
کامیت
f51fdc6eef

+ 43 - 0
platform/linuxbsd/display_server_x11.cpp

@@ -640,6 +640,14 @@ void DisplayServerX11::delete_sub_window(WindowID p_id) {
 
 	WindowData &wd = windows[p_id];
 
+	while (wd.transient_children.size()) {
+		window_set_transient(wd.transient_children.front()->get(), INVALID_WINDOW_ID);
+	}
+
+	if (wd.transient_parent != INVALID_WINDOW_ID) {
+		window_set_transient(p_id, INVALID_WINDOW_ID);
+	}
+
 #ifdef VULKAN_ENABLED
 	if (rendering_driver == "vulkan") {
 		context_vulkan->window_destroy(p_id);
@@ -733,6 +741,40 @@ void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window
 	}
 }
 
+void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent) {
+
+	ERR_FAIL_COND(p_window == p_parent);
+
+	ERR_FAIL_COND(!windows.has(p_window));
+	WindowData &wd_window = windows[p_window];
+
+	ERR_FAIL_COND(wd_window.transient_parent == p_parent);
+
+	ERR_FAIL_COND_MSG(wd_window.on_top, "Windows with the 'on top' can't become transient.");
+	if (p_parent == INVALID_WINDOW_ID) {
+		//remove transient
+
+		ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID);
+		ERR_FAIL_COND(!windows.has(wd_window.transient_parent));
+
+		WindowData &wd_parent = windows[wd_window.transient_parent];
+
+		wd_window.transient_parent = INVALID_WINDOW_ID;
+		wd_parent.transient_children.erase(p_window);
+
+		XSetTransientForHint(x11_display, wd_window.x11_window, None);
+	} else {
+		ERR_FAIL_COND(!windows.has(p_parent));
+		ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
+		WindowData &wd_parent = windows[p_parent];
+
+		wd_window.transient_parent = p_parent;
+		wd_parent.transient_children.insert(p_window);
+
+		XSetTransientForHint(x11_display, wd_window.x11_window, wd_parent.x11_window);
+	}
+}
+
 Point2i DisplayServerX11::window_get_position(WindowID p_window) const {
 	ERR_FAIL_COND_V(!windows.has(p_window), Point2i());
 	const WindowData &wd = windows[p_window];
@@ -1353,6 +1395,7 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
 		} break;
 		case WINDOW_FLAG_ALWAYS_ON_TOP: {
 
+			ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID, "Can't make a window transient if the 'on top' flag is active.");
 			if (p_enabled && wd.fullscreen) {
 				_set_wm_maximized(p_window, true);
 			}

+ 5 - 0
platform/linuxbsd/display_server_x11.h

@@ -125,6 +125,9 @@ class DisplayServerX11 : public DisplayServer {
 		Callable input_text_callback;
 		Callable drop_files_callback;
 
+		WindowID transient_parent = INVALID_WINDOW_ID;
+		Set<WindowID> transient_children;
+
 		//better to guess on the fly, given WM can change it
 		//WindowMode mode;
 		bool fullscreen = false; //OS can't exit from this mode
@@ -278,6 +281,8 @@ public:
 	virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
 	virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const;
 
+	virtual void window_set_transient(WindowID p_window, WindowID p_parent);
+
 	virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
 	virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const;
 

+ 172 - 2
scene/main/window.cpp

@@ -217,6 +217,16 @@ void Window::_make_window() {
 	DisplayServer::get_singleton()->window_set_title(title, window_id);
 
 	_update_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);
+		}
+	}
 }
 void Window::_update_from_window() {
 
@@ -233,6 +243,17 @@ void Window::_update_from_window() {
 
 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;
@@ -310,6 +331,69 @@ void Window::set_visible(bool p_visible) {
 		_update_size();
 	}
 }
+
+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);
+		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);
+	}
+
+	//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;
+}
+
 bool Window::is_visible() const {
 	return visible;
 }
@@ -467,9 +551,18 @@ void Window::_notification(int p_what) {
 				_update_window_callbacks();
 			}
 		}
+
+		if (transient) {
+			_make_transient();
+		}
 	}
 
 	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) {
@@ -495,7 +588,7 @@ Size2i Window::get_content_scale_size() const {
 	return content_scale_size;
 }
 
-void Window::set_content_scale_mode(const ContentScaleMode &p_mode) {
+void Window::set_content_scale_mode(ContentScaleMode p_mode) {
 	content_scale_mode = p_mode;
 	_update_size();
 }
@@ -503,7 +596,7 @@ Window::ContentScaleMode Window::get_content_scale_mode() const {
 	return content_scale_mode;
 }
 
-void Window::set_content_scale_aspect(const ContentScaleAspect &p_aspect) {
+void Window::set_content_scale_aspect(ContentScaleAspect p_aspect) {
 	content_scale_aspect = p_aspect;
 	_update_size();
 }
@@ -553,6 +646,83 @@ void Window::_window_drop_files(const Vector<String> &p_files) {
 
 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("set_transient", "transient"), &Window::set_transient);
+	ClassDB::bind_method(D_METHOD("is_transient"), &Window::is_transient);
+
+	ClassDB::bind_method(D_METHOD("can_draw"), &Window::is_transient);
+
+	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);
+
+	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, "transient"), "set_transient", "is_transient");
+	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_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_SIGNAL(MethodInfo("files_dropped"));
 	ADD_SIGNAL(MethodInfo("mouse_entered"));
 	ADD_SIGNAL(MethodInfo("mouse_exited"));

+ 11 - 2
scene/main/window.h

@@ -85,6 +85,7 @@ private:
 	bool visible = true;
 
 	bool use_font_oversampling = false;
+	bool transient = false;
 
 	Size2i content_scale_size;
 	ContentScaleMode content_scale_mode;
@@ -110,6 +111,11 @@ private:
 	void _window_unhandled_input(const Ref<InputEvent> &p_ev);
 	void _update_window_callbacks();
 
+	void _clear_transient();
+	void _make_transient();
+	Window *transient_parent = nullptr;
+	Set<Window *> transient_children;
+
 protected:
 	static void _bind_methods();
 	void _notification(int p_what);
@@ -149,6 +155,9 @@ public:
 	void set_visible(bool p_visible);
 	bool is_visible() const;
 
+	void set_transient(bool p_transient);
+	bool is_transient() const;
+
 	bool can_draw() const;
 
 	void set_ime_active(bool p_active);
@@ -159,10 +168,10 @@ public:
 	void set_content_scale_size(const Size2i &p_size);
 	Size2i get_content_scale_size() const;
 
-	void set_content_scale_mode(const ContentScaleMode &p_mode);
+	void set_content_scale_mode(ContentScaleMode p_mode);
 	ContentScaleMode get_content_scale_mode() const;
 
-	void set_content_scale_aspect(const ContentScaleAspect &p_aspect);
+	void set_content_scale_aspect(ContentScaleAspect p_aspect);
 	ContentScaleAspect get_content_scale_aspect() const;
 
 	void set_use_font_oversampling(bool p_oversampling);

+ 2 - 0
servers/display_server.cpp

@@ -299,6 +299,8 @@ void DisplayServer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("window_move_to_foreground", "window_id"), &DisplayServer::window_move_to_foreground, DEFVAL(MAIN_WINDOW_ID));
 	ClassDB::bind_method(D_METHOD("window_can_draw", "window_id"), &DisplayServer::window_can_draw, DEFVAL(MAIN_WINDOW_ID));
 
+	ClassDB::bind_method(D_METHOD("window_set_transient", "window_id", "parent_window_id"), &DisplayServer::window_set_transient);
+
 	ClassDB::bind_method(D_METHOD("window_set_ime_active", "active", "window_id"), &DisplayServer::window_set_ime_active, DEFVAL(MAIN_WINDOW_ID));
 	ClassDB::bind_method(D_METHOD("window_set_ime_position", "position", "window_id"), &DisplayServer::window_set_ime_position, DEFVAL(MAIN_WINDOW_ID));
 

+ 3 - 0
servers/display_server.h

@@ -194,6 +194,7 @@ public:
 	virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) = 0;
 	virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) = 0;
 	virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) = 0;
+
 	virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) = 0;
 
 	virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) = 0;
@@ -204,6 +205,8 @@ public:
 	virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const = 0;
 	virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) = 0;
 
+	virtual void window_set_transient(WindowID p_window, WindowID p_parent) = 0;
+
 	virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) = 0;
 	virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const = 0;