Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/gui/progress_dialog.cpp
9903 views
1
/**************************************************************************/
2
/* progress_dialog.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "progress_dialog.h"
32
33
#include "core/os/os.h"
34
#include "editor/editor_interface.h"
35
#include "editor/editor_node.h"
36
#include "editor/themes/editor_scale.h"
37
#include "main/main.h"
38
#include "scene/gui/panel_container.h"
39
#include "scene/main/window.h"
40
#include "servers/display_server.h"
41
42
void BackgroundProgress::_add_task(const String &p_task, const String &p_label, int p_steps) {
43
_THREAD_SAFE_METHOD_
44
ERR_FAIL_COND_MSG(tasks.has(p_task), "Task '" + p_task + "' already exists.");
45
BackgroundProgress::Task t;
46
t.hb = memnew(HBoxContainer);
47
Label *l = memnew(Label);
48
l->set_text(p_label + " ");
49
t.hb->add_child(l);
50
t.progress = memnew(ProgressBar);
51
t.progress->set_max(p_steps);
52
t.progress->set_value(p_steps);
53
Control *ec = memnew(Control);
54
ec->set_h_size_flags(SIZE_EXPAND_FILL);
55
ec->set_v_size_flags(SIZE_EXPAND_FILL);
56
t.progress->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
57
ec->add_child(t.progress);
58
ec->set_custom_minimum_size(Size2(80, 5) * EDSCALE);
59
t.hb->add_child(ec);
60
61
add_child(t.hb);
62
63
tasks[p_task] = t;
64
}
65
66
void BackgroundProgress::_update() {
67
_THREAD_SAFE_METHOD_
68
69
for (const KeyValue<String, int> &E : updates) {
70
if (tasks.has(E.key)) {
71
_task_step(E.key, E.value);
72
}
73
}
74
75
updates.clear();
76
}
77
78
void BackgroundProgress::_task_step(const String &p_task, int p_step) {
79
_THREAD_SAFE_METHOD_
80
81
ERR_FAIL_COND(!tasks.has(p_task));
82
83
Task &t = tasks[p_task];
84
if (p_step < 0) {
85
t.progress->set_value(t.progress->get_value() + 1);
86
} else {
87
t.progress->set_value(p_step);
88
}
89
}
90
91
void BackgroundProgress::_end_task(const String &p_task) {
92
_THREAD_SAFE_METHOD_
93
94
ERR_FAIL_COND(!tasks.has(p_task));
95
Task &t = tasks[p_task];
96
97
memdelete(t.hb);
98
tasks.erase(p_task);
99
}
100
101
void BackgroundProgress::add_task(const String &p_task, const String &p_label, int p_steps) {
102
callable_mp(this, &BackgroundProgress::_add_task).call_deferred(p_task, p_label, p_steps);
103
}
104
105
void BackgroundProgress::task_step(const String &p_task, int p_step) {
106
//this code is weird, but it prevents deadlock.
107
bool no_updates = true;
108
{
109
_THREAD_SAFE_METHOD_
110
no_updates = updates.is_empty();
111
}
112
113
if (no_updates) {
114
callable_mp(this, &BackgroundProgress::_update).call_deferred();
115
}
116
117
{
118
_THREAD_SAFE_METHOD_
119
updates[p_task] = p_step;
120
}
121
}
122
123
void BackgroundProgress::end_task(const String &p_task) {
124
callable_mp(this, &BackgroundProgress::_end_task).call_deferred(p_task);
125
}
126
127
////////////////////////////////////////////////
128
129
ProgressDialog *ProgressDialog::singleton = nullptr;
130
131
void ProgressDialog::_notification(int p_what) {
132
switch (p_what) {
133
case NOTIFICATION_THEME_CHANGED: {
134
Ref<StyleBox> style = main->get_theme_stylebox(SceneStringName(panel), SNAME("PopupMenu"));
135
main_border_size = style->get_minimum_size();
136
main->set_offset(SIDE_LEFT, style->get_margin(SIDE_LEFT));
137
main->set_offset(SIDE_RIGHT, -style->get_margin(SIDE_RIGHT));
138
main->set_offset(SIDE_TOP, style->get_margin(SIDE_TOP));
139
main->set_offset(SIDE_BOTTOM, -style->get_margin(SIDE_BOTTOM));
140
141
center_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), "PopupPanel"));
142
} break;
143
}
144
}
145
146
void ProgressDialog::_update_ui() {
147
// Run main loop for two frames.
148
if (is_inside_tree()) {
149
DisplayServer::get_singleton()->process_events();
150
Main::iteration();
151
}
152
}
153
154
void ProgressDialog::_popup() {
155
// Activate processing of all inputs in EditorNode, and the EditorNode::input method
156
// will discard every key input.
157
EditorNode::get_singleton()->set_process_input(true);
158
// Disable all other windows to prevent interaction with them.
159
for (Window *w : host_windows) {
160
w->set_process_mode(PROCESS_MODE_DISABLED);
161
}
162
163
Size2 ms = main->get_combined_minimum_size();
164
ms.width = MAX(500 * EDSCALE, ms.width);
165
ms += main_border_size;
166
167
center_panel->set_custom_minimum_size(ms);
168
169
if (is_ready()) {
170
_reparent_and_show();
171
} else {
172
callable_mp(this, &ProgressDialog::_reparent_and_show).call_deferred();
173
}
174
}
175
176
void ProgressDialog::_reparent_and_show() {
177
Window *current_window = SceneTree::get_singleton()->get_root()->get_last_exclusive_window();
178
ERR_FAIL_NULL(current_window);
179
reparent(current_window);
180
181
// Ensures that events are properly released before the dialog blocks input.
182
bool window_is_input_disabled = current_window->is_input_disabled();
183
current_window->set_disable_input(!window_is_input_disabled);
184
current_window->set_disable_input(window_is_input_disabled);
185
186
show();
187
}
188
189
void ProgressDialog::add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) {
190
if (MessageQueue::get_singleton()->is_flushing()) {
191
ERR_PRINT("Do not use progress dialog (task) while flushing the message queue or using call_deferred()!");
192
return;
193
}
194
195
ERR_FAIL_COND_MSG(tasks.has(p_task), "Task '" + p_task + "' already exists.");
196
ProgressDialog::Task t;
197
t.vb = memnew(VBoxContainer);
198
VBoxContainer *vb2 = memnew(VBoxContainer);
199
t.vb->add_margin_child(p_label, vb2);
200
t.progress = memnew(ProgressBar);
201
t.progress->set_max(p_steps);
202
t.progress->set_value(p_steps);
203
vb2->add_child(t.progress);
204
t.state = memnew(Label);
205
t.state->set_clip_text(true);
206
vb2->add_child(t.state);
207
main->add_child(t.vb);
208
209
tasks[p_task] = t;
210
if (p_can_cancel) {
211
cancel_hb->show();
212
} else {
213
cancel_hb->hide();
214
}
215
cancel_hb->move_to_front();
216
canceled = false;
217
_popup();
218
if (p_can_cancel) {
219
cancel->grab_focus();
220
}
221
_update_ui();
222
}
223
224
bool ProgressDialog::task_step(const String &p_task, const String &p_state, int p_step, bool p_force_redraw) {
225
ERR_FAIL_COND_V(!tasks.has(p_task), canceled);
226
227
Task &t = tasks[p_task];
228
if (!p_force_redraw) {
229
uint64_t tus = OS::get_singleton()->get_ticks_usec();
230
if (tus - t.last_progress_tick < 200000) { //200ms
231
return canceled;
232
}
233
}
234
if (p_step < 0) {
235
t.progress->set_value(t.progress->get_value() + 1);
236
} else {
237
t.progress->set_value(p_step);
238
}
239
240
t.state->set_text(p_state);
241
t.last_progress_tick = OS::get_singleton()->get_ticks_usec();
242
_update_ui();
243
244
return canceled;
245
}
246
247
void ProgressDialog::end_task(const String &p_task) {
248
ERR_FAIL_COND(!tasks.has(p_task));
249
Task &t = tasks[p_task];
250
251
memdelete(t.vb);
252
tasks.erase(p_task);
253
254
if (tasks.is_empty()) {
255
hide();
256
EditorNode::get_singleton()->set_process_input(false);
257
for (Window *w : host_windows) {
258
w->set_process_mode(PROCESS_MODE_INHERIT);
259
}
260
} else {
261
_popup();
262
}
263
}
264
265
void ProgressDialog::add_host_window(Window *p_window) {
266
ERR_FAIL_NULL(p_window);
267
host_windows.push_back(p_window);
268
}
269
270
void ProgressDialog::remove_host_window(Window *p_window) {
271
ERR_FAIL_NULL(p_window);
272
host_windows.erase(p_window);
273
}
274
275
void ProgressDialog::_cancel_pressed() {
276
canceled = true;
277
}
278
279
ProgressDialog::ProgressDialog() {
280
// We want to cover the entire screen to prevent the user from interacting with the Editor.
281
set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
282
// Be sure it's the top most component.
283
set_z_index(RS::CANVAS_ITEM_Z_MAX);
284
singleton = this;
285
hide();
286
287
center_panel = memnew(PanelContainer);
288
add_child(center_panel);
289
center_panel->set_h_size_flags(SIZE_SHRINK_BEGIN);
290
center_panel->set_v_size_flags(SIZE_SHRINK_BEGIN);
291
292
main = memnew(VBoxContainer);
293
center_panel->add_child(main);
294
295
cancel_hb = memnew(HBoxContainer);
296
main->add_child(cancel_hb);
297
cancel_hb->hide();
298
cancel = memnew(Button);
299
cancel_hb->add_spacer();
300
cancel_hb->add_child(cancel);
301
cancel->set_text(TTR("Cancel"));
302
cancel_hb->add_spacer();
303
cancel->connect(SceneStringName(pressed), callable_mp(this, &ProgressDialog::_cancel_pressed));
304
}
305
306