| 1 | /**************************************************************************/ |
| 2 | /* editor_data.h */ |
| 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 | #ifndef EDITOR_DATA_H |
| 32 | #define EDITOR_DATA_H |
| 33 | |
| 34 | #include "core/templates/list.h" |
| 35 | #include "scene/resources/texture.h" |
| 36 | |
| 37 | class ConfigFile; |
| 38 | class EditorPlugin; |
| 39 | class EditorUndoRedoManager; |
| 40 | |
| 41 | /** |
| 42 | * Stores the history of objects which have been selected for editing in the Editor & the Inspector. |
| 43 | * |
| 44 | * Used in the editor to set & access the currently edited object, as well as the history of objects which have been edited. |
| 45 | */ |
| 46 | class EditorSelectionHistory { |
| 47 | // Stores the object & property (if relevant). |
| 48 | struct _Object { |
| 49 | Ref<RefCounted> ref; |
| 50 | ObjectID object; |
| 51 | String property; |
| 52 | bool inspector_only = false; |
| 53 | }; |
| 54 | |
| 55 | // Represents the selection of an object for editing. |
| 56 | struct HistoryElement { |
| 57 | // The sub-resources of the parent object (first in the path) that have been edited. |
| 58 | // For example, Node2D -> nested resource -> nested resource, if edited each individually in their own inspector. |
| 59 | Vector<_Object> path; |
| 60 | // The current point in the path. This is always equal to the last item in the path - it is never decremented. |
| 61 | int level = 0; |
| 62 | }; |
| 63 | friend class EditorData; |
| 64 | |
| 65 | Vector<HistoryElement> history; |
| 66 | int current_elem_idx; // The current history element being edited. |
| 67 | |
| 68 | public: |
| 69 | void cleanup_history(); |
| 70 | |
| 71 | bool is_at_beginning() const; |
| 72 | bool is_at_end() const; |
| 73 | |
| 74 | // Adds an object to the selection history. A property name can be passed if the target is a subresource of the given object. |
| 75 | // If the object should not change the main screen plugin, it can be set as inspector only. |
| 76 | void add_object(ObjectID p_object, const String &p_property = String(), bool p_inspector_only = false); |
| 77 | |
| 78 | int get_history_len(); |
| 79 | int get_history_pos(); |
| 80 | |
| 81 | // Gets an object from the history. The most recent object would be the object with p_obj = get_history_len() - 1. |
| 82 | ObjectID get_history_obj(int p_obj) const; |
| 83 | |
| 84 | bool next(); |
| 85 | bool previous(); |
| 86 | ObjectID get_current(); |
| 87 | bool is_current_inspector_only() const; |
| 88 | |
| 89 | // Gets the size of the path of the current history item. |
| 90 | int get_path_size() const; |
| 91 | // Gets the object of the current history item, if valid. |
| 92 | ObjectID get_path_object(int p_index) const; |
| 93 | // Gets the property of the current history item. |
| 94 | String get_path_property(int p_index) const; |
| 95 | |
| 96 | void clear(); |
| 97 | |
| 98 | EditorSelectionHistory(); |
| 99 | }; |
| 100 | |
| 101 | class EditorSelection; |
| 102 | |
| 103 | class EditorData { |
| 104 | public: |
| 105 | struct CustomType { |
| 106 | String name; |
| 107 | Ref<Script> script; |
| 108 | Ref<Texture2D> icon; |
| 109 | }; |
| 110 | |
| 111 | struct EditedScene { |
| 112 | Node *root = nullptr; |
| 113 | String path; |
| 114 | uint64_t file_modified_time = 0; |
| 115 | Dictionary editor_states; |
| 116 | List<Node *> selection; |
| 117 | Vector<EditorSelectionHistory::HistoryElement> history_stored; |
| 118 | int history_current = 0; |
| 119 | Dictionary custom_state; |
| 120 | NodePath live_edit_root; |
| 121 | int history_id = 0; |
| 122 | uint64_t last_checked_version = 0; |
| 123 | }; |
| 124 | |
| 125 | private: |
| 126 | Vector<EditorPlugin *> editor_plugins; |
| 127 | HashMap<StringName, EditorPlugin *> extension_editor_plugins; |
| 128 | |
| 129 | struct PropertyData { |
| 130 | String name; |
| 131 | Variant value; |
| 132 | }; |
| 133 | HashMap<String, Vector<CustomType>> custom_types; |
| 134 | |
| 135 | List<PropertyData> clipboard; |
| 136 | EditorUndoRedoManager *undo_redo_manager; |
| 137 | Vector<Callable> undo_redo_callbacks; |
| 138 | HashMap<StringName, Callable> move_element_functions; |
| 139 | |
| 140 | Vector<EditedScene> edited_scene; |
| 141 | int current_edited_scene = -1; |
| 142 | int last_created_scene = 1; |
| 143 | |
| 144 | bool _find_updated_instances(Node *p_root, Node *p_node, HashSet<String> &checked_paths); |
| 145 | |
| 146 | HashMap<StringName, String> _script_class_icon_paths; |
| 147 | HashMap<String, StringName> _script_class_file_to_path; |
| 148 | HashMap<Ref<Script>, Ref<Texture>> _script_icon_cache; |
| 149 | |
| 150 | Ref<Texture2D> _load_script_icon(const String &p_path) const; |
| 151 | |
| 152 | public: |
| 153 | EditorPlugin *get_handling_main_editor(Object *p_object); |
| 154 | Vector<EditorPlugin *> get_handling_sub_editors(Object *p_object); |
| 155 | EditorPlugin *get_editor_by_name(String p_name); |
| 156 | |
| 157 | void copy_object_params(Object *p_object); |
| 158 | void paste_object_params(Object *p_object); |
| 159 | |
| 160 | Dictionary get_editor_plugin_states() const; |
| 161 | Dictionary get_scene_editor_states(int p_idx) const; |
| 162 | void set_editor_plugin_states(const Dictionary &p_states); |
| 163 | void get_editor_breakpoints(List<String> *p_breakpoints); |
| 164 | void clear_editor_states(); |
| 165 | void save_editor_external_data(); |
| 166 | void apply_changes_in_editors(); |
| 167 | |
| 168 | void add_editor_plugin(EditorPlugin *p_plugin); |
| 169 | void remove_editor_plugin(EditorPlugin *p_plugin); |
| 170 | |
| 171 | int get_editor_plugin_count() const; |
| 172 | EditorPlugin *get_editor_plugin(int p_idx); |
| 173 | |
| 174 | void add_extension_editor_plugin(const StringName &p_class_name, EditorPlugin *p_plugin); |
| 175 | void remove_extension_editor_plugin(const StringName &p_class_name); |
| 176 | bool has_extension_editor_plugin(const StringName &p_class_name); |
| 177 | EditorPlugin *get_extension_editor_plugin(const StringName &p_class_name); |
| 178 | |
| 179 | void add_undo_redo_inspector_hook_callback(Callable p_callable); // Callbacks should have this signature: void (Object* undo_redo, Object *modified_object, String property, Variant new_value) |
| 180 | void remove_undo_redo_inspector_hook_callback(Callable p_callable); |
| 181 | const Vector<Callable> get_undo_redo_inspector_hook_callback(); |
| 182 | |
| 183 | void add_move_array_element_function(const StringName &p_class, Callable p_callable); // Function should have this signature: void (Object* undo_redo, Object *modified_object, String array_prefix, int element_index, int new_position) |
| 184 | void remove_move_array_element_function(const StringName &p_class); |
| 185 | Callable get_move_array_element_function(const StringName &p_class) const; |
| 186 | |
| 187 | void save_editor_global_states(); |
| 188 | |
| 189 | void add_custom_type(const String &p_type, const String &p_inherits, const Ref<Script> &p_script, const Ref<Texture2D> &p_icon); |
| 190 | Variant instantiate_custom_type(const String &p_type, const String &p_inherits); |
| 191 | void remove_custom_type(const String &p_type); |
| 192 | const HashMap<String, Vector<CustomType>> &get_custom_types() const { return custom_types; } |
| 193 | const CustomType *get_custom_type_by_name(const String &p_name) const; |
| 194 | const CustomType *get_custom_type_by_path(const String &p_path) const; |
| 195 | bool is_type_recognized(const String &p_type) const; |
| 196 | |
| 197 | void instantiate_object_properties(Object *p_object); |
| 198 | |
| 199 | int add_edited_scene(int p_at_pos); |
| 200 | void move_edited_scene_index(int p_idx, int p_to_idx); |
| 201 | void remove_scene(int p_idx); |
| 202 | void set_edited_scene(int p_idx); |
| 203 | void set_edited_scene_root(Node *p_root); |
| 204 | int get_edited_scene() const; |
| 205 | int get_edited_scene_from_path(const String &p_path) const; |
| 206 | Node *get_edited_scene_root(int p_idx = -1); |
| 207 | int get_edited_scene_count() const; |
| 208 | Vector<EditedScene> get_edited_scenes() const; |
| 209 | |
| 210 | String get_scene_title(int p_idx, bool p_always_strip_extension = false) const; |
| 211 | String get_scene_path(int p_idx) const; |
| 212 | String get_scene_type(int p_idx) const; |
| 213 | void set_scene_path(int p_idx, const String &p_path); |
| 214 | Ref<Script> get_scene_root_script(int p_idx) const; |
| 215 | void set_scene_modified_time(int p_idx, uint64_t p_time); |
| 216 | uint64_t get_scene_modified_time(int p_idx) const; |
| 217 | void clear_edited_scenes(); |
| 218 | void set_edited_scene_live_edit_root(const NodePath &p_root); |
| 219 | NodePath get_edited_scene_live_edit_root(); |
| 220 | bool check_and_update_scene(int p_idx); |
| 221 | void move_edited_scene_to_index(int p_idx); |
| 222 | |
| 223 | bool call_build(); |
| 224 | |
| 225 | void set_scene_as_saved(int p_idx); |
| 226 | bool is_scene_changed(int p_idx); |
| 227 | |
| 228 | int get_scene_history_id_from_path(const String &p_path) const; |
| 229 | int get_current_edited_scene_history_id() const; |
| 230 | int get_scene_history_id(int p_idx) const; |
| 231 | |
| 232 | void set_plugin_window_layout(Ref<ConfigFile> p_layout); |
| 233 | void get_plugin_window_layout(Ref<ConfigFile> p_layout); |
| 234 | |
| 235 | void save_edited_scene_state(EditorSelection *p_selection, EditorSelectionHistory *p_history, const Dictionary &p_custom); |
| 236 | Dictionary restore_edited_scene_state(EditorSelection *p_selection, EditorSelectionHistory *p_history); |
| 237 | void notify_edited_scene_changed(); |
| 238 | void notify_resource_saved(const Ref<Resource> &p_resource); |
| 239 | |
| 240 | bool script_class_is_parent(const String &p_class, const String &p_inherits); |
| 241 | StringName script_class_get_base(const String &p_class) const; |
| 242 | Variant script_class_instance(const String &p_class); |
| 243 | |
| 244 | Ref<Script> script_class_load_script(const String &p_class) const; |
| 245 | |
| 246 | StringName script_class_get_name(const String &p_path) const; |
| 247 | void script_class_set_name(const String &p_path, const StringName &p_class); |
| 248 | |
| 249 | String script_class_get_icon_path(const String &p_class) const; |
| 250 | void script_class_set_icon_path(const String &p_class, const String &p_icon_path); |
| 251 | void script_class_clear_icon_paths() { _script_class_icon_paths.clear(); } |
| 252 | void script_class_save_icon_paths(); |
| 253 | void script_class_load_icon_paths(); |
| 254 | |
| 255 | Ref<Texture2D> extension_class_get_icon(const String &p_class) const; |
| 256 | |
| 257 | Ref<Texture2D> get_script_icon(const Ref<Script> &p_script); |
| 258 | void clear_script_icon_cache(); |
| 259 | |
| 260 | EditorData(); |
| 261 | ~EditorData(); |
| 262 | }; |
| 263 | |
| 264 | /** |
| 265 | * Stores and provides access to the nodes currently selected in the editor. |
| 266 | * |
| 267 | * This provides a central location for storing "selected" nodes, as a selection can be triggered from multiple places, |
| 268 | * such as the SceneTreeDock or a main screen editor plugin (e.g. CanvasItemEditor). |
| 269 | */ |
| 270 | class EditorSelection : public Object { |
| 271 | GDCLASS(EditorSelection, Object); |
| 272 | |
| 273 | // Contains the selected nodes and corresponding metadata. |
| 274 | // Metadata objects come from calling _get_editor_data on the editor_plugins, passing the selected node. |
| 275 | HashMap<Node *, Object *> selection; |
| 276 | |
| 277 | // Tracks whether the selection change signal has been emitted. |
| 278 | // Prevents multiple signals being called in one frame. |
| 279 | bool emitted = false; |
| 280 | |
| 281 | bool changed = false; |
| 282 | bool node_list_changed = false; |
| 283 | |
| 284 | void _node_removed(Node *p_node); |
| 285 | |
| 286 | // Editor plugins which are related to selection. |
| 287 | List<Object *> editor_plugins; |
| 288 | List<Node *> selected_node_list; |
| 289 | |
| 290 | void _update_node_list(); |
| 291 | TypedArray<Node> _get_transformable_selected_nodes(); |
| 292 | void _emit_change(); |
| 293 | |
| 294 | protected: |
| 295 | static void _bind_methods(); |
| 296 | |
| 297 | public: |
| 298 | void add_node(Node *p_node); |
| 299 | void remove_node(Node *p_node); |
| 300 | bool is_selected(Node *p_node) const; |
| 301 | |
| 302 | template <class T> |
| 303 | T *get_node_editor_data(Node *p_node) { |
| 304 | if (!selection.has(p_node)) { |
| 305 | return nullptr; |
| 306 | } |
| 307 | return Object::cast_to<T>(selection[p_node]); |
| 308 | } |
| 309 | |
| 310 | // Adds an editor plugin which can provide metadata for selected nodes. |
| 311 | void add_editor_plugin(Object *p_object); |
| 312 | |
| 313 | void update(); |
| 314 | void clear(); |
| 315 | |
| 316 | // Returns all the selected nodes. |
| 317 | TypedArray<Node> get_selected_nodes(); |
| 318 | // Returns only the top level selected nodes. |
| 319 | // That is, if the selection includes some node and a child of that node, only the parent is returned. |
| 320 | List<Node *> &get_selected_node_list(); |
| 321 | // Returns all the selected nodes (list version of "get_selected_nodes"). |
| 322 | List<Node *> get_full_selected_node_list(); |
| 323 | // Returns the map of selected objects and their metadata. |
| 324 | HashMap<Node *, Object *> &get_selection() { return selection; } |
| 325 | |
| 326 | EditorSelection(); |
| 327 | ~EditorSelection(); |
| 328 | }; |
| 329 | |
| 330 | #endif // EDITOR_DATA_H |
| 331 | |