Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix progress dialog steals focus #97009

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion editor/editor_node.cpp
Original file line number Diff line number Diff line change
@@ -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);
1 change: 1 addition & 0 deletions editor/editor_node.h
Original file line number Diff line number Diff line change
@@ -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; }
80 changes: 53 additions & 27 deletions editor/progress_dialog.cpp
Original file line number Diff line number Diff line change
@@ -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<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 +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<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, -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();
15 changes: 12 additions & 3 deletions editor/progress_dialog.h
Original file line number Diff line number Diff line change
@@ -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<String, Task> tasks;
PanelContainer *center_panel = nullptr;
VBoxContainer *main = nullptr;

LocalVector<Window *> host_windows;

Size2 main_border_size;

static ProgressDialog *singleton;
void _popup();

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

WindowWrapper();
~WindowWrapper();
};

class ScreenSelect : public Button {