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