Fix progress dialog steals focus

This commit is contained in:
Hilderin 2024-09-14 13:30:42 -04:00
parent 6681f2563b
commit 18d64b802b
5 changed files with 64 additions and 41 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) {
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);

View File

@ -592,6 +592,7 @@ private:
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;
bool has_main_screen() const { return true; }

View File

@ -35,6 +35,7 @@
#include "editor/editor_node.h"
#include "editor/themes/editor_scale.h"
#include "main/main.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 +127,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<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() {
// Run main loop for two frames.
if (is_inside_tree()) {
@ -135,33 +151,20 @@ 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);
Size2 ms = main->get_combined_minimum_size();
ms.width = MAX(500 * EDSCALE, ms.width);
ms += main_border_size;
Ref<StyleBox> 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, get_parent()->get_child_count() - 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,27 +234,32 @@ void ProgressDialog::end_task(const String &p_task) {
if (tasks.is_empty()) {
hide();
EditorNode::get_singleton()->set_process_input(false);
} else {
_popup();
}
}
void ProgressDialog::add_host_window(Window *p_window) {
ERR_FAIL_NULL(p_window);
host_windows.push_back(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();

View File

@ -33,8 +33,9 @@
#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/panel_container.h"
#include "scene/gui/progress_bar.h"
class BackgroundProgress : public HBoxContainer {
@ -64,8 +65,8 @@ public:
BackgroundProgress() {}
};
class ProgressDialog : public PopupPanel {
GDCLASS(ProgressDialog, PopupPanel);
class ProgressDialog : public CenterContainer {
GDCLASS(ProgressDialog, CenterContainer);
struct Task {
String task;
VBoxContainer *vb = nullptr;
@ -77,9 +78,10 @@ class ProgressDialog : public PopupPanel {
Button *cancel = nullptr;
HashMap<String, Task> tasks;
PanelContainer *center_panel = nullptr;
VBoxContainer *main = nullptr;
LocalVector<Window *> host_windows;
Size2 main_border_size;
static ProgressDialog *singleton;
void _popup();
@ -89,14 +91,15 @@ 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);
bool task_step(const String &p_task, const String &p_state, int p_step = -1, bool p_force_redraw = true);
void end_task(const String &p_task);
void add_host_window(Window *p_window);
ProgressDialog();
};

View File

@ -333,8 +333,6 @@ WindowWrapper::WindowWrapper() {
window_background = memnew(Panel);
window_background->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
window->add_child(window_background);
ProgressDialog::get_singleton()->add_host_window(window);
}
// ScreenSelect