1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-11 13:10:58 +00:00

Merge pull request #99844 from KoBeWi/give_back_the_focus

Fix progress dialog steals focus
This commit is contained in:
Rémi Verschelde
2025-01-14 12:08:46 +01:00
6 changed files with 98 additions and 31 deletions

View File

@@ -341,6 +341,19 @@ void EditorNode::_update_title() {
} }
} }
void EditorNode::input(const Ref<InputEvent> &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<InputEventKey> k = p_event;
if (k.is_valid()) {
get_tree()->get_root()->set_input_as_handled();
}
}
void EditorNode::shortcut_input(const Ref<InputEvent> &p_event) { void EditorNode::shortcut_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null()); ERR_FAIL_COND(p_event.is_null());
@@ -7079,7 +7092,7 @@ EditorNode::EditorNode() {
resource_preview = memnew(EditorResourcePreview); resource_preview = memnew(EditorResourcePreview);
add_child(resource_preview); add_child(resource_preview);
progress_dialog = memnew(ProgressDialog); 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)); progress_dialog->connect(SceneStringName(visibility_changed), callable_mp(this, &EditorNode::_progress_dialog_visibility_changed));
gui_base = memnew(Panel); gui_base = memnew(Panel);

View File

@@ -592,6 +592,7 @@ private:
void _exit_editor(int p_exit_code); void _exit_editor(int p_exit_code);
virtual void input(const Ref<InputEvent> &p_event) override;
virtual void shortcut_input(const Ref<InputEvent> &p_event) override; virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
bool has_main_screen() const { return true; } bool has_main_screen() const { return true; }

View File

