Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/docks/history_dock.cpp
21023 views
1
/**************************************************************************/
2
/* history_dock.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 "history_dock.h"
32
33
#include "core/io/config_file.h"
34
#include "editor/editor_node.h"
35
#include "editor/editor_string_names.h"
36
#include "editor/editor_undo_redo_manager.h"
37
#include "editor/settings/editor_command_palette.h"
38
#include "editor/settings/editor_settings.h"
39
#include "scene/gui/check_box.h"
40
#include "scene/gui/item_list.h"
41
42
struct SortActionsByTimestamp {
43
bool operator()(const EditorUndoRedoManager::Action &l, const EditorUndoRedoManager::Action &r) const {
44
return l.timestamp > r.timestamp;
45
}
46
};
47
48
void HistoryDock::on_history_changed() {
49
if (is_visible_in_tree()) {
50
refresh_history();
51
} else {
52
need_refresh = true;
53
}
54
}
55
56
void HistoryDock::refresh_history() {
57
action_list->clear();
58
bool include_scene = current_scene_checkbox->is_pressed();
59
bool include_global = global_history_checkbox->is_pressed();
60
61
if (!include_scene && !include_global) {
62
action_list->add_item(TTRC("The Beginning"));
63
action_list->set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_ALWAYS);
64
return;
65
}
66
67
const EditorUndoRedoManager::History &current_scene_history = ur_manager->get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id());
68
const EditorUndoRedoManager::History &global_history = ur_manager->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY);
69
70
Vector<EditorUndoRedoManager::Action> full_history;
71
{
72
int full_size = 0;
73
if (include_scene) {
74
full_size += current_scene_history.redo_stack.size() + current_scene_history.undo_stack.size();
75
}
76
if (include_global) {
77
full_size += global_history.redo_stack.size() + global_history.undo_stack.size();
78
}
79
full_history.resize(full_size);
80
}
81
82
int i = 0;
83
if (include_scene) {
84
for (const EditorUndoRedoManager::Action &E : current_scene_history.redo_stack) {
85
full_history.write[i] = E;
86
i++;
87
}
88
for (const EditorUndoRedoManager::Action &E : current_scene_history.undo_stack) {
89
full_history.write[i] = E;
90
i++;
91
}
92
}
93
if (include_global) {
94
for (const EditorUndoRedoManager::Action &E : global_history.redo_stack) {
95
full_history.write[i] = E;
96
i++;
97
}
98
for (const EditorUndoRedoManager::Action &E : global_history.undo_stack) {
99
full_history.write[i] = E;
100
i++;
101
}
102
}
103
104
full_history.sort_custom<SortActionsByTimestamp>();
105
for (const EditorUndoRedoManager::Action &E : full_history) {
106
action_list->add_item(E.action_name);
107
if (E.history_id == EditorUndoRedoManager::GLOBAL_HISTORY) {
108
action_list->set_item_custom_fg_color(-1, get_theme_color(SNAME("accent_color"), EditorStringName(Editor)));
109
}
110
}
111
112
action_list->add_item(TTRC("The Beginning"));
113
action_list->set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_ALWAYS);
114
refresh_version();
115
}
116
117
void HistoryDock::on_version_changed() {
118
if (is_visible_in_tree()) {
119
refresh_version();
120
} else {
121
need_refresh = true;
122
}
123
}
124
125
void HistoryDock::refresh_version() {
126
int idx = 0;
127
bool include_scene = current_scene_checkbox->is_pressed();
128
bool include_global = global_history_checkbox->is_pressed();
129
130
if (!include_scene && !include_global) {
131
current_version = idx;
132
action_list->set_current(idx);
133
return;
134
}
135
136
const EditorUndoRedoManager::History &current_scene_history = ur_manager->get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id());
137
const EditorUndoRedoManager::History &global_history = ur_manager->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY);
138
double newest_undo_timestamp = 0;
139
140
if (include_scene && !current_scene_history.undo_stack.is_empty()) {
141
newest_undo_timestamp = current_scene_history.undo_stack.front()->get().timestamp;
142
}
143
144
if (include_global && !global_history.undo_stack.is_empty()) {
145
double global_undo_timestamp = global_history.undo_stack.front()->get().timestamp;
146
if (global_undo_timestamp > newest_undo_timestamp) {
147
newest_undo_timestamp = global_undo_timestamp;
148
}
149
}
150
151
if (include_scene) {
152
int skip = 0;
153
for (const EditorUndoRedoManager::Action &E : current_scene_history.redo_stack) {
154
if (E.timestamp < newest_undo_timestamp) {
155
skip++;
156
} else {
157
break;
158
}
159
}
160
idx += current_scene_history.redo_stack.size() - skip;
161
}
162
163
if (include_global) {
164
int skip = 0;
165
for (const EditorUndoRedoManager::Action &E : global_history.redo_stack) {
166
if (E.timestamp < newest_undo_timestamp) {
167
skip++;
168
} else {
169
break;
170
}
171
}
172
idx += global_history.redo_stack.size() - skip;
173
}
174
175
current_version = idx;
176
action_list->set_current(idx);
177
}
178
179
void HistoryDock::save_layout_to_config(Ref<ConfigFile> &p_layout, const String &p_section) const {
180
p_layout->set_value(p_section, "include_scene", current_scene_checkbox->is_pressed());
181
p_layout->set_value(p_section, "include_global", global_history_checkbox->is_pressed());
182
}
183
184
void HistoryDock::load_layout_from_config(const Ref<ConfigFile> &p_layout, const String &p_section) {
185
current_scene_checkbox->set_pressed_no_signal(p_layout->get_value(p_section, "include_scene", true));
186
global_history_checkbox->set_pressed_no_signal(p_layout->get_value(p_section, "include_global", true));
187
refresh_history();
188
}
189
190
void HistoryDock::seek_history(int p_index) {
191
bool include_scene = current_scene_checkbox->is_pressed();
192
bool include_global = global_history_checkbox->is_pressed();
193
194
if (!include_scene && !include_global) {
195
return;
196
}
197
int current_scene_id = EditorNode::get_editor_data().get_current_edited_scene_history_id();
198
199
while (current_version < p_index) {
200
if (include_scene) {
201
if (include_global) {
202
ur_manager->undo();
203
} else {
204
ur_manager->undo_history(current_scene_id);
205
}
206
} else {
207
ur_manager->undo_history(EditorUndoRedoManager::GLOBAL_HISTORY);
208
}
209
}
210
211
while (current_version > p_index) {
212
if (include_scene) {
213
if (include_global) {
214
ur_manager->redo();
215
} else {
216
ur_manager->redo_history(current_scene_id);
217
}
218
} else {
219
ur_manager->redo_history(EditorUndoRedoManager::GLOBAL_HISTORY);
220
}
221
}
222
}
223
224
void HistoryDock::_notification(int p_notification) {
225
switch (p_notification) {
226
case NOTIFICATION_READY: {
227
EditorNode::get_singleton()->connect("scene_changed", callable_mp(this, &HistoryDock::on_history_changed));
228
} break;
229
230
case NOTIFICATION_VISIBILITY_CHANGED: {
231
if (is_visible_in_tree() && need_refresh) {
232
refresh_history();
233
}
234
} break;
235
}
236
}
237
238
HistoryDock::HistoryDock() {
239
set_name(TTRC("History"));
240
set_icon_name("History");
241
set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("docks/open_history", TTRC("Open History Dock")));
242
set_default_slot(EditorDock::DOCK_SLOT_LEFT_BR);
243
244
ur_manager = EditorUndoRedoManager::get_singleton();
245
ur_manager->connect("history_changed", callable_mp(this, &HistoryDock::on_history_changed));
246
ur_manager->connect("version_changed", callable_mp(this, &HistoryDock::on_version_changed));
247
248
VBoxContainer *main_vb = memnew(VBoxContainer);
249
add_child(main_vb);
250
251
HBoxContainer *mode_hb = memnew(HBoxContainer);
252
main_vb->add_child(mode_hb);
253
254
current_scene_checkbox = memnew(CheckBox);
255
mode_hb->add_child(current_scene_checkbox);
256
current_scene_checkbox->set_flat(true);
257
current_scene_checkbox->set_text(TTRC("Scene"));
258
current_scene_checkbox->set_h_size_flags(SIZE_EXPAND_FILL);
259
current_scene_checkbox->set_clip_text(true);
260
current_scene_checkbox->set_pressed(true);
261
current_scene_checkbox->connect(SceneStringName(toggled), callable_mp(this, &HistoryDock::refresh_history).unbind(1));
262
263
global_history_checkbox = memnew(CheckBox);
264
mode_hb->add_child(global_history_checkbox);
265
global_history_checkbox->set_flat(true);
266
global_history_checkbox->set_text(TTRC("Global"));
267
global_history_checkbox->set_h_size_flags(SIZE_EXPAND_FILL);
268
global_history_checkbox->set_clip_text(true);
269
global_history_checkbox->set_pressed(true);
270
global_history_checkbox->connect(SceneStringName(toggled), callable_mp(this, &HistoryDock::refresh_history).unbind(1));
271
272
MarginContainer *mc = memnew(MarginContainer);
273
mc->set_theme_type_variation("NoBorderHorizontalBottom");
274
mc->set_v_size_flags(SIZE_EXPAND_FILL);
275
main_vb->add_child(mc);
276
277
action_list = memnew(ItemList);
278
action_list->set_scroll_hint_mode(ItemList::SCROLL_HINT_MODE_TOP);
279
action_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
280
mc->add_child(action_list);
281
action_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
282
action_list->connect(SceneStringName(item_selected), callable_mp(this, &HistoryDock::seek_history));
283
}
284
285