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