@@ -35,6 +35,8 @@
#include "editor/editor_node.h" #include "editor/editor_node.h"
#include "editor/themes/editor_scale.h" #include "editor/themes/editor_scale.h"
#include "main/main.h" #include "main/main.h"
#include "scene/gui/panel_container.h"
#include "scene/main/window.h"
#include "servers/display_server.h" #include "servers/display_server.h"
void BackgroundProgress::_add_task(const String &p_task, const String &p_label, int p_steps) { 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; ProgressDialog *ProgressDialog::singleton = nullptr;
void ProgressDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
Ref<StyleBox> 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() { void ProgressDialog::_update_ui() {
// Run main loop for two frames. // Run main loop for two frames.
if (is_inside_tree()) { if (is_inside_tree()) {
@@ -135,33 +152,33 @@ void ProgressDialog::_update_ui() {
} }
void ProgressDialog::_popup() { 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(); Size2 ms = main->get_combined_minimum_size();
ms.width = MAX(500 * EDSCALE, ms.width); ms.width = MAX(500 * EDSCALE, ms.width);
ms += main_border_size;
Ref<StyleBox> style = main->get_theme_stylebox(SceneStringName(panel), SNAME("PopupMenu")); center_panel->set_custom_minimum_size(ms);
ms += style->get_minimum_size();
main->set_offset(SIDE_LEFT, style->get_margin(SIDE_LEFT)); Window *current_window = Window::get_from_id(DisplayServer::get_singleton()->get_focused_window());
main->set_offset(SIDE_RIGHT, -style->get_margin(SIDE_RIGHT)); if (!current_window) {
main->set_offset(SIDE_TOP, style->get_margin(SIDE_TOP)); current_window = get_tree()->get_root();
main->set_offset(SIDE_BOTTOM, -style->get_margin(SIDE_BOTTOM)); }
if (is_inside_tree()) { reparent(current_window);
Rect2i adjust = _popup_adjust_rect();
if (adjust != Rect2i()) { // Ensures that events are properly released before the dialog blocks input.
set_position(adjust.position); bool window_is_input_disabled = current_window->is_input_disabled();
set_size(adjust.size); current_window->set_disable_input(!window_is_input_disabled);
} current_window->set_disable_input(window_is_input_disabled);
} else {
for (Window *window : host_windows) { show();
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);
}
} }
void ProgressDialog::add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) { void ProgressDialog::add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) {
@@ -231,6 +248,10 @@ void ProgressDialog::end_task(const String &p_task) {
if (tasks.is_empty()) { if (tasks.is_empty()) {
hide(); hide();
EditorNode::get_singleton()->set_process_input(false);
for (Window *w : host_windows) {
w->set_process_mode(PROCESS_MODE_INHERIT);
}
} else { } else {
_popup(); _popup();
} }
@@ -241,17 +262,31 @@ void ProgressDialog::add_host_window(Window *p_window) {
host_windows.push_back(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() { void ProgressDialog::_cancel_pressed() {
canceled = true; canceled = true;
} }
ProgressDialog::ProgressDialog() { ProgressDialog::ProgressDialog() {
main = memnew(VBoxContainer); // We want to cover the entire screen to prevent the user from interacting with the Editor.
add_child(main); set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
main->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); // Be sure it's the top most component.
set_exclusive(true); set_z_index(RS::CANVAS_ITEM_Z_MAX);
set_flag(Window::FLAG_POPUP, false);
singleton = this; 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); cancel_hb = memnew(HBoxContainer);
main->add_child(cancel_hb); main->add_child(cancel_hb);
cancel_hb->hide(); cancel_hb->hide();

View File

@@ -33,8 +33,8 @@
#include "scene/gui/box_container.h" #include "scene/gui/box_container.h"
#include "scene/gui/button.h" #include "scene/gui/button.h"
#include "scene/gui/center_container.h"
#include "scene/gui/label.h" #include "scene/gui/label.h"
#include "scene/gui/popup.h"
#include "scene/gui/progress_bar.h" #include "scene/gui/progress_bar.h"
class BackgroundProgress : public HBoxContainer { class BackgroundProgress : public HBoxContainer {
@@ -64,8 +64,10 @@ public:
BackgroundProgress() {} BackgroundProgress() {}
}; };
class ProgressDialog : public PopupPanel { class PanelContainer;
GDCLASS(ProgressDialog, PopupPanel);
class ProgressDialog : public CenterContainer {
GDCLASS(ProgressDialog, CenterContainer);
struct Task { struct Task {
String task; String task;
VBoxContainer *vb = nullptr; VBoxContainer *vb = nullptr;
@@ -77,10 +79,13 @@ class ProgressDialog : public PopupPanel {
Button *cancel = nullptr; Button *cancel = nullptr;
HashMap<String, Task> tasks; HashMap<String, Task> tasks;
PanelContainer *center_panel = nullptr;
VBoxContainer *main = nullptr; VBoxContainer *main = nullptr;
LocalVector<Window *> host_windows; LocalVector<Window *> host_windows;
Size2 main_border_size;
static ProgressDialog *singleton; static ProgressDialog *singleton;
void _popup(); void _popup();
@@ -89,6 +94,9 @@ class ProgressDialog : public PopupPanel {
void _update_ui(); void _update_ui();
bool canceled = false; bool canceled = false;
protected:
void _notification(int p_what);
public: public:
static ProgressDialog *get_singleton() { return singleton; } 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); void add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel = false);
@@ -96,6 +104,7 @@ public:
void end_task(const String &p_task); void end_task(const String &p_task);
void add_host_window(Window *p_window); void add_host_window(Window *p_window);
void remove_host_window(Window *p_window);
ProgressDialog(); ProgressDialog();
}; };

View File

@@ -336,6 +336,7 @@ WindowWrapper::WindowWrapper() {
} }
window = memnew(Window); window = memnew(Window);
window_id = window->get_instance_id();
window->set_wrap_controls(true); window->set_wrap_controls(true);
add_child(window); add_child(window);
@@ -354,6 +355,12 @@ WindowWrapper::WindowWrapper() {
ProgressDialog::get_singleton()->add_host_window(window); ProgressDialog::get_singleton()->add_host_window(window);
} }
WindowWrapper::~WindowWrapper() {
if (ObjectDB::get_instance(window_id)) {
ProgressDialog::get_singleton()->remove_host_window(window);
}
}
// ScreenSelect // ScreenSelect
void ScreenSelect::_build_advanced_menu() { void ScreenSelect::_build_advanced_menu() {

View File

@@ -44,6 +44,7 @@ class WindowWrapper : public MarginContainer {
Control *wrapped_control = nullptr; Control *wrapped_control = nullptr;
MarginContainer *margins = nullptr; MarginContainer *margins = nullptr;
Window *window = nullptr; Window *window = nullptr;
ObjectID window_id;
Panel *window_background = nullptr; Panel *window_background = nullptr;
@@ -84,6 +85,7 @@ public:
void grab_window_focus(); void grab_window_focus();
WindowWrapper(); WindowWrapper();
~WindowWrapper();
}; };
class ScreenSelect : public Button { class ScreenSelect : public Button {