Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/scene/resource_preloader_editor_plugin.cpp
20873 views
1
/**************************************************************************/
2
/* resource_preloader_editor_plugin.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 "resource_preloader_editor_plugin.h"
32
33
#include "core/io/resource_loader.h"
34
#include "editor/docks/editor_dock_manager.h"
35
#include "editor/editor_interface.h"
36
#include "editor/editor_node.h"
37
#include "editor/editor_undo_redo_manager.h"
38
#include "editor/gui/editor_file_dialog.h"
39
#include "editor/settings/editor_command_palette.h"
40
#include "editor/settings/editor_settings.h"
41
#include "editor/themes/editor_scale.h"
42
#include "scene/gui/dialogs.h"
43
#include "scene/gui/tree.h"
44
#include "scene/main/resource_preloader.h"
45
46
void ResourcePreloaderEditor::_notification(int p_what) {
47
switch (p_what) {
48
case NOTIFICATION_TRANSLATION_CHANGED: {
49
if (preloader) {
50
_update_library();
51
}
52
} break;
53
54
case NOTIFICATION_THEME_CHANGED: {
55
load->set_button_icon(get_editor_theme_icon(SNAME("Folder")));
56
} break;
57
}
58
}
59
60
void ResourcePreloaderEditor::_files_load_request(const Vector<String> &p_paths) {
61
for (int i = 0; i < p_paths.size(); i++) {
62
const String &path = p_paths[i];
63
64
Ref<Resource> resource;
65
resource = ResourceLoader::load(path);
66
67
if (resource.is_null()) {
68
dialog->set_text(TTRC("ERROR: Couldn't load resource!"));
69
dialog->popup_centered();
70
return; /// Beh, should show an error I guess.
71
}
72
73
String basename = path.get_file().get_basename();
74
String name = basename;
75
int counter = 1;
76
while (preloader->has_resource(name)) {
77
counter++;
78
name = basename + " " + itos(counter);
79
}
80
81
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
82
undo_redo->create_action(TTR("Add Resource"));
83
undo_redo->add_do_method(preloader, "add_resource", name, resource);
84
undo_redo->add_undo_method(preloader, "remove_resource", name);
85
undo_redo->add_do_method(this, "_update_library");
86
undo_redo->add_undo_method(this, "_update_library");
87
undo_redo->commit_action();
88
}
89
}
90
91
void ResourcePreloaderEditor::_load_pressed() {
92
loading_scene = false;
93
94
file->clear_filters();
95
List<String> extensions;
96
ResourceLoader::get_recognized_extensions_for_type("", &extensions);
97
for (const String &extension : extensions) {
98
file->add_filter("*." + extension);
99
}
100
101
file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILES);
102
file->popup_file_dialog();
103
}
104
105
void ResourcePreloaderEditor::_item_edited() {
106
if (!tree->get_selected()) {
107
return;
108
}
109
110
TreeItem *s = tree->get_selected();
111
112
if (tree->get_selected_column() == 0) {
113
// renamed
114
String old_name = s->get_metadata(0);
115
String new_name = s->get_text(0);
116
if (old_name == new_name) {
117
return;
118
}
119
120
if (new_name.is_empty() || new_name.contains_char('\\') || new_name.contains_char('/') || preloader->has_resource(new_name)) {
121
s->set_text(0, old_name);
122
return;
123
}
124
125
Ref<Resource> samp = preloader->get_resource(old_name);
126
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
127
undo_redo->create_action(TTR("Rename Resource"));
128
undo_redo->add_do_method(preloader, "remove_resource", old_name);
129
undo_redo->add_do_method(preloader, "add_resource", new_name, samp);
130
undo_redo->add_undo_method(preloader, "remove_resource", new_name);
131
undo_redo->add_undo_method(preloader, "add_resource", old_name, samp);
132
undo_redo->add_do_method(this, "_update_library");
133
undo_redo->add_undo_method(this, "_update_library");
134
undo_redo->commit_action();
135
}
136
}
137
138
void ResourcePreloaderEditor::_remove_resource(const String &p_to_remove) {
139
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
140
undo_redo->create_action(TTR("Delete Resource"));
141
undo_redo->add_do_method(preloader, "remove_resource", p_to_remove);
142
undo_redo->add_undo_method(preloader, "add_resource", p_to_remove, preloader->get_resource(p_to_remove));
143
undo_redo->add_do_method(this, "_update_library");
144
undo_redo->add_undo_method(this, "_update_library");
145
undo_redo->commit_action();
146
}
147
148
void ResourcePreloaderEditor::_paste_pressed() {
149
Ref<Resource> r = EditorSettings::get_singleton()->get_resource_clipboard();
150
if (r.is_null()) {
151
dialog->set_text(TTRC("Resource clipboard is empty!"));
152
dialog->popup_centered();
153
return; /// Beh, should show an error I guess.
154
}
155
156
String name = r->get_name();
157
if (name.is_empty()) {
158
name = r->get_path().get_file();
159
}
160
if (name.is_empty()) {
161
name = r->get_class();
162
}
163
164
String basename = name;
165
int counter = 1;
166
while (preloader->has_resource(name)) {
167
counter++;
168
name = basename + " " + itos(counter);
169
}
170
171
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
172
undo_redo->create_action(TTR("Paste Resource"));
173
undo_redo->add_do_method(preloader, "add_resource", name, r);
174
undo_redo->add_undo_method(preloader, "remove_resource", name);
175
undo_redo->add_do_method(this, "_update_library");
176
undo_redo->add_undo_method(this, "_update_library");
177
undo_redo->commit_action();
178
}
179
180
void ResourcePreloaderEditor::_update_library() {
181
tree->clear();
182
if (!preloader) {
183
return;
184
}
185
186
TreeItem *root = tree->create_item(nullptr);
187
188
List<StringName> rnames;
189
preloader->get_resource_list(&rnames);
190
191
List<String> names;
192
for (const StringName &E : rnames) {
193
names.push_back(E);
194
}
195
196
names.sort();
197
198
for (const String &E : names) {
199
TreeItem *ti = tree->create_item(root);
200
ti->set_cell_mode(0, TreeItem::CELL_MODE_STRING);
201
ti->set_editable(0, true);
202
ti->set_selectable(0, true);
203
ti->set_text(0, E);
204
ti->set_metadata(0, E);
205
206
Ref<Resource> r = preloader->get_resource(E);
207
208
ERR_CONTINUE(r.is_null());
209
210
String type = r->get_class();
211
ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(type));
212
ti->set_tooltip_text(0, TTR("Instance:") + " " + r->get_path() + "\n" + TTR("Type:") + " " + type);
213
214
ti->set_text(1, r->get_path());
215
ti->set_editable(1, false);
216
ti->set_selectable(1, false);
217
218
if (type == "PackedScene") {
219
ti->add_button(1, get_editor_theme_icon(SNAME("InstanceOptions")), BUTTON_OPEN_SCENE, false, TTR("Open in Editor"));
220
} else {
221
ti->add_button(1, get_editor_theme_icon(SNAME("Load")), BUTTON_EDIT_RESOURCE, false, TTR("Open in Editor"));
222
}
223
ti->add_button(1, get_editor_theme_icon(SNAME("Remove")), BUTTON_REMOVE, false, TTR("Remove"));
224
}
225
}
226
227
void ResourcePreloaderEditor::_cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) {
228
if (p_button != MouseButton::LEFT) {
229
return;
230
}
231
232
TreeItem *item = Object::cast_to<TreeItem>(p_item);
233
ERR_FAIL_NULL(item);
234
235
if (p_id == BUTTON_OPEN_SCENE) {
236
String rpath = item->get_text(p_column);
237
EditorInterface::get_singleton()->open_scene_from_path(rpath);
238
239
} else if (p_id == BUTTON_EDIT_RESOURCE) {
240
Ref<Resource> r = preloader->get_resource(item->get_text(0));
241
EditorInterface::get_singleton()->edit_resource(r);
242
243
} else if (p_id == BUTTON_REMOVE) {
244
_remove_resource(item->get_text(0));
245
}
246
}
247
248
void ResourcePreloaderEditor::edit(ResourcePreloader *p_preloader) {
249
preloader = p_preloader;
250
_update_library();
251
}
252
253
Variant ResourcePreloaderEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
254
TreeItem *ti = (p_point == Vector2(Math::INF, Math::INF)) ? tree->get_selected() : tree->get_item_at_position(p_point);
255
if (!ti) {
256
return Variant();
257
}
258
259
String name = ti->get_metadata(0);
260
261
Ref<Resource> res = preloader->get_resource(name);
262
if (res.is_null()) {
263
return Variant();
264
}
265
266
return EditorNode::get_singleton()->drag_resource(res, p_from);
267
}
268
269
bool ResourcePreloaderEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
270
Dictionary d = p_data;
271
272
if (!d.has("type")) {
273
return false;
274
}
275
276
if (d.has("from") && (Object *)(d["from"]) == tree) {
277
return false;
278
}
279
280
if (String(d["type"]) == "resource" && d.has("resource")) {
281
Ref<Resource> r = d["resource"];
282
283
return r.is_valid();
284
}
285
286
if (String(d["type"]) == "files") {
287
Vector<String> files = d["files"];
288
289
return files.size() != 0;
290
}
291
return false;
292
}
293
294
void ResourcePreloaderEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
295
if (!can_drop_data_fw(p_point, p_data, p_from)) {
296
return;
297
}
298
299
Dictionary d = p_data;
300
301
if (!d.has("type")) {
302
return;
303
}
304
305
if (String(d["type"]) == "resource" && d.has("resource")) {
306
Ref<Resource> r = d["resource"];
307
308
if (r.is_valid()) {
309
String basename;
310
if (!r->get_name().is_empty()) {
311
basename = r->get_name();
312
} else if (r->get_path().is_resource_file()) {
313
basename = r->get_path().get_basename();
314
} else {
315
basename = "Resource";
316
}
317
318
String name = basename;
319
int counter = 0;
320
while (preloader->has_resource(name)) {
321
counter++;
322
name = basename + "_" + itos(counter);
323
}
324
325
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
326
undo_redo->create_action(TTR("Add Resource"));
327
undo_redo->add_do_method(preloader, "add_resource", name, r);
328
undo_redo->add_undo_method(preloader, "remove_resource", name);
329
undo_redo->add_do_method(this, "_update_library");
330
undo_redo->add_undo_method(this, "_update_library");
331
undo_redo->commit_action();
332
}
333
}
334
335
if (String(d["type"]) == "files") {
336
Vector<String> files = d["files"];
337
338
_files_load_request(files);
339
}
340
}
341
342
void ResourcePreloaderEditor::update_layout(EditorDock::DockLayout p_layout) {
343
bool new_horizontal = (p_layout == EditorDock::DOCK_LAYOUT_HORIZONTAL);
344
if (horizontal == new_horizontal) {
345
return;
346
}
347
horizontal = new_horizontal;
348
349
if (horizontal) {
350
mc->set_theme_type_variation("NoBorderHorizontal");
351
tree->set_scroll_hint_mode(Tree::SCROLL_HINT_MODE_BOTH);
352
} else {
353
mc->set_theme_type_variation("NoBorderHorizontalBottom");
354
tree->set_scroll_hint_mode(Tree::SCROLL_HINT_MODE_TOP);
355
}
356
}
357
358
void ResourcePreloaderEditor::_bind_methods() {
359
ClassDB::bind_method(D_METHOD("_update_library"), &ResourcePreloaderEditor::_update_library);
360
ClassDB::bind_method(D_METHOD("_remove_resource", "to_remove"), &ResourcePreloaderEditor::_remove_resource);
361
}
362
363
ResourcePreloaderEditor::ResourcePreloaderEditor() {
364
set_name(TTRC("ResourcePreloader"));
365
set_icon_name("ResourcePreloader");
366
set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_resource_preloader_bottom_panel", TTRC("Toggle ResourcePreloader Dock")));
367
set_default_slot(EditorDock::DOCK_SLOT_BOTTOM);
368
set_available_layouts(EditorDock::DOCK_LAYOUT_ALL);
369
set_global(false);
370
set_transient(true);
371
372
set_custom_minimum_size(Size2(0, 250 * EDSCALE));
373
374
VBoxContainer *vbc = memnew(VBoxContainer);
375
add_child(vbc);
376
377
HBoxContainer *hbc = memnew(HBoxContainer);
378
vbc->add_child(hbc);
379
380
load = memnew(Button);
381
load->set_tooltip_text(TTRC("Load Resource"));
382
hbc->add_child(load);
383
384
paste = memnew(Button);
385
paste->set_text(TTRC("Paste"));
386
hbc->add_child(paste);
387
388
file = memnew(EditorFileDialog);
389
add_child(file);
390
391
mc = memnew(MarginContainer);
392
mc->set_v_size_flags(SIZE_EXPAND_FILL);
393
vbc->add_child(mc);
394
395
tree = memnew(Tree);
396
tree->connect("button_clicked", callable_mp(this, &ResourcePreloaderEditor::_cell_button_pressed));
397
tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
398
tree->set_hide_root(true);
399
tree->set_columns(2);
400
tree->set_column_expand_ratio(0, 2);
401
tree->set_column_clip_content(0, true);
402
tree->set_column_expand_ratio(1, 3);
403
tree->set_column_clip_content(1, true);
404
405
SET_DRAG_FORWARDING_GCD(tree, ResourcePreloaderEditor);
406
mc->add_child(tree);
407
408
dialog = memnew(AcceptDialog);
409
dialog->set_title(TTRC("Error!"));
410
dialog->set_ok_button_text(TTRC("Close"));
411
add_child(dialog);
412
413
load->connect(SceneStringName(pressed), callable_mp(this, &ResourcePreloaderEditor::_load_pressed));
414
paste->connect(SceneStringName(pressed), callable_mp(this, &ResourcePreloaderEditor::_paste_pressed));
415
file->connect("files_selected", callable_mp(this, &ResourcePreloaderEditor::_files_load_request));
416
tree->connect("item_edited", callable_mp(this, &ResourcePreloaderEditor::_item_edited));
417
loading_scene = false;
418
}
419
420
void ResourcePreloaderEditorPlugin::edit(Object *p_object) {
421
ResourcePreloader *s = Object::cast_to<ResourcePreloader>(p_object);
422
if (!s) {
423
return;
424
}
425
426
preloader_editor->edit(s);
427
}
428
429
bool ResourcePreloaderEditorPlugin::handles(Object *p_object) const {
430
return Object::cast_to<ResourcePreloader>(p_object);
431
}
432
433
void ResourcePreloaderEditorPlugin::make_visible(bool p_visible) {
434
if (p_visible) {
435
preloader_editor->make_visible();
436
} else {
437
preloader_editor->close();
438
}
439
}
440
441
ResourcePreloaderEditorPlugin::ResourcePreloaderEditorPlugin() {
442
preloader_editor = memnew(ResourcePreloaderEditor);
443
EditorDockManager::get_singleton()->add_dock(preloader_editor);
444
preloader_editor->close();
445
}
446
447