Skip to content

Commit

Permalink
Fix progress dialog steals focus
Browse files Browse the repository at this point in the history
  • Loading branch information
Hilderin committed Sep 24, 2024
1 parent 6681f25 commit a7d63a5
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 31 deletions.
15 changes: 14 additions & 1 deletion editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions editor/editor_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ class EditorNode : public Node {

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; }
Expand Down
79 changes: 52 additions & 27 deletions editor/progress_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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()) {
Expand All @@ -135,33 +151,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<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) {
Expand Down Expand Up @@ -231,6 +238,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();
}
Expand All @@ -241,17 +252,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();
Expand Down
14 changes: 11 additions & 3 deletions editor/progress_dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -64,8 +65,8 @@ class BackgroundProgress : public HBoxContainer {
BackgroundProgress() {}
};

class ProgressDialog : public PopupPanel {
GDCLASS(ProgressDialog, PopupPanel);
class ProgressDialog : public CenterContainer {
GDCLASS(ProgressDialog, CenterContainer);
struct Task {
String task;
VBoxContainer *vb = nullptr;
Expand All @@ -77,10 +78,13 @@ 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();

Expand All @@ -89,13 +93,17 @@ 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);
void remove_host_window(Window *p_window);

ProgressDialog();
};
Expand Down
6 changes: 6 additions & 0 deletions editor/window_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
1 change: 1 addition & 0 deletions editor/window_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class WindowWrapper : public MarginContainer {
void set_margins_enabled(bool p_enabled);

WindowWrapper();
~WindowWrapper();
};

class ScreenSelect : public Button {
Expand Down

0 comments on commit a7d63a5

Please sign in to comment.