From 2fa29dbb3903e56a94c0a3d9b3584f61adea3551 Mon Sep 17 00:00:00 2001 From: Hilderin <81109165+Hilderin@users.noreply.github.com> Date: Sat, 14 Sep 2024 13:30:42 -0400 Subject: [PATCH] Fix progress dialog steals focus --- editor/editor_node.cpp | 15 ++++++- editor/editor_node.h | 1 + editor/progress_dialog.cpp | 80 +++++++++++++++++++++++++------------- editor/progress_dialog.h | 15 +++++-- editor/window_wrapper.cpp | 6 +++ editor/window_wrapper.h | 1 + 6 files changed, 87 insertions(+), 31 deletions(-) diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 66fd2cf904ec..ccbff2653b2d 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -341,6 +341,19 @@ void EditorNode::_update_title() { } } +void EditorNode::input(const Ref &p_event) { + // EditorNode::get_singleton()->set_process_input is set to true in ProgressDialog + // only when the progress dialog is visible. + // We need to discard all key events to disable all shortcuts while the progress + // dialog is displayed, simulating an exclusive popup. Mouse events are + // captured by a full-screen container in front of the EditorNode in ProgressDialog, + // allowing interaction with the actual dialog where a Cancel button may be visible. + Ref k = p_event; + if (k.is_valid()) { + get_tree()->get_root()->set_input_as_handled(); + } +} + void EditorNode::shortcut_input(const Ref &p_event) { ERR_FAIL_COND(p_event.is_null()); @@ -6840,7 +6853,7 @@ EditorNode::EditorNode() { resource_preview = memnew(EditorResourcePreview); add_child(resource_preview); progress_dialog = memnew(ProgressDialog); - progress_dialog->set_unparent_when_invisible(true); + add_child(progress_dialog); progress_dialog->connect(SceneStringName(visibility_changed), callable_mp(this, &EditorNode::_progress_dialog_visibility_changed)); gui_base = memnew(Panel); diff --git a/editor/editor_node.h b/editor/editor_node.h index 4127dd1539db..95a299d06e7b 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -592,6 +592,7 @@ class EditorNode : public Node { void _exit_editor(int p_exit_code); + virtual void input(const Ref &p_event) override; virtual void shortcut_input(const Ref &p_event) override; bool has_main_screen() const { return true; } diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp index 2f345e516161..327cb3bddd89 100644 --- a/editor/progress_dialog.cpp +++ b/editor/progress_dialog.cpp @@ -35,6 +35,8 @@ #include "editor/editor_node.h" #include "editor/themes/editor_scale.h" #include "main/main.h" +#include "scene/gui/panel_container.h" +#include "scene/main/window.h" #include "servers/display_server.h" void BackgroundProgress::_add_task(const String &p_task, const String &p_label, int p_steps) { @@ -126,6 +128,21 @@ void BackgroundProgress::end_task(const String &p_task) { ProgressDialog *ProgressDialog::singleton = nullptr; +void ProgressDialog::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + Ref style = main->get_theme_stylebox(SceneStringName(panel), SNAME("PopupMenu")); + main_border_size = style->get_minimum_size(); + main->set_offset(SIDE_LEFT, style->get_margin(SIDE_LEFT)); + main->set_offset(SIDE_RIGHT, -style->get_margin(SIDE_RIGHT)); + main->set_offset(SIDE_TOP, style->get_margin(SIDE_TOP)); + main->set_offset(SIDE_BOTTOM, -style->get_margin(SIDE_BOTTOM)); + + center_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), "PopupPanel")); + } break; + } +} + void ProgressDialog::_update_ui() { // Run main loop for two frames. if (is_inside_tree()) { @@ -135,33 +152,24 @@ void ProgressDialog::_update_ui() { } void ProgressDialog::_popup() { + // Activate processing of all inputs in EditorNode, and the EditorNode::input method + // will discard every key input. + EditorNode::get_singleton()->set_process_input(true); + // Disable all other windows to prevent interaction with them. + for (Window *w : host_windows) { + w->set_process_mode(PROCESS_MODE_DISABLED); + } + Size2 ms = main->get_combined_minimum_size(); ms.width = MAX(500 * EDSCALE, ms.width); + ms += main_border_size; - Ref style = main->get_theme_stylebox(SceneStringName(panel), SNAME("PopupMenu")); - ms += style->get_minimum_size(); + center_panel->set_custom_minimum_size(ms); - main->set_offset(SIDE_LEFT, style->get_margin(SIDE_LEFT)); - main->set_offset(SIDE_RIGHT, -style->get_margin(SIDE_RIGHT)); - main->set_offset(SIDE_TOP, style->get_margin(SIDE_TOP)); - main->set_offset(SIDE_BOTTOM, -style->get_margin(SIDE_BOTTOM)); + // Be sure it's always the very last node to prevent user interaction while the dialog is visible. + get_parent()->move_child(this, -1); - if (is_inside_tree()) { - Rect2i adjust = _popup_adjust_rect(); - if (adjust != Rect2i()) { - set_position(adjust.position); - set_size(adjust.size); - } - } else { - for (Window *window : host_windows) { - if (window->has_focus()) { - popup_exclusive_centered(window, ms); - return; - } - } - // No host window found, use main window. - EditorInterface::get_singleton()->popup_dialog_centered(this, ms); - } + show(); } void ProgressDialog::add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) { @@ -231,6 +239,10 @@ void ProgressDialog::end_task(const String &p_task) { if (tasks.is_empty()) { hide(); + EditorNode::get_singleton()->set_process_input(false); + for (Window *w : host_windows) { + w->set_process_mode(PROCESS_MODE_INHERIT); + } } else { _popup(); } @@ -241,17 +253,31 @@ void ProgressDialog::add_host_window(Window *p_window) { host_windows.push_back(p_window); } +void ProgressDialog::remove_host_window(Window *p_window) { + ERR_FAIL_NULL(p_window); + host_windows.erase(p_window); +} + void ProgressDialog::_cancel_pressed() { canceled = true; } ProgressDialog::ProgressDialog() { - main = memnew(VBoxContainer); - add_child(main); - main->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); - set_exclusive(true); - set_flag(Window::FLAG_POPUP, false); + // We want to cover the entire screen to prevent the user from interacting with the Editor. + set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); + // Be sure it's the top most component. + set_z_index(RS::CANVAS_ITEM_Z_MAX); singleton = this; + hide(); + + center_panel = memnew(PanelContainer); + add_child(center_panel); + center_panel->set_h_size_flags(SIZE_SHRINK_BEGIN); + center_panel->set_v_size_flags(SIZE_SHRINK_BEGIN); + + main = memnew(VBoxContainer); + center_panel->add_child(main); + cancel_hb = memnew(HBoxContainer); main->add_child(cancel_hb); cancel_hb->hide(); diff --git a/editor/progress_dialog.h b/editor/progress_dialog.h index 355812b0b7a6..87fb02b37f9c 100644 --- a/editor/progress_dialog.h +++ b/editor/progress_dialog.h @@ -33,8 +33,8 @@ #include "scene/gui/box_container.h" #include "scene/gui/button.h" +#include "scene/gui/center_container.h" #include "scene/gui/label.h" -#include "scene/gui/popup.h" #include "scene/gui/progress_bar.h" class BackgroundProgress : public HBoxContainer { @@ -64,8 +64,10 @@ class BackgroundProgress : public HBoxContainer { BackgroundProgress() {} }; -class ProgressDialog : public PopupPanel { - GDCLASS(ProgressDialog, PopupPanel); +class PanelContainer; + +class ProgressDialog : public CenterContainer { + GDCLASS(ProgressDialog, CenterContainer); struct Task { String task; VBoxContainer *vb = nullptr; @@ -77,10 +79,13 @@ class ProgressDialog : public PopupPanel { Button *cancel = nullptr; HashMap tasks; + PanelContainer *center_panel = nullptr; VBoxContainer *main = nullptr; LocalVector host_windows; + Size2 main_border_size; + static ProgressDialog *singleton; void _popup(); @@ -89,6 +94,9 @@ class ProgressDialog : public PopupPanel { void _update_ui(); bool canceled = false; +protected: + void _notification(int p_what); + public: static ProgressDialog *get_singleton() { return singleton; } void add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel = false); @@ -96,6 +104,7 @@ class ProgressDialog : public PopupPanel { void end_task(const String &p_task); void add_host_window(Window *p_window); + void remove_host_window(Window *p_window); ProgressDialog(); }; diff --git a/editor/window_wrapper.cpp b/editor/window_wrapper.cpp index 9496ba016cd0..9d04bab75b45 100644 --- a/editor/window_wrapper.cpp +++ b/editor/window_wrapper.cpp @@ -337,6 +337,12 @@ WindowWrapper::WindowWrapper() { ProgressDialog::get_singleton()->add_host_window(window); } +WindowWrapper::~WindowWrapper() { + if (window) { + ProgressDialog::get_singleton()->remove_host_window(window); + } +} + // ScreenSelect void ScreenSelect::_build_advanced_menu() { diff --git a/editor/window_wrapper.h b/editor/window_wrapper.h index a07e95f09ee3..d975ac781692 100644 --- a/editor/window_wrapper.h +++ b/editor/window_wrapper.h @@ -82,6 +82,7 @@ class WindowWrapper : public MarginContainer { void set_margins_enabled(bool p_enabled); WindowWrapper(); + ~WindowWrapper(); }; class ScreenSelect : public Button {