initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
6
editor/docks/SCsub
Normal file
6
editor/docks/SCsub
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.editor_sources, "*.cpp")
|
1328
editor/docks/editor_dock_manager.cpp
Normal file
1328
editor/docks/editor_dock_manager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
250
editor/docks/editor_dock_manager.h
Normal file
250
editor/docks/editor_dock_manager.h
Normal file
@@ -0,0 +1,250 @@
|
||||
/**************************************************************************/
|
||||
/* editor_dock_manager.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/popup.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
|
||||
class Button;
|
||||
class ConfigFile;
|
||||
class Control;
|
||||
class PopupMenu;
|
||||
class TabBar;
|
||||
class TabContainer;
|
||||
class VBoxContainer;
|
||||
class WindowWrapper;
|
||||
class StyleBoxFlat;
|
||||
|
||||
class DockSplitContainer : public SplitContainer {
|
||||
GDCLASS(DockSplitContainer, SplitContainer);
|
||||
|
||||
private:
|
||||
bool is_updating = false;
|
||||
|
||||
protected:
|
||||
void _update_visibility();
|
||||
|
||||
virtual void add_child_notify(Node *p_child) override;
|
||||
virtual void remove_child_notify(Node *p_child) override;
|
||||
|
||||
public:
|
||||
DockSplitContainer();
|
||||
};
|
||||
|
||||
class DockContextPopup;
|
||||
class EditorDockDragHint;
|
||||
|
||||
class EditorDockManager : public Object {
|
||||
GDCLASS(EditorDockManager, Object);
|
||||
|
||||
public:
|
||||
enum DockSlot {
|
||||
DOCK_SLOT_NONE = -1,
|
||||
DOCK_SLOT_LEFT_UL,
|
||||
DOCK_SLOT_LEFT_BL,
|
||||
DOCK_SLOT_LEFT_UR,
|
||||
DOCK_SLOT_LEFT_BR,
|
||||
DOCK_SLOT_RIGHT_UL,
|
||||
DOCK_SLOT_RIGHT_BL,
|
||||
DOCK_SLOT_RIGHT_UR,
|
||||
DOCK_SLOT_RIGHT_BR,
|
||||
DOCK_SLOT_MAX
|
||||
};
|
||||
|
||||
private:
|
||||
friend class DockContextPopup;
|
||||
friend class EditorDockDragHint;
|
||||
|
||||
struct DockInfo {
|
||||
String title;
|
||||
bool open = false;
|
||||
bool enabled = true;
|
||||
bool at_bottom = false;
|
||||
int previous_tab_index = -1;
|
||||
bool previous_at_bottom = false;
|
||||
WindowWrapper *dock_window = nullptr;
|
||||
int dock_slot_index = DOCK_SLOT_NONE;
|
||||
Ref<Shortcut> shortcut;
|
||||
Ref<Texture2D> icon; // Only used when `icon_name` is empty.
|
||||
StringName icon_name;
|
||||
};
|
||||
|
||||
static EditorDockManager *singleton;
|
||||
|
||||
// To access splits easily by index.
|
||||
Vector<DockSplitContainer *> vsplits;
|
||||
Vector<DockSplitContainer *> hsplits;
|
||||
|
||||
Vector<WindowWrapper *> dock_windows;
|
||||
TabContainer *dock_slot[DOCK_SLOT_MAX];
|
||||
EditorDockDragHint *dock_drag_rects[DOCK_SLOT_MAX];
|
||||
HashMap<Control *, DockInfo> all_docks;
|
||||
Control *dock_tab_dragged = nullptr;
|
||||
bool docks_visible = true;
|
||||
|
||||
DockContextPopup *dock_context_popup = nullptr;
|
||||
PopupMenu *docks_menu = nullptr;
|
||||
Vector<Control *> docks_menu_docks;
|
||||
Control *closed_dock_parent = nullptr;
|
||||
|
||||
Control *_get_dock_tab_dragged();
|
||||
void _dock_drag_stopped();
|
||||
void _dock_split_dragged(int p_offset);
|
||||
void _dock_container_gui_input(const Ref<InputEvent> &p_input, TabContainer *p_dock_container);
|
||||
void _bottom_dock_button_gui_input(const Ref<InputEvent> &p_input, Control *p_dock, Button *p_bottom_button);
|
||||
void _dock_container_update_visibility(TabContainer *p_dock_container);
|
||||
void _update_layout();
|
||||
|
||||
void _docks_menu_option(int p_id);
|
||||
|
||||
void _window_close_request(WindowWrapper *p_wrapper);
|
||||
Control *_close_window(WindowWrapper *p_wrapper);
|
||||
void _open_dock_in_window(Control *p_dock, bool p_show_window = true, bool p_reset_size = false);
|
||||
void _restore_dock_to_saved_window(Control *p_dock, const Dictionary &p_window_dump);
|
||||
|
||||
void _dock_move_to_bottom(Control *p_dock, bool p_visible);
|
||||
void _dock_remove_from_bottom(Control *p_dock);
|
||||
bool _is_dock_at_bottom(Control *p_dock);
|
||||
|
||||
void _move_dock_tab_index(Control *p_dock, int p_tab_index, bool p_set_current);
|
||||
void _move_dock(Control *p_dock, Control *p_target, int p_tab_index = -1, bool p_set_current = true);
|
||||
|
||||
void _update_tab_style(Control *p_dock);
|
||||
|
||||
public:
|
||||
static EditorDockManager *get_singleton() { return singleton; }
|
||||
|
||||
void update_docks_menu();
|
||||
void update_tab_styles();
|
||||
void set_tab_icon_max_width(int p_max_width);
|
||||
|
||||
void add_vsplit(DockSplitContainer *p_split);
|
||||
void add_hsplit(DockSplitContainer *p_split);
|
||||
void register_dock_slot(DockSlot p_dock_slot, TabContainer *p_tab_container);
|
||||
int get_vsplit_count() const;
|
||||
PopupMenu *get_docks_menu();
|
||||
|
||||
void save_docks_to_config(Ref<ConfigFile> p_layout, const String &p_section) const;
|
||||
void load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section, bool p_first_load = false);
|
||||
|
||||
void set_dock_enabled(Control *p_dock, bool p_enabled);
|
||||
void close_dock(Control *p_dock);
|
||||
void open_dock(Control *p_dock, bool p_set_current = true);
|
||||
void focus_dock(Control *p_dock);
|
||||
|
||||
TabContainer *get_dock_tab_container(Control *p_dock) const;
|
||||
|
||||
void bottom_dock_show_placement_popup(const Rect2i &p_position, Control *p_dock);
|
||||
|
||||
void set_docks_visible(bool p_show);
|
||||
bool are_docks_visible() const;
|
||||
|
||||
void add_dock(Control *p_dock, const String &p_title = "", DockSlot p_slot = DOCK_SLOT_NONE, const Ref<Shortcut> &p_shortcut = nullptr, const StringName &p_icon_name = StringName());
|
||||
void remove_dock(Control *p_dock);
|
||||
|
||||
void set_dock_tab_icon(Control *p_dock, const Ref<Texture2D> &p_icon);
|
||||
|
||||
EditorDockManager();
|
||||
};
|
||||
|
||||
class EditorDockDragHint : public Control {
|
||||
GDCLASS(EditorDockDragHint, Control);
|
||||
|
||||
private:
|
||||
EditorDockManager *dock_manager = nullptr;
|
||||
EditorDockManager::DockSlot occupied_slot = EditorDockManager::DOCK_SLOT_MAX;
|
||||
TabBar *drop_tabbar = nullptr;
|
||||
|
||||
Color valid_drop_color;
|
||||
Ref<StyleBoxFlat> dock_drop_highlight;
|
||||
bool can_drop_dock = false;
|
||||
bool mouse_inside = false;
|
||||
bool mouse_inside_tabbar = false;
|
||||
|
||||
void _drag_move_tab(int p_from_index, int p_to_index);
|
||||
void _drag_move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index);
|
||||
|
||||
protected:
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
void _notification(int p_what);
|
||||
bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
|
||||
void drop_data(const Point2 &p_point, const Variant &p_data) override;
|
||||
|
||||
public:
|
||||
void set_slot(EditorDockManager::DockSlot p_slot);
|
||||
|
||||
EditorDockDragHint();
|
||||
};
|
||||
|
||||
class DockContextPopup : public PopupPanel {
|
||||
GDCLASS(DockContextPopup, PopupPanel);
|
||||
|
||||
private:
|
||||
VBoxContainer *dock_select_popup_vb = nullptr;
|
||||
|
||||
Button *make_float_button = nullptr;
|
||||
Button *tab_move_left_button = nullptr;
|
||||
Button *tab_move_right_button = nullptr;
|
||||
Button *close_button = nullptr;
|
||||
Button *dock_to_bottom_button = nullptr;
|
||||
|
||||
Control *dock_select = nullptr;
|
||||
Rect2 dock_select_rects[EditorDockManager::DOCK_SLOT_MAX];
|
||||
int dock_select_rect_over_idx = -1;
|
||||
|
||||
Control *context_dock = nullptr;
|
||||
|
||||
EditorDockManager *dock_manager = nullptr;
|
||||
|
||||
void _tab_move_left();
|
||||
void _tab_move_right();
|
||||
void _close_dock();
|
||||
void _float_dock();
|
||||
void _move_dock_to_bottom();
|
||||
|
||||
void _dock_select_input(const Ref<InputEvent> &p_input);
|
||||
void _dock_select_mouse_exited();
|
||||
void _dock_select_draw();
|
||||
|
||||
void _update_buttons();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void select_current_dock_in_dock_slot(int p_dock_slot);
|
||||
void set_dock(Control *p_dock);
|
||||
Control *get_dock() const;
|
||||
void docks_updated();
|
||||
|
||||
DockContextPopup();
|
||||
};
|
4415
editor/docks/filesystem_dock.cpp
Normal file
4415
editor/docks/filesystem_dock.cpp
Normal file
File diff suppressed because it is too large
Load Diff
433
editor/docks/filesystem_dock.h
Normal file
433
editor/docks/filesystem_dock.h
Normal file
@@ -0,0 +1,433 @@
|
||||
/**************************************************************************/
|
||||
/* filesystem_dock.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/file_system/dependency_editor.h"
|
||||
#include "editor/file_system/editor_file_system.h"
|
||||
#include "editor/file_system/file_info.h"
|
||||
#include "editor/script/script_create_dialog.h"
|
||||
#include "editor/script/script_editor_plugin.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
class CreateDialog;
|
||||
class EditorDirDialog;
|
||||
class ItemList;
|
||||
class LineEdit;
|
||||
class ProgressBar;
|
||||
class SceneCreateDialog;
|
||||
class ShaderCreateDialog;
|
||||
class DirectoryCreateDialog;
|
||||
class EditorResourceTooltipPlugin;
|
||||
|
||||
class FileSystemTree : public Tree {
|
||||
virtual Control *make_custom_tooltip(const String &p_text) const;
|
||||
};
|
||||
|
||||
class FileSystemList : public ItemList {
|
||||
GDCLASS(FileSystemList, ItemList);
|
||||
|
||||
bool popup_edit_committed = true;
|
||||
VBoxContainer *popup_editor_vb = nullptr;
|
||||
Popup *popup_editor = nullptr;
|
||||
LineEdit *line_editor = nullptr;
|
||||
|
||||
virtual Control *make_custom_tooltip(const String &p_text) const override;
|
||||
void _line_editor_submit(const String &p_text);
|
||||
void _text_editor_popup_modal_close();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
bool edit_selected();
|
||||
String get_edit_text();
|
||||
|
||||
FileSystemList();
|
||||
};
|
||||
|
||||
class FileSystemDock : public VBoxContainer {
|
||||
GDCLASS(FileSystemDock, VBoxContainer);
|
||||
|
||||
public:
|
||||
enum FileListDisplayMode {
|
||||
FILE_LIST_DISPLAY_THUMBNAILS,
|
||||
FILE_LIST_DISPLAY_LIST
|
||||
};
|
||||
|
||||
enum DisplayMode {
|
||||
DISPLAY_MODE_TREE_ONLY,
|
||||
DISPLAY_MODE_VSPLIT,
|
||||
DISPLAY_MODE_HSPLIT,
|
||||
};
|
||||
|
||||
enum Overwrite {
|
||||
OVERWRITE_UNDECIDED,
|
||||
OVERWRITE_REPLACE,
|
||||
OVERWRITE_RENAME,
|
||||
};
|
||||
|
||||
private:
|
||||
enum FileMenu {
|
||||
FILE_MENU_OPEN,
|
||||
FILE_MENU_INHERIT,
|
||||
FILE_MENU_MAIN_SCENE,
|
||||
FILE_MENU_INSTANTIATE,
|
||||
FILE_MENU_ADD_FAVORITE,
|
||||
FILE_MENU_REMOVE_FAVORITE,
|
||||
FILE_MENU_SHOW_IN_FILESYSTEM,
|
||||
FILE_MENU_DEPENDENCIES,
|
||||
FILE_MENU_OWNERS,
|
||||
FILE_MENU_MOVE,
|
||||
FILE_MENU_RENAME,
|
||||
FILE_MENU_REMOVE,
|
||||
FILE_MENU_DUPLICATE,
|
||||
FILE_MENU_REIMPORT,
|
||||
FILE_MENU_NEW,
|
||||
FILE_MENU_SHOW_IN_EXPLORER,
|
||||
FILE_MENU_OPEN_EXTERNAL,
|
||||
FILE_MENU_OPEN_IN_TERMINAL,
|
||||
FILE_MENU_COPY_PATH,
|
||||
FILE_MENU_COPY_ABSOLUTE_PATH,
|
||||
FILE_MENU_COPY_UID,
|
||||
FILE_MENU_EXPAND_ALL,
|
||||
FILE_MENU_COLLAPSE_ALL,
|
||||
FILE_MENU_NEW_RESOURCE,
|
||||
FILE_MENU_NEW_TEXTFILE,
|
||||
FILE_MENU_NEW_FOLDER,
|
||||
FILE_MENU_NEW_SCRIPT,
|
||||
FILE_MENU_NEW_SCENE,
|
||||
FILE_MENU_RUN_SCRIPT,
|
||||
FILE_MENU_MAX,
|
||||
// Extra shortcuts that don't exist in the menu.
|
||||
EXTRA_FOCUS_PATH,
|
||||
EXTRA_FOCUS_FILTER,
|
||||
|
||||
CONVERT_BASE_ID = 1000,
|
||||
};
|
||||
|
||||
HashMap<String, TreeItem *> folder_map;
|
||||
HashMap<String, Color> folder_colors;
|
||||
Dictionary assigned_folder_colors;
|
||||
|
||||
FileSortOption file_sort = FileSortOption::FILE_SORT_NAME;
|
||||
|
||||
VBoxContainer *scanning_vb = nullptr;
|
||||
ProgressBar *scanning_progress = nullptr;
|
||||
SplitContainer *split_box = nullptr;
|
||||
VBoxContainer *file_list_vb = nullptr;
|
||||
|
||||
int split_box_offset_h = 0;
|
||||
int split_box_offset_v = 0;
|
||||
|
||||
HashSet<String> favorites;
|
||||
|
||||
Button *button_dock_placement = nullptr;
|
||||
|
||||
Button *button_toggle_display_mode = nullptr;
|
||||
Button *button_file_list_display_mode = nullptr;
|
||||
Button *button_hist_next = nullptr;
|
||||
Button *button_hist_prev = nullptr;
|
||||
LineEdit *current_path_line_edit = nullptr;
|
||||
|
||||
HBoxContainer *toolbar2_hbc = nullptr;
|
||||
LineEdit *tree_search_box = nullptr;
|
||||
MenuButton *tree_button_sort = nullptr;
|
||||
|
||||
LineEdit *file_list_search_box = nullptr;
|
||||
MenuButton *file_list_button_sort = nullptr;
|
||||
|
||||
PackedStringArray searched_tokens;
|
||||
Vector<String> uncollapsed_paths_before_search;
|
||||
|
||||
TextureRect *search_icon = nullptr;
|
||||
HBoxContainer *path_hb = nullptr;
|
||||
|
||||
FileListDisplayMode file_list_display_mode;
|
||||
DisplayMode display_mode;
|
||||
DisplayMode old_display_mode;
|
||||
|
||||
PopupMenu *file_list_popup = nullptr;
|
||||
PopupMenu *tree_popup = nullptr;
|
||||
|
||||
DependencyEditor *deps_editor = nullptr;
|
||||
DependencyEditorOwners *owners_editor = nullptr;
|
||||
DependencyRemoveDialog *remove_dialog = nullptr;
|
||||
|
||||
EditorDirDialog *move_dialog = nullptr;
|
||||
DirectoryCreateDialog *make_dir_dialog = nullptr;
|
||||
|
||||
ConfirmationDialog *overwrite_dialog = nullptr;
|
||||
ScrollContainer *overwrite_dialog_scroll = nullptr;
|
||||
Label *overwrite_dialog_header = nullptr;
|
||||
Label *overwrite_dialog_footer = nullptr;
|
||||
Label *overwrite_dialog_file_list = nullptr;
|
||||
|
||||
ConfirmationDialog *conversion_dialog = nullptr;
|
||||
|
||||
SceneCreateDialog *make_scene_dialog = nullptr;
|
||||
ScriptCreateDialog *make_script_dialog = nullptr;
|
||||
ShaderCreateDialog *make_shader_dialog = nullptr;
|
||||
CreateDialog *new_resource_dialog = nullptr;
|
||||
|
||||
bool always_show_folders = false;
|
||||
int thumbnail_size_setting = 0;
|
||||
|
||||
bool editor_is_dark_theme = false;
|
||||
|
||||
class FileOrFolder {
|
||||
public:
|
||||
String path;
|
||||
bool is_file = false;
|
||||
|
||||
FileOrFolder() {}
|
||||
FileOrFolder(const String &p_path, bool p_is_file) :
|
||||
path(p_path),
|
||||
is_file(p_is_file) {}
|
||||
};
|
||||
FileOrFolder to_rename;
|
||||
FileOrFolder to_duplicate;
|
||||
Vector<FileOrFolder> to_move;
|
||||
String to_move_path;
|
||||
bool to_move_or_copy = false;
|
||||
|
||||
Vector<String> to_convert;
|
||||
int selected_conversion_id = 0;
|
||||
|
||||
Vector<String> history;
|
||||
int history_pos;
|
||||
int history_max_size;
|
||||
|
||||
String current_path;
|
||||
String select_after_scan;
|
||||
|
||||
bool updating_tree = false;
|
||||
int tree_update_id;
|
||||
FileSystemTree *tree = nullptr;
|
||||
FileSystemList *files = nullptr;
|
||||
bool import_dock_needs_update = false;
|
||||
TreeItem *resources_item = nullptr;
|
||||
TreeItem *favorites_item = nullptr;
|
||||
Control *had_focus = nullptr;
|
||||
|
||||
bool holding_branch = false;
|
||||
Vector<TreeItem *> tree_items_selected_on_drag_begin;
|
||||
PackedInt32Array list_items_selected_on_drag_begin;
|
||||
|
||||
LocalVector<Ref<EditorResourceTooltipPlugin>> tooltip_plugins;
|
||||
|
||||
HashSet<String> cached_valid_conversion_targets;
|
||||
|
||||
void _tree_mouse_exited();
|
||||
void _reselect_items_selected_on_drag_begin(bool reset = false);
|
||||
|
||||
Ref<Texture2D> _get_tree_item_icon(bool p_is_valid, const String &p_file_type, const String &p_icon_path);
|
||||
void _create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path = false);
|
||||
void _update_tree(const Vector<String> &p_uncollapsed_paths = Vector<String>(), bool p_uncollapse_root = false, bool p_scroll_to_selected = true);
|
||||
void _navigate_to_path(const String &p_path, bool p_select_in_favorites = false, bool p_grab_focus = false);
|
||||
bool _update_filtered_items(TreeItem *p_tree_item = nullptr);
|
||||
|
||||
void _file_list_gui_input(Ref<InputEvent> p_event);
|
||||
void _tree_gui_input(Ref<InputEvent> p_event);
|
||||
|
||||
HashSet<String> _get_valid_conversions_for_file_paths(const Vector<String> &p_paths);
|
||||
|
||||
void _update_file_list(bool p_keep_selection);
|
||||
void _toggle_file_display();
|
||||
void _set_file_display(bool p_active);
|
||||
void _fs_changed();
|
||||
|
||||
void _select_file(const String &p_path, bool p_select_in_favorites = false, bool p_navigate = true);
|
||||
void _tree_activate_file();
|
||||
void _file_list_activate_file(int p_idx);
|
||||
void _file_multi_selected(int p_index, bool p_selected);
|
||||
void _tree_multi_selected(Object *p_item, int p_column, bool p_selected);
|
||||
|
||||
bool _get_imported_files(const String &p_path, String &r_extension, Vector<String> &r_files) const;
|
||||
void _update_import_dock();
|
||||
|
||||
void _get_all_items_in_dir(EditorFileSystemDirectory *p_efsd, Vector<String> &r_files, Vector<String> &r_folders) const;
|
||||
void _find_file_owners(EditorFileSystemDirectory *p_efsd, const HashSet<String> &p_renames, HashSet<String> &r_file_owners) const;
|
||||
void _try_move_item(const FileOrFolder &p_item, const String &p_new_path, HashMap<String, String> &p_file_renames, HashMap<String, String> &p_folder_renames);
|
||||
void _try_duplicate_item(const FileOrFolder &p_item, const String &p_new_path) const;
|
||||
void _before_move(HashMap<String, ResourceUID::ID> &r_uids, HashSet<String> &r_file_owners) const;
|
||||
void _update_dependencies_after_move(const HashMap<String, String> &p_renames, const HashSet<String> &p_file_owners) const;
|
||||
void _update_resource_paths_after_move(const HashMap<String, String> &p_renames, const HashMap<String, ResourceUID::ID> &p_uids) const;
|
||||
void _update_favorites_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const;
|
||||
void _update_project_settings_after_move(const HashMap<String, String> &p_renames, const HashMap<String, String> &p_folders_renames);
|
||||
String _get_unique_name(const FileOrFolder &p_entry, const String &p_at_path);
|
||||
|
||||
void _update_folder_colors_setting();
|
||||
|
||||
void _resource_removed(const Ref<Resource> &p_resource);
|
||||
void _file_removed(const String &p_file);
|
||||
void _folder_removed(const String &p_folder);
|
||||
|
||||
void _resource_created();
|
||||
void _make_scene_confirm();
|
||||
void _rename_operation_confirm();
|
||||
void _duplicate_operation_confirm(const String &p_path);
|
||||
void _overwrite_dialog_action(bool p_overwrite);
|
||||
void _convert_dialog_action();
|
||||
Vector<String> _check_existing();
|
||||
void _move_operation_confirm(const String &p_to_path, bool p_copy = false, Overwrite p_overwrite = OVERWRITE_UNDECIDED);
|
||||
|
||||
void _tree_rmb_option(int p_option);
|
||||
void _file_list_rmb_option(int p_option);
|
||||
void _generic_rmb_option_selected(int p_option);
|
||||
void _file_option(int p_option, const Vector<String> &p_selected);
|
||||
int _get_menu_option_from_key(const Ref<InputEventKey> &p_key);
|
||||
|
||||
void _fw_history();
|
||||
void _bw_history();
|
||||
void _update_history();
|
||||
void _push_to_history();
|
||||
|
||||
void _set_scanning_mode();
|
||||
void _rescan();
|
||||
|
||||
void _change_split_mode();
|
||||
void _split_dragged(int p_offset);
|
||||
|
||||
void _search_changed(const String &p_text, const Control *p_from);
|
||||
bool _matches_all_search_tokens(const String &p_text);
|
||||
|
||||
MenuButton *_create_file_menu_button();
|
||||
void _file_sort_popup(int p_id);
|
||||
|
||||
void _folder_color_index_pressed(int p_index, PopupMenu *p_menu);
|
||||
void _file_and_folders_fill_popup(PopupMenu *p_popup, const Vector<String> &p_paths, bool p_display_path_dependent_options = true);
|
||||
void _tree_rmb_select(const Vector2 &p_pos, MouseButton p_button);
|
||||
void _file_list_item_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
|
||||
void _file_list_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index);
|
||||
void _tree_empty_click(const Vector2 &p_pos, MouseButton p_button);
|
||||
void _tree_empty_selected();
|
||||
|
||||
void _search(EditorFileSystemDirectory *p_path, List<FileInfo> *matches, int p_max_items);
|
||||
|
||||
void _set_current_path_line_edit_text(const String &p_path);
|
||||
|
||||
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
|
||||
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
|
||||
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
|
||||
void _get_drag_target_folder(String &target, bool &target_favorites, const Point2 &p_point, Control *p_from) const;
|
||||
|
||||
void _preview_invalidated(const String &p_path);
|
||||
void _file_list_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata);
|
||||
void _tree_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, const Variant &p_udata);
|
||||
|
||||
void _update_display_mode(bool p_force = false);
|
||||
|
||||
Vector<String> _tree_get_selected(bool remove_self_inclusion = true, bool p_include_unselected_cursor = false) const;
|
||||
Vector<String> _file_list_get_selected() const;
|
||||
|
||||
bool _is_file_type_disabled_by_feature_profile(const StringName &p_class);
|
||||
|
||||
void _feature_profile_changed();
|
||||
void _project_settings_changed();
|
||||
static Vector<String> _remove_self_included_paths(Vector<String> selected_strings);
|
||||
|
||||
void _change_bottom_dock_placement();
|
||||
|
||||
bool _can_dock_horizontal() const;
|
||||
void _set_dock_horizontal(bool p_enable);
|
||||
|
||||
void _save_layout_to_config(Ref<ConfigFile> p_layout, const String &p_section) const;
|
||||
void _load_layout_from_config(Ref<ConfigFile> p_layout, const String &p_section);
|
||||
|
||||
private:
|
||||
inline static FileSystemDock *singleton = nullptr;
|
||||
|
||||
public:
|
||||
static FileSystemDock *get_singleton() { return singleton; }
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static constexpr double ITEM_COLOR_SCALE = 1.75;
|
||||
static constexpr double ITEM_ALPHA_MIN = 0.1;
|
||||
static constexpr double ITEM_ALPHA_MAX = 0.15;
|
||||
static constexpr double ITEM_BG_DARK_SCALE = 0.3;
|
||||
|
||||
static Color get_dir_icon_color(const String &p_dir_path, const Color &p_default);
|
||||
|
||||
const HashMap<String, Color> &get_folder_colors() const;
|
||||
Dictionary get_assigned_folder_colors() const;
|
||||
|
||||
Vector<String> get_selected_paths() const;
|
||||
Vector<String> get_uncollapsed_paths() const;
|
||||
|
||||
String get_current_path() const;
|
||||
String get_current_directory() const;
|
||||
String get_folder_path_at_mouse_position() const;
|
||||
|
||||
void navigate_to_path(const String &p_path);
|
||||
void focus_on_path();
|
||||
void focus_on_filter();
|
||||
void create_directory(const String &p_path, const String &p_base_dir);
|
||||
|
||||
ScriptCreateDialog *get_script_create_dialog() const;
|
||||
|
||||
void fix_dependencies(const String &p_for_file);
|
||||
void update_all();
|
||||
|
||||
int get_h_split_offset() const { return split_box_offset_h; }
|
||||
void set_h_split_offset(int p_offset) { split_box_offset_h = p_offset; }
|
||||
int get_v_split_offset() const { return split_box_offset_v; }
|
||||
void set_v_split_offset(int p_offset) { split_box_offset_v = p_offset; }
|
||||
void select_file(const String &p_file);
|
||||
|
||||
void set_display_mode(DisplayMode p_display_mode);
|
||||
DisplayMode get_display_mode() const { return display_mode; }
|
||||
|
||||
void set_file_sort(FileSortOption p_file_sort);
|
||||
FileSortOption get_file_sort() const { return file_sort; }
|
||||
|
||||
void set_file_list_display_mode(FileListDisplayMode p_mode);
|
||||
FileListDisplayMode get_file_list_display_mode() const { return file_list_display_mode; }
|
||||
|
||||
Tree *get_tree_control() { return tree; }
|
||||
|
||||
void add_resource_tooltip_plugin(const Ref<EditorResourceTooltipPlugin> &p_plugin);
|
||||
void remove_resource_tooltip_plugin(const Ref<EditorResourceTooltipPlugin> &p_plugin);
|
||||
Control *create_tooltip_for_path(const String &p_path) const;
|
||||
|
||||
FileSystemDock();
|
||||
~FileSystemDock();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(FileSystemDock::Overwrite);
|
875
editor/docks/groups_editor.cpp
Normal file
875
editor/docks/groups_editor.cpp
Normal file
@@ -0,0 +1,875 @@
|
||||
/**************************************************************************/
|
||||
/* groups_editor.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "groups_editor.h"
|
||||
|
||||
#include "editor/docks/scene_tree_dock.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/gui/editor_validation_panel.h"
|
||||
#include "editor/settings/editor_settings.h"
|
||||
#include "editor/settings/project_settings_editor.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/check_button.h"
|
||||
#include "scene/gui/grid_container.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
||||
static bool can_edit(Node *p_node, const String &p_group) {
|
||||
Node *n = p_node;
|
||||
bool can_edit = true;
|
||||
while (n) {
|
||||
Ref<SceneState> ss = (n == EditorNode::get_singleton()->get_edited_scene()) ? n->get_scene_inherited_state() : n->get_scene_instance_state();
|
||||
if (ss.is_valid()) {
|
||||
int path = ss->find_node_by_path(n->get_path_to(p_node));
|
||||
if (path != -1) {
|
||||
if (ss->is_node_in_group(path, p_group)) {
|
||||
can_edit = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
n = n->get_owner();
|
||||
}
|
||||
return can_edit;
|
||||
}
|
||||
|
||||
struct _GroupInfoComparator {
|
||||
bool operator()(const Node::GroupInfo &p_a, const Node::GroupInfo &p_b) const {
|
||||
return p_a.name.operator String() < p_b.name.operator String();
|
||||
}
|
||||
};
|
||||
|
||||
void GroupsEditor::_add_scene_group(const String &p_name) {
|
||||
scene_groups[p_name] = true;
|
||||
}
|
||||
|
||||
void GroupsEditor::_remove_scene_group(const String &p_name) {
|
||||
scene_groups.erase(p_name);
|
||||
ProjectSettingsEditor::get_singleton()->get_group_settings()->remove_node_references(scene_root_node, p_name);
|
||||
}
|
||||
|
||||
void GroupsEditor::_rename_scene_group(const String &p_old_name, const String &p_new_name) {
|
||||
scene_groups[p_new_name] = scene_groups[p_old_name];
|
||||
scene_groups.erase(p_old_name);
|
||||
ProjectSettingsEditor::get_singleton()->get_group_settings()->rename_node_references(scene_root_node, p_old_name, p_new_name);
|
||||
}
|
||||
|
||||
void GroupsEditor::_set_group_checked(const String &p_name, bool p_checked) {
|
||||
TreeItem *ti = tree->get_item_with_text(p_name);
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
|
||||
ti->set_checked(0, p_checked);
|
||||
}
|
||||
|
||||
bool GroupsEditor::_has_group(const String &p_name) {
|
||||
return global_groups.has(p_name) || scene_groups.has(p_name);
|
||||
}
|
||||
|
||||
void GroupsEditor::_modify_group(Object *p_item, int p_column, int p_id, MouseButton p_mouse_button) {
|
||||
if (p_mouse_button != MouseButton::LEFT) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
TreeItem *ti = Object::cast_to<TreeItem>(p_item);
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_id == COPY_GROUP) {
|
||||
DisplayServer::get_singleton()->clipboard_set(ti->get_text(p_column));
|
||||
}
|
||||
}
|
||||
|
||||
void GroupsEditor::_load_scene_groups(Node *p_node) {
|
||||
List<Node::GroupInfo> groups;
|
||||
p_node->get_groups(&groups);
|
||||
|
||||
for (const GroupInfo &gi : groups) {
|
||||
if (!gi.persistent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (global_groups.has(gi.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool is_editable = can_edit(p_node, gi.name);
|
||||
if (scene_groups.has(gi.name)) {
|
||||
scene_groups[gi.name] = scene_groups[gi.name] && is_editable;
|
||||
} else {
|
||||
scene_groups[gi.name] = is_editable;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < p_node->get_child_count(); i++) {
|
||||
_load_scene_groups(p_node->get_child(i));
|
||||
}
|
||||
}
|
||||
|
||||
void GroupsEditor::_update_groups() {
|
||||
if (!is_visible_in_tree()) {
|
||||
groups_dirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (updating_groups) {
|
||||
return;
|
||||
}
|
||||
|
||||
updating_groups = true;
|
||||
|
||||
global_groups = ProjectSettings::get_singleton()->get_global_groups_list();
|
||||
|
||||
_load_scene_groups(scene_root_node);
|
||||
|
||||
for (HashMap<StringName, bool>::Iterator E = scene_groups.begin(); E;) {
|
||||
HashMap<StringName, bool>::Iterator next = E;
|
||||
++next;
|
||||
|
||||
if (global_groups.has(E->key)) {
|
||||
scene_groups.erase(E->key);
|
||||
}
|
||||
E = next;
|
||||
}
|
||||
|
||||
updating_groups = false;
|
||||
}
|
||||
|
||||
void GroupsEditor::_update_tree() {
|
||||
if (!is_visible_in_tree()) {
|
||||
groups_dirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (updating_tree) {
|
||||
return;
|
||||
}
|
||||
|
||||
updating_tree = true;
|
||||
|
||||
tree->clear();
|
||||
|
||||
List<Node::GroupInfo> groups;
|
||||
node->get_groups(&groups);
|
||||
groups.sort_custom<_GroupInfoComparator>();
|
||||
|
||||
List<StringName> current_groups;
|
||||
for (const Node::GroupInfo &gi : groups) {
|
||||
current_groups.push_back(gi.name);
|
||||
}
|
||||
|
||||
TreeItem *root = tree->create_item();
|
||||
|
||||
TreeItem *local_root = tree->create_item(root);
|
||||
local_root->set_text(0, TTR("Scene Groups"));
|
||||
local_root->set_icon(0, get_editor_theme_icon(SNAME("PackedScene")));
|
||||
local_root->set_custom_bg_color(0, get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));
|
||||
local_root->set_selectable(0, false);
|
||||
|
||||
List<StringName> scene_keys;
|
||||
for (const KeyValue<StringName, bool> &E : scene_groups) {
|
||||
scene_keys.push_back(E.key);
|
||||
}
|
||||
scene_keys.sort_custom<NoCaseComparator>();
|
||||
|
||||
for (const StringName &E : scene_keys) {
|
||||
if (!filter->get_text().is_subsequence_ofn(E)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TreeItem *item = tree->create_item(local_root);
|
||||
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
|
||||
item->set_editable(0, can_edit(node, E));
|
||||
item->set_checked(0, current_groups.find(E) != nullptr);
|
||||
item->set_text(0, E);
|
||||
item->set_meta("__local", true);
|
||||
item->set_meta("__name", E);
|
||||
item->set_meta("__description", "");
|
||||
if (!scene_groups[E]) {
|
||||
item->add_button(0, get_editor_theme_icon(SNAME("Lock")), -1, true, TTR("This group belongs to another scene and can't be edited."));
|
||||
}
|
||||
item->add_button(0, get_editor_theme_icon(SNAME("ActionCopy")), COPY_GROUP, false, TTR("Copy group name to clipboard."));
|
||||
}
|
||||
|
||||
List<StringName> keys;
|
||||
for (const KeyValue<StringName, String> &E : global_groups) {
|
||||
keys.push_back(E.key);
|
||||
}
|
||||
keys.sort_custom<NoCaseComparator>();
|
||||
|
||||
TreeItem *global_root = tree->create_item(root);
|
||||
global_root->set_text(0, TTR("Global Groups"));
|
||||
global_root->set_icon(0, get_editor_theme_icon(SNAME("Environment")));
|
||||
global_root->set_custom_bg_color(0, get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));
|
||||
global_root->set_selectable(0, false);
|
||||
|
||||
for (const StringName &E : keys) {
|
||||
if (!filter->get_text().is_subsequence_ofn(E)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TreeItem *item = tree->create_item(global_root);
|
||||
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
|
||||
item->set_editable(0, can_edit(node, E));
|
||||
item->set_checked(0, current_groups.find(E) != nullptr);
|
||||
item->set_text(0, E);
|
||||
item->set_meta("__local", false);
|
||||
item->set_meta("__name", E);
|
||||
item->set_meta("__description", global_groups[E]);
|
||||
if (!global_groups[E].is_empty()) {
|
||||
item->set_tooltip_text(0, vformat("%s\n\n%s", E, global_groups[E]));
|
||||
}
|
||||
item->add_button(0, get_editor_theme_icon(SNAME("ActionCopy")), COPY_GROUP, false, TTR("Copy group name to clipboard."));
|
||||
}
|
||||
|
||||
updating_tree = false;
|
||||
}
|
||||
|
||||
void GroupsEditor::_queue_update_groups_and_tree() {
|
||||
if (update_groups_and_tree_queued) {
|
||||
return;
|
||||
}
|
||||
update_groups_and_tree_queued = true;
|
||||
callable_mp(this, &GroupsEditor::_update_groups_and_tree).call_deferred();
|
||||
}
|
||||
|
||||
void GroupsEditor::_update_groups_and_tree() {
|
||||
update_groups_and_tree_queued = false;
|
||||
// The scene_root_node could be unset before we actually run this code because this is queued with call_deferred().
|
||||
// In that case NOTIFICATION_VISIBILITY_CHANGED will call this function again soon.
|
||||
if (!scene_root_node) {
|
||||
return;
|
||||
}
|
||||
_update_groups();
|
||||
_update_tree();
|
||||
}
|
||||
|
||||
void GroupsEditor::_update_scene_groups(const ObjectID &p_id) {
|
||||
HashMap<ObjectID, HashMap<StringName, bool>>::Iterator I = scene_groups_cache.find(p_id);
|
||||
if (I) {
|
||||
scene_groups = I->value;
|
||||
scene_groups_cache.remove(I);
|
||||
} else {
|
||||
scene_groups = HashMap<StringName, bool>();
|
||||
}
|
||||
}
|
||||
|
||||
void GroupsEditor::_cache_scene_groups(const ObjectID &p_id) {
|
||||
const int edited_scene_count = EditorNode::get_editor_data().get_edited_scene_count();
|
||||
for (int i = 0; i < edited_scene_count; i++) {
|
||||
Node *edited_scene_root = EditorNode::get_editor_data().get_edited_scene_root(i);
|
||||
if (edited_scene_root && p_id == edited_scene_root->get_instance_id()) {
|
||||
scene_groups_cache[p_id] = scene_groups_for_caching;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GroupsEditor::set_current(Node *p_node) {
|
||||
if (node == p_node) {
|
||||
return;
|
||||
}
|
||||
node = p_node;
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (scene_tree->get_edited_scene_root() != scene_root_node) {
|
||||
scene_root_node = scene_tree->get_edited_scene_root();
|
||||
_update_scene_groups(scene_root_node->get_instance_id());
|
||||
_update_groups();
|
||||
}
|
||||
|
||||
_update_tree();
|
||||
}
|
||||
|
||||
void GroupsEditor::_item_edited() {
|
||||
TreeItem *ti = tree->get_edited();
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
|
||||
String name = ti->get_text(0);
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
if (ti->is_checked(0)) {
|
||||
undo_redo->create_action(TTR("Add to Group"));
|
||||
|
||||
undo_redo->add_do_method(node, "add_to_group", name, true);
|
||||
undo_redo->add_undo_method(node, "remove_from_group", name);
|
||||
|
||||
undo_redo->add_do_method(this, "_set_group_checked", name, true);
|
||||
undo_redo->add_undo_method(this, "_set_group_checked", name, false);
|
||||
|
||||
// To force redraw of scene tree.
|
||||
undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
|
||||
undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
|
||||
|
||||
undo_redo->commit_action();
|
||||
|
||||
} else {
|
||||
undo_redo->create_action(TTR("Remove from Group"));
|
||||
|
||||
undo_redo->add_do_method(node, "remove_from_group", name);
|
||||
undo_redo->add_undo_method(node, "add_to_group", name, true);
|
||||
|
||||
undo_redo->add_do_method(this, "_set_group_checked", name, false);
|
||||
undo_redo->add_undo_method(this, "_set_group_checked", name, true);
|
||||
|
||||
// To force redraw of scene tree.
|
||||
undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
|
||||
undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
|
||||
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
}
|
||||
|
||||
void GroupsEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
get_tree()->connect("node_added", callable_mp(this, &GroupsEditor::_load_scene_groups));
|
||||
get_tree()->connect("node_removed", callable_mp(this, &GroupsEditor::_node_removed));
|
||||
} break;
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
filter->set_right_icon(get_editor_theme_icon("Search"));
|
||||
add->set_button_icon(get_editor_theme_icon("Add"));
|
||||
_update_tree();
|
||||
} break;
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
if (groups_dirty && is_visible_in_tree()) {
|
||||
groups_dirty = false;
|
||||
_update_groups_and_tree();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void GroupsEditor::_menu_id_pressed(int p_id) {
|
||||
TreeItem *ti = tree->get_selected();
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_local = ti->get_meta("__local");
|
||||
String group_name = ti->get_meta("__name");
|
||||
|
||||
switch (p_id) {
|
||||
case DELETE_GROUP: {
|
||||
if (!is_local || scene_groups[group_name]) {
|
||||
_show_remove_group_dialog();
|
||||
}
|
||||
} break;
|
||||
case RENAME_GROUP: {
|
||||
if (!is_local || scene_groups[group_name]) {
|
||||
_show_rename_group_dialog();
|
||||
}
|
||||
} break;
|
||||
case CONVERT_GROUP: {
|
||||
String description = ti->get_meta("__description");
|
||||
String property_name = GLOBAL_GROUP_PREFIX + group_name;
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
if (is_local) {
|
||||
undo_redo->create_action(TTR("Convert to Global Group"));
|
||||
|
||||
undo_redo->add_do_property(ProjectSettings::get_singleton(), property_name, "");
|
||||
undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_name, Variant());
|
||||
|
||||
undo_redo->add_do_method(ProjectSettings::get_singleton(), "save");
|
||||
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "save");
|
||||
|
||||
undo_redo->add_undo_method(this, "_add_scene_group", group_name);
|
||||
|
||||
undo_redo->add_do_method(this, "_update_groups_and_tree");
|
||||
undo_redo->add_undo_method(this, "_update_groups_and_tree");
|
||||
|
||||
undo_redo->commit_action();
|
||||
} else {
|
||||
undo_redo->create_action(TTR("Convert to Scene Group"));
|
||||
|
||||
undo_redo->add_do_property(ProjectSettings::get_singleton(), property_name, Variant());
|
||||
undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_name, description);
|
||||
|
||||
undo_redo->add_do_method(ProjectSettings::get_singleton(), "save");
|
||||
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "save");
|
||||
|
||||
undo_redo->add_do_method(this, "_add_scene_group", group_name);
|
||||
|
||||
undo_redo->add_do_method(this, "_update_groups_and_tree");
|
||||
undo_redo->add_undo_method(this, "_update_groups_and_tree");
|
||||
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void GroupsEditor::_item_mouse_selected(const Vector2 &p_pos, MouseButton p_mouse_button) {
|
||||
TreeItem *ti = tree->get_selected();
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_mouse_button == MouseButton::LEFT) {
|
||||
callable_mp(this, &GroupsEditor::_item_edited).call_deferred();
|
||||
} else if (p_mouse_button == MouseButton::RIGHT) {
|
||||
// Restore the previous state after clicking RMB.
|
||||
if (ti->is_editable(0)) {
|
||||
ti->set_checked(0, !ti->is_checked(0));
|
||||
}
|
||||
|
||||
menu->clear();
|
||||
if (ti->get_meta("__local")) {
|
||||
menu->add_icon_item(get_editor_theme_icon(SNAME("Environment")), TTR("Convert to Global Group"), CONVERT_GROUP);
|
||||
} else {
|
||||
menu->add_icon_item(get_editor_theme_icon(SNAME("PackedScene")), TTR("Convert to Scene Group"), CONVERT_GROUP);
|
||||
}
|
||||
|
||||
String group_name = ti->get_meta("__name");
|
||||
if (global_groups.has(group_name) || scene_groups[group_name]) {
|
||||
menu->add_separator();
|
||||
menu->add_icon_shortcut(get_editor_theme_icon(SNAME("Rename")), ED_GET_SHORTCUT("groups_editor/rename"), RENAME_GROUP);
|
||||
menu->add_icon_shortcut(get_editor_theme_icon(SNAME("Remove")), ED_GET_SHORTCUT("groups_editor/delete"), DELETE_GROUP);
|
||||
}
|
||||
|
||||
menu->set_position(tree->get_screen_position() + p_pos);
|
||||
menu->reset_size();
|
||||
menu->popup();
|
||||
}
|
||||
}
|
||||
|
||||
void GroupsEditor::_confirm_add() {
|
||||
String name = add_group_name->get_text().strip_edges();
|
||||
|
||||
String description = add_group_description->get_text();
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Add to Group"));
|
||||
|
||||
undo_redo->add_do_method(node, "add_to_group", name, true);
|
||||
undo_redo->add_undo_method(node, "remove_from_group", name);
|
||||
|
||||
bool is_local = !global_group_button->is_pressed();
|
||||
if (is_local) {
|
||||
undo_redo->add_do_method(this, "_add_scene_group", name);
|
||||
undo_redo->add_undo_method(this, "_remove_scene_group", name);
|
||||
} else {
|
||||
String property_name = GLOBAL_GROUP_PREFIX + name;
|
||||
|
||||
undo_redo->add_do_property(ProjectSettings::get_singleton(), property_name, description);
|
||||
undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_name, Variant());
|
||||
|
||||
undo_redo->add_do_method(ProjectSettings::get_singleton(), "save");
|
||||
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "save");
|
||||
|
||||
undo_redo->add_do_method(this, "_update_groups");
|
||||
undo_redo->add_undo_method(this, "_update_groups");
|
||||
}
|
||||
|
||||
undo_redo->add_do_method(this, "_update_tree");
|
||||
undo_redo->add_undo_method(this, "_update_tree");
|
||||
|
||||
// To force redraw of scene tree.
|
||||
undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
|
||||
undo_redo->add_undo_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree");
|
||||
|
||||
undo_redo->commit_action();
|
||||
tree->grab_focus();
|
||||
}
|
||||
|
||||
void GroupsEditor::_confirm_rename() {
|
||||
TreeItem *ti = tree->get_selected();
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
|
||||
String old_name = ti->get_meta("__name");
|
||||
String new_name = rename_group->get_text().strip_edges();
|
||||
|
||||
if (old_name == new_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Rename Group"));
|
||||
|
||||
if (!global_groups.has(old_name)) {
|
||||
undo_redo->add_do_method(this, "_rename_scene_group", old_name, new_name);
|
||||
undo_redo->add_undo_method(this, "_rename_scene_group", new_name, old_name);
|
||||
} else {
|
||||
String property_new_name = GLOBAL_GROUP_PREFIX + new_name;
|
||||
String property_old_name = GLOBAL_GROUP_PREFIX + old_name;
|
||||
|
||||
String description = ti->get_meta("__description");
|
||||
|
||||
undo_redo->add_do_property(ProjectSettings::get_singleton(), property_new_name, description);
|
||||
undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_new_name, Variant());
|
||||
|
||||
undo_redo->add_do_property(ProjectSettings::get_singleton(), property_old_name, Variant());
|
||||
undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_old_name, description);
|
||||
|
||||
if (rename_check_box->is_pressed()) {
|
||||
undo_redo->add_do_method(ProjectSettingsEditor::get_singleton()->get_group_settings(), "rename_references", old_name, new_name);
|
||||
undo_redo->add_undo_method(ProjectSettingsEditor::get_singleton()->get_group_settings(), "rename_references", new_name, old_name);
|
||||
}
|
||||
|
||||
undo_redo->add_do_method(ProjectSettings::get_singleton(), "save");
|
||||
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "save");
|
||||
|
||||
undo_redo->add_do_method(this, "_update_groups");
|
||||
undo_redo->add_undo_method(this, "_update_groups");
|
||||
}
|
||||
|
||||
undo_redo->add_do_method(this, "_update_tree");
|
||||
undo_redo->add_undo_method(this, "_update_tree");
|
||||
|
||||
undo_redo->commit_action();
|
||||
|
||||
tree->grab_focus();
|
||||
}
|
||||
|
||||
void GroupsEditor::_confirm_delete() {
|
||||
TreeItem *ti = tree->get_selected();
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
|
||||
String name = ti->get_meta("__name");
|
||||
bool is_local = ti->get_meta("__local");
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action(TTR("Remove Group"));
|
||||
|
||||
if (is_local) {
|
||||
undo_redo->add_do_method(this, "_remove_scene_group", name);
|
||||
undo_redo->add_undo_method(this, "_add_scene_group", name);
|
||||
} else {
|
||||
String property_name = GLOBAL_GROUP_PREFIX + name;
|
||||
String description = ti->get_meta("__description");
|
||||
|
||||
undo_redo->add_do_property(ProjectSettings::get_singleton(), property_name, Variant());
|
||||
undo_redo->add_undo_property(ProjectSettings::get_singleton(), property_name, description);
|
||||
|
||||
if (remove_check_box->is_pressed()) {
|
||||
undo_redo->add_do_method(ProjectSettingsEditor::get_singleton()->get_group_settings(), "remove_references", name);
|
||||
}
|
||||
|
||||
undo_redo->add_do_method(ProjectSettings::get_singleton(), "save");
|
||||
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "save");
|
||||
|
||||
undo_redo->add_do_method(this, "_update_groups");
|
||||
undo_redo->add_undo_method(this, "_update_groups");
|
||||
}
|
||||
|
||||
undo_redo->add_do_method(this, "_update_tree");
|
||||
undo_redo->add_undo_method(this, "_update_tree");
|
||||
|
||||
undo_redo->commit_action();
|
||||
tree->grab_focus();
|
||||
}
|
||||
|
||||
void GroupsEditor::_show_add_group_dialog() {
|
||||
if (!add_group_dialog) {
|
||||
add_group_dialog = memnew(ConfirmationDialog);
|
||||
add_group_dialog->set_title(TTR("Create New Group"));
|
||||
add_group_dialog->connect(SceneStringName(confirmed), callable_mp(this, &GroupsEditor::_confirm_add));
|
||||
|
||||
VBoxContainer *vbc = memnew(VBoxContainer);
|
||||
add_group_dialog->add_child(vbc);
|
||||
|
||||
GridContainer *gc = memnew(GridContainer);
|
||||
gc->set_columns(2);
|
||||
vbc->add_child(gc);
|
||||
|
||||
Label *label_name = memnew(Label(TTR("Name:")));
|
||||
label_name->set_h_size_flags(SIZE_SHRINK_BEGIN);
|
||||
gc->add_child(label_name);
|
||||
|
||||
HBoxContainer *hbc = memnew(HBoxContainer);
|
||||
hbc->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
gc->add_child(hbc);
|
||||
|
||||
add_group_name = memnew(LineEdit);
|
||||
add_group_name->set_custom_minimum_size(Size2(200 * EDSCALE, 0));
|
||||
add_group_name->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
add_group_name->set_accessibility_name(TTRC("Name:"));
|
||||
hbc->add_child(add_group_name);
|
||||
|
||||
global_group_button = memnew(CheckButton);
|
||||
global_group_button->set_text(TTR("Global"));
|
||||
hbc->add_child(global_group_button);
|
||||
|
||||
Label *label_description = memnew(Label(TTR("Description:")));
|
||||
label_name->set_h_size_flags(SIZE_SHRINK_BEGIN);
|
||||
gc->add_child(label_description);
|
||||
|
||||
add_group_description = memnew(LineEdit);
|
||||
add_group_description->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
add_group_description->set_editable(false);
|
||||
add_group_description->set_accessibility_name(TTRC("Description:"));
|
||||
gc->add_child(add_group_description);
|
||||
|
||||
global_group_button->connect(SceneStringName(toggled), callable_mp(add_group_description, &LineEdit::set_editable));
|
||||
|
||||
add_group_dialog->register_text_enter(add_group_name);
|
||||
add_group_dialog->register_text_enter(add_group_description);
|
||||
|
||||
add_validation_panel = memnew(EditorValidationPanel);
|
||||
add_validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Group name is valid."));
|
||||
add_validation_panel->set_update_callback(callable_mp(this, &GroupsEditor::_check_add));
|
||||
add_validation_panel->set_accept_button(add_group_dialog->get_ok_button());
|
||||
|
||||
add_group_name->connect(SceneStringName(text_changed), callable_mp(add_validation_panel, &EditorValidationPanel::update).unbind(1));
|
||||
|
||||
vbc->add_child(add_validation_panel);
|
||||
|
||||
add_child(add_group_dialog);
|
||||
}
|
||||
add_group_name->clear();
|
||||
add_group_description->clear();
|
||||
|
||||
global_group_button->set_pressed(false);
|
||||
|
||||
add_validation_panel->update();
|
||||
|
||||
add_group_dialog->popup_centered();
|
||||
add_group_name->grab_focus();
|
||||
}
|
||||
|
||||
void GroupsEditor::_show_rename_group_dialog() {
|
||||
if (!rename_group_dialog) {
|
||||
rename_group_dialog = memnew(ConfirmationDialog);
|
||||
rename_group_dialog->set_title(TTR("Rename Group"));
|
||||
rename_group_dialog->connect(SceneStringName(confirmed), callable_mp(this, &GroupsEditor::_confirm_rename));
|
||||
|
||||
VBoxContainer *vbc = memnew(VBoxContainer);
|
||||
rename_group_dialog->add_child(vbc);
|
||||
|
||||
HBoxContainer *hbc = memnew(HBoxContainer);
|
||||
hbc->add_child(memnew(Label(TTR("Name:"))));
|
||||
|
||||
rename_group = memnew(LineEdit);
|
||||
rename_group->set_custom_minimum_size(Size2(300 * EDSCALE, 1));
|
||||
hbc->add_child(rename_group);
|
||||
vbc->add_child(hbc);
|
||||
|
||||
rename_group_dialog->register_text_enter(rename_group);
|
||||
|
||||
rename_validation_panel = memnew(EditorValidationPanel);
|
||||
rename_validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Group name is valid."));
|
||||
rename_validation_panel->set_update_callback(callable_mp(this, &GroupsEditor::_check_rename));
|
||||
rename_validation_panel->set_accept_button(rename_group_dialog->get_ok_button());
|
||||
|
||||
rename_group->connect(SceneStringName(text_changed), callable_mp(rename_validation_panel, &EditorValidationPanel::update).unbind(1));
|
||||
|
||||
vbc->add_child(rename_validation_panel);
|
||||
|
||||
rename_check_box = memnew(CheckBox);
|
||||
rename_check_box->set_text(TTR("Rename references in all scenes"));
|
||||
vbc->add_child(rename_check_box);
|
||||
|
||||
add_child(rename_group_dialog);
|
||||
}
|
||||
|
||||
TreeItem *ti = tree->get_selected();
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_global = !ti->get_meta("__local");
|
||||
rename_check_box->set_visible(is_global);
|
||||
rename_check_box->set_pressed(false);
|
||||
|
||||
String name = ti->get_meta("__name");
|
||||
|
||||
rename_group->set_text(name);
|
||||
rename_group_dialog->set_meta("__name", name);
|
||||
|
||||
rename_validation_panel->update();
|
||||
|
||||
rename_group_dialog->reset_size();
|
||||
rename_group_dialog->popup_centered();
|
||||
rename_group->select_all();
|
||||
rename_group->grab_focus();
|
||||
}
|
||||
|
||||
void GroupsEditor::_show_remove_group_dialog() {
|
||||
if (!remove_group_dialog) {
|
||||
remove_group_dialog = memnew(ConfirmationDialog);
|
||||
remove_group_dialog->connect(SceneStringName(confirmed), callable_mp(this, &GroupsEditor::_confirm_delete));
|
||||
|
||||
VBoxContainer *vbox = memnew(VBoxContainer);
|
||||
remove_label = memnew(Label);
|
||||
remove_label->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
vbox->add_child(remove_label);
|
||||
|
||||
remove_check_box = memnew(CheckBox);
|
||||
remove_check_box->set_text(TTR("Delete references from all scenes"));
|
||||
vbox->add_child(remove_check_box);
|
||||
|
||||
remove_group_dialog->add_child(vbox);
|
||||
|
||||
add_child(remove_group_dialog);
|
||||
}
|
||||
|
||||
TreeItem *ti = tree->get_selected();
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_global = !ti->get_meta("__local");
|
||||
remove_check_box->set_visible(is_global);
|
||||
remove_check_box->set_pressed(false);
|
||||
remove_label->set_text(vformat(TTR("Delete group \"%s\" and all its references?"), ti->get_text(0)));
|
||||
|
||||
remove_group_dialog->reset_size();
|
||||
remove_group_dialog->popup_centered();
|
||||
}
|
||||
|
||||
void GroupsEditor::_check_add() {
|
||||
String group_name = add_group_name->get_text().strip_edges();
|
||||
_validate_name(group_name, add_validation_panel);
|
||||
}
|
||||
|
||||
void GroupsEditor::_check_rename() {
|
||||
String group_name = rename_group->get_text().strip_edges();
|
||||
String old_name = rename_group_dialog->get_meta("__name");
|
||||
|
||||
if (group_name == old_name) {
|
||||
return;
|
||||
}
|
||||
_validate_name(group_name, rename_validation_panel);
|
||||
}
|
||||
|
||||
void GroupsEditor::_validate_name(const String &p_name, EditorValidationPanel *p_validation_panel) {
|
||||
if (p_name.is_empty()) {
|
||||
p_validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Group can't be empty."), EditorValidationPanel::MSG_ERROR);
|
||||
} else if (_has_group(p_name)) {
|
||||
p_validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Group already exists."), EditorValidationPanel::MSG_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
void GroupsEditor::_groups_gui_input(Ref<InputEvent> p_event) {
|
||||
Ref<InputEventKey> key = p_event;
|
||||
if (key.is_valid() && key->is_pressed() && !key->is_echo()) {
|
||||
if (ED_IS_SHORTCUT("groups_editor/delete", p_event)) {
|
||||
_menu_id_pressed(DELETE_GROUP);
|
||||
} else if (ED_IS_SHORTCUT("groups_editor/rename", p_event)) {
|
||||
_menu_id_pressed(RENAME_GROUP);
|
||||
} else if (ED_IS_SHORTCUT("editor/open_search", p_event)) {
|
||||
filter->grab_focus();
|
||||
filter->select_all();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
accept_event();
|
||||
}
|
||||
}
|
||||
|
||||
void GroupsEditor::_bind_methods() {
|
||||
ClassDB::bind_method("_update_tree", &GroupsEditor::_update_tree);
|
||||
ClassDB::bind_method("_update_groups", &GroupsEditor::_update_groups);
|
||||
ClassDB::bind_method("_update_groups_and_tree", &GroupsEditor::_update_groups_and_tree);
|
||||
|
||||
ClassDB::bind_method("_add_scene_group", &GroupsEditor::_add_scene_group);
|
||||
ClassDB::bind_method("_rename_scene_group", &GroupsEditor::_rename_scene_group);
|
||||
ClassDB::bind_method("_remove_scene_group", &GroupsEditor::_remove_scene_group);
|
||||
ClassDB::bind_method("_set_group_checked", &GroupsEditor::_set_group_checked);
|
||||
}
|
||||
|
||||
void GroupsEditor::_node_removed(Node *p_node) {
|
||||
if (scene_root_node == p_node) {
|
||||
scene_groups_for_caching = scene_groups;
|
||||
callable_mp(this, &GroupsEditor::_cache_scene_groups).call_deferred(p_node->get_instance_id());
|
||||
scene_root_node = nullptr;
|
||||
}
|
||||
|
||||
if (scene_root_node && scene_root_node == p_node->get_owner()) {
|
||||
_queue_update_groups_and_tree();
|
||||
}
|
||||
}
|
||||
|
||||
GroupsEditor::GroupsEditor() {
|
||||
node = nullptr;
|
||||
scene_tree = SceneTree::get_singleton();
|
||||
|
||||
ED_SHORTCUT("groups_editor/delete", TTRC("Delete"), Key::KEY_DELETE);
|
||||
ED_SHORTCUT("groups_editor/rename", TTRC("Rename"), Key::F2);
|
||||
ED_SHORTCUT_OVERRIDE("groups_editor/rename", "macos", Key::ENTER);
|
||||
|
||||
HBoxContainer *hbc = memnew(HBoxContainer);
|
||||
add_child(hbc);
|
||||
|
||||
add = memnew(Button);
|
||||
add->set_theme_type_variation("FlatMenuButton");
|
||||
add->set_tooltip_text(TTR("Add a new group."));
|
||||
add->connect(SceneStringName(pressed), callable_mp(this, &GroupsEditor::_show_add_group_dialog));
|
||||
hbc->add_child(add);
|
||||
|
||||
filter = memnew(LineEdit);
|
||||
filter->set_clear_button_enabled(true);
|
||||
filter->set_placeholder(TTR("Filter Groups"));
|
||||
filter->set_accessibility_name(TTRC("Filter Groups"));
|
||||
filter->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
filter->connect(SceneStringName(text_changed), callable_mp(this, &GroupsEditor::_update_tree).unbind(1));
|
||||
hbc->add_child(filter);
|
||||
|
||||
tree = memnew(Tree);
|
||||
tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
tree->set_hide_root(true);
|
||||
tree->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
tree->set_allow_rmb_select(true);
|
||||
tree->set_select_mode(Tree::SelectMode::SELECT_SINGLE);
|
||||
tree->connect("button_clicked", callable_mp(this, &GroupsEditor::_modify_group));
|
||||
tree->connect("item_mouse_selected", callable_mp(this, &GroupsEditor::_item_mouse_selected));
|
||||
tree->connect(SceneStringName(gui_input), callable_mp(this, &GroupsEditor::_groups_gui_input));
|
||||
add_child(tree);
|
||||
|
||||
menu = memnew(PopupMenu);
|
||||
menu->connect(SceneStringName(id_pressed), callable_mp(this, &GroupsEditor::_menu_id_pressed));
|
||||
tree->add_child(menu);
|
||||
|
||||
ProjectSettingsEditor::get_singleton()->get_group_settings()->connect("group_changed", callable_mp(this, &GroupsEditor::_update_groups_and_tree));
|
||||
}
|
140
editor/docks/groups_editor.h
Normal file
140
editor/docks/groups_editor.h
Normal file
@@ -0,0 +1,140 @@
|
||||
/**************************************************************************/
|
||||
/* groups_editor.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/dialogs.h"
|
||||
|
||||
class Button;
|
||||
class CheckBox;
|
||||
class CheckButton;
|
||||
class EditorValidationPanel;
|
||||
class Label;
|
||||
class LineEdit;
|
||||
class PopupMenu;
|
||||
class Tree;
|
||||
class TreeItem;
|
||||
|
||||
class GroupsEditor : public VBoxContainer {
|
||||
GDCLASS(GroupsEditor, VBoxContainer);
|
||||
|
||||
const String GLOBAL_GROUP_PREFIX = "global_group/";
|
||||
|
||||
bool updating_tree = false;
|
||||
bool updating_groups = false;
|
||||
bool groups_dirty = false;
|
||||
bool update_groups_and_tree_queued = false;
|
||||
|
||||
Node *node = nullptr;
|
||||
Node *scene_root_node = nullptr;
|
||||
SceneTree *scene_tree = nullptr;
|
||||
|
||||
ConfirmationDialog *add_group_dialog = nullptr;
|
||||
LineEdit *add_group_name = nullptr;
|
||||
LineEdit *add_group_description = nullptr;
|
||||
CheckButton *global_group_button = nullptr;
|
||||
EditorValidationPanel *add_validation_panel = nullptr;
|
||||
|
||||
ConfirmationDialog *rename_group_dialog = nullptr;
|
||||
LineEdit *rename_group = nullptr;
|
||||
CheckBox *rename_check_box = nullptr;
|
||||
EditorValidationPanel *rename_validation_panel = nullptr;
|
||||
|
||||
ConfirmationDialog *remove_group_dialog = nullptr;
|
||||
CheckBox *remove_check_box = nullptr;
|
||||
Label *remove_label = nullptr;
|
||||
|
||||
PopupMenu *menu = nullptr;
|
||||
|
||||
LineEdit *filter = nullptr;
|
||||
Button *add = nullptr;
|
||||
Tree *tree = nullptr;
|
||||
|
||||
HashMap<ObjectID, HashMap<StringName, bool>> scene_groups_cache;
|
||||
HashMap<StringName, bool> scene_groups_for_caching;
|
||||
|
||||
HashMap<StringName, bool> scene_groups;
|
||||
HashMap<StringName, String> global_groups;
|
||||
|
||||
void _update_scene_groups(const ObjectID &p_id);
|
||||
void _cache_scene_groups(const ObjectID &p_id);
|
||||
|
||||
void _show_add_group_dialog();
|
||||
void _show_rename_group_dialog();
|
||||
void _show_remove_group_dialog();
|
||||
|
||||
void _check_add();
|
||||
void _check_rename();
|
||||
void _validate_name(const String &p_name, EditorValidationPanel *p_validation_panel);
|
||||
|
||||
void _update_tree();
|
||||
|
||||
void _update_groups();
|
||||
void _load_scene_groups(Node *p_node);
|
||||
|
||||
void _add_scene_group(const String &p_name);
|
||||
void _rename_scene_group(const String &p_old_name, const String &p_new_name);
|
||||
void _remove_scene_group(const String &p_name);
|
||||
|
||||
bool _has_group(const String &p_name);
|
||||
void _set_group_checked(const String &p_name, bool p_checked);
|
||||
|
||||
void _confirm_add();
|
||||
void _confirm_rename();
|
||||
void _confirm_delete();
|
||||
|
||||
void _item_edited();
|
||||
void _item_mouse_selected(const Vector2 &p_pos, MouseButton p_mouse_button);
|
||||
void _modify_group(Object *p_item, int p_column, int p_id, MouseButton p_mouse_button);
|
||||
void _menu_id_pressed(int p_id);
|
||||
|
||||
void _update_groups_and_tree();
|
||||
void _queue_update_groups_and_tree();
|
||||
|
||||
void _groups_gui_input(Ref<InputEvent> p_event);
|
||||
|
||||
void _node_removed(Node *p_node);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
enum ModifyButton {
|
||||
DELETE_GROUP,
|
||||
COPY_GROUP,
|
||||
RENAME_GROUP,
|
||||
CONVERT_GROUP,
|
||||
};
|
||||
|
||||
void set_current(Node *p_node);
|
||||
|
||||
GroupsEditor();
|
||||
};
|
275
editor/docks/history_dock.cpp
Normal file
275
editor/docks/history_dock.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
/**************************************************************************/
|
||||
/* history_dock.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "history_dock.h"
|
||||
|
||||
#include "core/io/config_file.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/settings/editor_settings.h"
|
||||
#include "scene/gui/check_box.h"
|
||||
#include "scene/gui/item_list.h"
|
||||
|
||||
struct SortActionsByTimestamp {
|
||||
bool operator()(const EditorUndoRedoManager::Action &l, const EditorUndoRedoManager::Action &r) const {
|
||||
return l.timestamp > r.timestamp;
|
||||
}
|
||||
};
|
||||
|
||||
void HistoryDock::on_history_changed() {
|
||||
if (is_visible_in_tree()) {
|
||||
refresh_history();
|
||||
} else {
|
||||
need_refresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryDock::refresh_history() {
|
||||
action_list->clear();
|
||||
bool include_scene = current_scene_checkbox->is_pressed();
|
||||
bool include_global = global_history_checkbox->is_pressed();
|
||||
|
||||
if (!include_scene && !include_global) {
|
||||
action_list->add_item(TTRC("The Beginning"));
|
||||
action_list->set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_ALWAYS);
|
||||
return;
|
||||
}
|
||||
|
||||
const EditorUndoRedoManager::History ¤t_scene_history = ur_manager->get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id());
|
||||
const EditorUndoRedoManager::History &global_history = ur_manager->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY);
|
||||
|
||||
Vector<EditorUndoRedoManager::Action> full_history;
|
||||
{
|
||||
int full_size = 0;
|
||||
if (include_scene) {
|
||||
full_size += current_scene_history.redo_stack.size() + current_scene_history.undo_stack.size();
|
||||
}
|
||||
if (include_global) {
|
||||
full_size += global_history.redo_stack.size() + global_history.undo_stack.size();
|
||||
}
|
||||
full_history.resize(full_size);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
if (include_scene) {
|
||||
for (const EditorUndoRedoManager::Action &E : current_scene_history.redo_stack) {
|
||||
full_history.write[i] = E;
|
||||
i++;
|
||||
}
|
||||
for (const EditorUndoRedoManager::Action &E : current_scene_history.undo_stack) {
|
||||
full_history.write[i] = E;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (include_global) {
|
||||
for (const EditorUndoRedoManager::Action &E : global_history.redo_stack) {
|
||||
full_history.write[i] = E;
|
||||
i++;
|
||||
}
|
||||
for (const EditorUndoRedoManager::Action &E : global_history.undo_stack) {
|
||||
full_history.write[i] = E;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
full_history.sort_custom<SortActionsByTimestamp>();
|
||||
for (const EditorUndoRedoManager::Action &E : full_history) {
|
||||
action_list->add_item(E.action_name);
|
||||
if (E.history_id == EditorUndoRedoManager::GLOBAL_HISTORY) {
|
||||
action_list->set_item_custom_fg_color(-1, get_theme_color(SNAME("accent_color"), EditorStringName(Editor)));
|
||||
}
|
||||
}
|
||||
|
||||
action_list->add_item(TTRC("The Beginning"));
|
||||
action_list->set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_ALWAYS);
|
||||
refresh_version();
|
||||
}
|
||||
|
||||
void HistoryDock::on_version_changed() {
|
||||
if (is_visible_in_tree()) {
|
||||
refresh_version();
|
||||
} else {
|
||||
need_refresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryDock::refresh_version() {
|
||||
int idx = 0;
|
||||
bool include_scene = current_scene_checkbox->is_pressed();
|
||||
bool include_global = global_history_checkbox->is_pressed();
|
||||
|
||||
if (!include_scene && !include_global) {
|
||||
current_version = idx;
|
||||
action_list->set_current(idx);
|
||||
return;
|
||||
}
|
||||
|
||||
const EditorUndoRedoManager::History ¤t_scene_history = ur_manager->get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id());
|
||||
const EditorUndoRedoManager::History &global_history = ur_manager->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY);
|
||||
double newest_undo_timestamp = 0;
|
||||
|
||||
if (include_scene && !current_scene_history.undo_stack.is_empty()) {
|
||||
newest_undo_timestamp = current_scene_history.undo_stack.front()->get().timestamp;
|
||||
}
|
||||
|
||||
if (include_global && !global_history.undo_stack.is_empty()) {
|
||||
double global_undo_timestamp = global_history.undo_stack.front()->get().timestamp;
|
||||
if (global_undo_timestamp > newest_undo_timestamp) {
|
||||
newest_undo_timestamp = global_undo_timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
if (include_scene) {
|
||||
int skip = 0;
|
||||
for (const EditorUndoRedoManager::Action &E : current_scene_history.redo_stack) {
|
||||
if (E.timestamp < newest_undo_timestamp) {
|
||||
skip++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
idx += current_scene_history.redo_stack.size() - skip;
|
||||
}
|
||||
|
||||
if (include_global) {
|
||||
int skip = 0;
|
||||
for (const EditorUndoRedoManager::Action &E : global_history.redo_stack) {
|
||||
if (E.timestamp < newest_undo_timestamp) {
|
||||
skip++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
idx += global_history.redo_stack.size() - skip;
|
||||
}
|
||||
|
||||
current_version = idx;
|
||||
action_list->set_current(idx);
|
||||
}
|
||||
|
||||
void HistoryDock::_save_layout_to_config(Ref<ConfigFile> p_layout, const String &p_section) const {
|
||||
p_layout->set_value(p_section, "dock_history_include_scene", current_scene_checkbox->is_pressed());
|
||||
p_layout->set_value(p_section, "dock_history_include_global", global_history_checkbox->is_pressed());
|
||||
}
|
||||
|
||||
void HistoryDock::_load_layout_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
|
||||
current_scene_checkbox->set_pressed_no_signal(p_layout->get_value(p_section, "dock_history_include_scene", true));
|
||||
global_history_checkbox->set_pressed_no_signal(p_layout->get_value(p_section, "dock_history_include_global", true));
|
||||
refresh_history();
|
||||
}
|
||||
|
||||
void HistoryDock::seek_history(int p_index) {
|
||||
bool include_scene = current_scene_checkbox->is_pressed();
|
||||
bool include_global = global_history_checkbox->is_pressed();
|
||||
|
||||
if (!include_scene && !include_global) {
|
||||
return;
|
||||
}
|
||||
int current_scene_id = EditorNode::get_editor_data().get_current_edited_scene_history_id();
|
||||
|
||||
while (current_version < p_index) {
|
||||
if (include_scene) {
|
||||
if (include_global) {
|
||||
ur_manager->undo();
|
||||
} else {
|
||||
ur_manager->undo_history(current_scene_id);
|
||||
}
|
||||
} else {
|
||||
ur_manager->undo_history(EditorUndoRedoManager::GLOBAL_HISTORY);
|
||||
}
|
||||
}
|
||||
|
||||
while (current_version > p_index) {
|
||||
if (include_scene) {
|
||||
if (include_global) {
|
||||
ur_manager->redo();
|
||||
} else {
|
||||
ur_manager->redo_history(current_scene_id);
|
||||
}
|
||||
} else {
|
||||
ur_manager->redo_history(EditorUndoRedoManager::GLOBAL_HISTORY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryDock::_notification(int p_notification) {
|
||||
switch (p_notification) {
|
||||
case NOTIFICATION_READY: {
|
||||
EditorNode::get_singleton()->connect("scene_changed", callable_mp(this, &HistoryDock::on_history_changed));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
if (is_visible_in_tree() && need_refresh) {
|
||||
refresh_history();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryDock::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_save_layout_to_config"), &HistoryDock::_save_layout_to_config);
|
||||
ClassDB::bind_method(D_METHOD("_load_layout_from_config"), &HistoryDock::_load_layout_from_config);
|
||||
}
|
||||
|
||||
HistoryDock::HistoryDock() {
|
||||
set_name("History");
|
||||
|
||||
ur_manager = EditorUndoRedoManager::get_singleton();
|
||||
ur_manager->connect("history_changed", callable_mp(this, &HistoryDock::on_history_changed));
|
||||
ur_manager->connect("version_changed", callable_mp(this, &HistoryDock::on_version_changed));
|
||||
|
||||
HBoxContainer *mode_hb = memnew(HBoxContainer);
|
||||
add_child(mode_hb);
|
||||
|
||||
current_scene_checkbox = memnew(CheckBox);
|
||||
mode_hb->add_child(current_scene_checkbox);
|
||||
current_scene_checkbox->set_flat(true);
|
||||
current_scene_checkbox->set_text(TTRC("Scene"));
|
||||
current_scene_checkbox->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
current_scene_checkbox->set_clip_text(true);
|
||||
current_scene_checkbox->set_pressed(true);
|
||||
current_scene_checkbox->connect(SceneStringName(toggled), callable_mp(this, &HistoryDock::refresh_history).unbind(1));
|
||||
|
||||
global_history_checkbox = memnew(CheckBox);
|
||||
mode_hb->add_child(global_history_checkbox);
|
||||
global_history_checkbox->set_flat(true);
|
||||
global_history_checkbox->set_text(TTRC("Global"));
|
||||
global_history_checkbox->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
global_history_checkbox->set_clip_text(true);
|
||||
global_history_checkbox->set_pressed(true);
|
||||
global_history_checkbox->connect(SceneStringName(toggled), callable_mp(this, &HistoryDock::refresh_history).unbind(1));
|
||||
|
||||
action_list = memnew(ItemList);
|
||||
action_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
add_child(action_list);
|
||||
action_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
action_list->connect(SceneStringName(item_selected), callable_mp(this, &HistoryDock::seek_history));
|
||||
}
|
68
editor/docks/history_dock.h
Normal file
68
editor/docks/history_dock.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/**************************************************************************/
|
||||
/* history_dock.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/box_container.h"
|
||||
|
||||
class CheckBox;
|
||||
class ConfigFile;
|
||||
class ItemList;
|
||||
class EditorUndoRedoManager;
|
||||
|
||||
class HistoryDock : public VBoxContainer {
|
||||
GDCLASS(HistoryDock, VBoxContainer);
|
||||
|
||||
EditorUndoRedoManager *ur_manager;
|
||||
ItemList *action_list = nullptr;
|
||||
|
||||
CheckBox *current_scene_checkbox = nullptr;
|
||||
CheckBox *global_history_checkbox = nullptr;
|
||||
|
||||
bool need_refresh = true;
|
||||
int current_version = 0;
|
||||
|
||||
void on_history_changed();
|
||||
void refresh_history();
|
||||
void on_version_changed();
|
||||
void refresh_version();
|
||||
|
||||
void _save_layout_to_config(Ref<ConfigFile> p_layout, const String &p_section) const;
|
||||
void _load_layout_from_config(Ref<ConfigFile> p_layout, const String &p_section);
|
||||
|
||||
protected:
|
||||
void _notification(int p_notification);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void seek_history(int p_index);
|
||||
|
||||
HistoryDock();
|
||||
};
|
828
editor/docks/import_dock.cpp
Normal file
828
editor/docks/import_dock.cpp
Normal file
@@ -0,0 +1,828 @@
|
||||
/**************************************************************************/
|
||||
/* import_dock.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "import_dock.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/inspector/editor_resource_preview.h"
|
||||
#include "editor/settings/editor_settings.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "editor/themes/editor_theme_manager.h"
|
||||
|
||||
class ImportDockParameters : public Object {
|
||||
GDCLASS(ImportDockParameters, Object);
|
||||
|
||||
public:
|
||||
HashMap<StringName, Variant> values;
|
||||
List<PropertyInfo> properties;
|
||||
Ref<ResourceImporter> importer;
|
||||
Vector<String> paths;
|
||||
HashSet<StringName> checked;
|
||||
bool checking = false;
|
||||
bool skip = false;
|
||||
String base_options_path;
|
||||
|
||||
bool _set(const StringName &p_name, const Variant &p_value) {
|
||||
if (values.has(p_name)) {
|
||||
values[p_name] = p_value;
|
||||
if (checking) {
|
||||
checked.insert(p_name);
|
||||
notify_property_list_changed();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const {
|
||||
if (values.has(p_name)) {
|
||||
r_ret = values[p_name];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const {
|
||||
for (const PropertyInfo &E : properties) {
|
||||
if (!importer->get_option_visibility(base_options_path, E.name, values)) {
|
||||
continue;
|
||||
}
|
||||
PropertyInfo pi = E;
|
||||
if (checking) {
|
||||
pi.usage |= PROPERTY_USAGE_CHECKABLE;
|
||||
if (checked.has(E.name)) {
|
||||
pi.usage |= PROPERTY_USAGE_CHECKED;
|
||||
}
|
||||
}
|
||||
p_list->push_back(pi);
|
||||
}
|
||||
}
|
||||
|
||||
void update() {
|
||||
notify_property_list_changed();
|
||||
}
|
||||
};
|
||||
|
||||
ImportDock *ImportDock::singleton = nullptr;
|
||||
|
||||
void ImportDock::set_edit_path(const String &p_path) {
|
||||
Ref<ConfigFile> config;
|
||||
config.instantiate();
|
||||
Error err = config->load(p_path + ".import");
|
||||
if (err != OK) {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
String importer_name = config->get_value("remap", "importer");
|
||||
if (importer_name == "keep") {
|
||||
params->importer.unref();
|
||||
params->skip = false;
|
||||
} else if (importer_name == "skip") {
|
||||
params->importer.unref();
|
||||
params->skip = true;
|
||||
} else {
|
||||
params->importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name);
|
||||
params->skip = false;
|
||||
}
|
||||
|
||||
params->paths.clear();
|
||||
params->paths.push_back(p_path);
|
||||
params->base_options_path = p_path;
|
||||
|
||||
_update_options(p_path, config);
|
||||
|
||||
List<Ref<ResourceImporter>> importers;
|
||||
ResourceFormatImporter::get_singleton()->get_importers_for_file(p_path, &importers);
|
||||
List<Pair<String, String>> importer_names;
|
||||
|
||||
for (const Ref<ResourceImporter> &E : importers) {
|
||||
importer_names.push_back(Pair<String, String>(E->get_visible_name(), E->get_importer_name()));
|
||||
}
|
||||
|
||||
importer_names.sort_custom<PairSort<String, String>>();
|
||||
|
||||
import_as->clear();
|
||||
|
||||
for (const Pair<String, String> &E : importer_names) {
|
||||
import_as->add_item(E.first);
|
||||
import_as->set_item_metadata(-1, E.second);
|
||||
if (E.second == importer_name) {
|
||||
import_as->select(import_as->get_item_count() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
_add_keep_import_option(importer_name);
|
||||
|
||||
import->set_disabled(false);
|
||||
_set_dirty(false);
|
||||
import_as->set_disabled(false);
|
||||
preset->set_disabled(false);
|
||||
content->show();
|
||||
select_a_resource->hide();
|
||||
|
||||
imported->set_text(p_path.get_file());
|
||||
}
|
||||
|
||||
void ImportDock::_add_keep_import_option(const String &p_importer_name) {
|
||||
import_as->add_separator();
|
||||
import_as->add_item(TTRC("Keep File (exported as is)"));
|
||||
import_as->set_item_metadata(-1, "keep");
|
||||
import_as->add_item(TTRC("Skip File (not exported)"));
|
||||
import_as->set_item_metadata(-1, "skip");
|
||||
if (p_importer_name == "keep") {
|
||||
import_as->select(import_as->get_item_count() - 2);
|
||||
} else if (p_importer_name == "skip") {
|
||||
import_as->select(import_as->get_item_count() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDock::_update_options(const String &p_path, const Ref<ConfigFile> &p_config) {
|
||||
// Set the importer class to fetch the correct class in the XML class reference.
|
||||
// This allows tooltips to display when hovering properties.
|
||||
if (params->importer.is_valid()) {
|
||||
// Null check to avoid crashing if the "Keep File (exported as is)" mode is selected.
|
||||
import_opts->set_object_class(params->importer->get_class_name());
|
||||
}
|
||||
|
||||
List<ResourceImporter::ImportOption> options;
|
||||
|
||||
if (params->importer.is_valid()) {
|
||||
params->importer->get_import_options(p_path, &options);
|
||||
}
|
||||
|
||||
params->properties.clear();
|
||||
params->values.clear();
|
||||
params->checking = params->paths.size() > 1;
|
||||
params->checked.clear();
|
||||
params->base_options_path = p_path;
|
||||
|
||||
HashMap<StringName, Variant> import_options;
|
||||
if (p_config.is_valid() && p_config->has_section("params")) {
|
||||
Vector<String> section_keys = p_config->get_section_keys("params");
|
||||
for (const String §ion_key : section_keys) {
|
||||
import_options[section_key] = p_config->get_value("params", section_key);
|
||||
}
|
||||
if (params->importer.is_valid()) {
|
||||
params->importer->handle_compatibility_options(import_options);
|
||||
}
|
||||
}
|
||||
|
||||
for (const ResourceImporter::ImportOption &E : options) {
|
||||
params->properties.push_back(E.option);
|
||||
if (p_config.is_valid() && import_options.has(E.option.name)) {
|
||||
params->values[E.option.name] = import_options[E.option.name];
|
||||
} else {
|
||||
params->values[E.option.name] = E.default_value;
|
||||
}
|
||||
}
|
||||
|
||||
params->update();
|
||||
_update_preset_menu();
|
||||
|
||||
bool was_imported = p_config.is_valid() && p_config->get_value("remap", "importer") != "skip" && p_config->get_value("remap", "importer") != "keep";
|
||||
if (was_imported && params->importer.is_valid() && params->paths.size() == 1 && params->importer->has_advanced_options()) {
|
||||
advanced->show();
|
||||
advanced_spacer->show();
|
||||
} else {
|
||||
advanced->hide();
|
||||
advanced_spacer->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) {
|
||||
clear();
|
||||
|
||||
// Use the value that is repeated the most.
|
||||
HashMap<String, Dictionary> value_frequency;
|
||||
HashSet<String> extensions;
|
||||
|
||||
for (int i = 0; i < p_paths.size(); i++) {
|
||||
Ref<ConfigFile> config;
|
||||
config.instantiate();
|
||||
extensions.insert(p_paths[i].get_extension());
|
||||
Error err = config->load(p_paths[i] + ".import");
|
||||
ERR_CONTINUE(err != OK);
|
||||
|
||||
if (i == 0) {
|
||||
String importer_name = config->get_value("remap", "importer");
|
||||
if (importer_name == "keep") {
|
||||
params->importer.unref();
|
||||
params->skip = false;
|
||||
} else if (importer_name == "skip") {
|
||||
params->importer.unref();
|
||||
params->skip = true;
|
||||
} else {
|
||||
params->importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name);
|
||||
params->skip = false;
|
||||
}
|
||||
if (params->importer.is_null()) {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!config->has_section("params")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector<String> keys = config->get_section_keys("params");
|
||||
|
||||
for (const String &E : keys) {
|
||||
if (!value_frequency.has(E)) {
|
||||
value_frequency[E] = Dictionary();
|
||||
}
|
||||
|
||||
Variant value = config->get_value("params", E);
|
||||
|
||||
if (value_frequency[E].has(value)) {
|
||||
value_frequency[E][value] = int(value_frequency[E][value]) + 1;
|
||||
} else {
|
||||
value_frequency[E][value] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(params->importer.is_null());
|
||||
|
||||
String base_path;
|
||||
if (extensions.size() == 1 && p_paths.size() > 0) {
|
||||
base_path = p_paths[0];
|
||||
}
|
||||
List<ResourceImporter::ImportOption> options;
|
||||
params->importer->get_import_options(base_path, &options);
|
||||
|
||||
params->properties.clear();
|
||||
params->values.clear();
|
||||
params->checking = true;
|
||||
params->checked.clear();
|
||||
params->base_options_path = base_path;
|
||||
|
||||
for (const ResourceImporter::ImportOption &E : options) {
|
||||
params->properties.push_back(E.option);
|
||||
|
||||
if (value_frequency.has(E.option.name)) {
|
||||
Dictionary d = value_frequency[E.option.name];
|
||||
int freq = 0;
|
||||
Variant value;
|
||||
for (const KeyValue<Variant, Variant> &kv : d) {
|
||||
int f = kv.value;
|
||||
if (f > freq) {
|
||||
value = kv.key;
|
||||
}
|
||||
}
|
||||
|
||||
params->values[E.option.name] = value;
|
||||
} else {
|
||||
params->values[E.option.name] = E.default_value;
|
||||
}
|
||||
}
|
||||
|
||||
params->update();
|
||||
|
||||
List<Ref<ResourceImporter>> importers;
|
||||
ResourceFormatImporter::get_singleton()->get_importers_for_file(p_paths[0], &importers);
|
||||
List<Pair<String, String>> importer_names;
|
||||
|
||||
for (const Ref<ResourceImporter> &E : importers) {
|
||||
importer_names.push_back(Pair<String, String>(E->get_visible_name(), E->get_importer_name()));
|
||||
}
|
||||
|
||||
importer_names.sort_custom<PairSort<String, String>>();
|
||||
|
||||
import_as->clear();
|
||||
|
||||
for (const Pair<String, String> &E : importer_names) {
|
||||
import_as->add_item(E.first);
|
||||
import_as->set_item_metadata(-1, E.second);
|
||||
if (E.second == params->importer->get_importer_name()) {
|
||||
import_as->select(import_as->get_item_count() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
_add_keep_import_option(params->importer->get_importer_name());
|
||||
|
||||
_update_preset_menu();
|
||||
|
||||
params->paths = p_paths;
|
||||
import->set_disabled(false);
|
||||
_set_dirty(false);
|
||||
import_as->set_disabled(false);
|
||||
preset->set_disabled(false);
|
||||
content->show();
|
||||
select_a_resource->hide();
|
||||
|
||||
imported->set_text(vformat(TTR("%d Files"), p_paths.size()));
|
||||
|
||||
if (params->paths.size() == 1 && params->importer->has_advanced_options()) {
|
||||
advanced->show();
|
||||
advanced_spacer->show();
|
||||
} else {
|
||||
advanced->hide();
|
||||
advanced_spacer->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDock::reimport_resources(const Vector<String> &p_paths) {
|
||||
switch (p_paths.size()) {
|
||||
case 0:
|
||||
ERR_FAIL_MSG("You need to select files to reimport them.");
|
||||
case 1:
|
||||
set_edit_path(p_paths[0]);
|
||||
break;
|
||||
default:
|
||||
set_edit_multiple_paths(p_paths);
|
||||
break;
|
||||
}
|
||||
|
||||
_reimport_attempt();
|
||||
}
|
||||
|
||||
void ImportDock::_update_preset_menu() {
|
||||
preset->get_popup()->clear();
|
||||
|
||||
if (params->importer.is_null()) {
|
||||
preset->get_popup()->add_item(TTRC("Default"));
|
||||
preset->hide();
|
||||
return;
|
||||
}
|
||||
preset->show();
|
||||
|
||||
if (params->importer->get_preset_count() <= 0) {
|
||||
preset->get_popup()->add_item(TTRC("Default"));
|
||||
} else {
|
||||
for (int i = 0; i < params->importer->get_preset_count(); i++) {
|
||||
preset->get_popup()->add_item(params->importer->get_preset_name(i));
|
||||
}
|
||||
}
|
||||
|
||||
preset->get_popup()->add_separator();
|
||||
preset->get_popup()->add_item(vformat(TTR("Set as Default for '%s'"), params->importer->get_visible_name()), ITEM_SET_AS_DEFAULT);
|
||||
if (ProjectSettings::get_singleton()->has_setting("importer_defaults/" + params->importer->get_importer_name())) {
|
||||
preset->get_popup()->add_item(TTRC("Load Default"), ITEM_LOAD_DEFAULT);
|
||||
preset->get_popup()->add_separator();
|
||||
preset->get_popup()->add_item(vformat(TTR("Clear Default for '%s'"), params->importer->get_visible_name()), ITEM_CLEAR_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDock::_importer_selected(int i_idx) {
|
||||
String name = import_as->get_selected_metadata();
|
||||
if (name == "keep") {
|
||||
params->importer.unref();
|
||||
params->skip = false;
|
||||
_update_options(params->base_options_path, Ref<ConfigFile>());
|
||||
} else if (name == "skip") {
|
||||
params->importer.unref();
|
||||
params->skip = true;
|
||||
_update_options(params->base_options_path, Ref<ConfigFile>());
|
||||
} else {
|
||||
Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(name);
|
||||
ERR_FAIL_COND(importer.is_null());
|
||||
|
||||
params->importer = importer;
|
||||
params->skip = false;
|
||||
Ref<ConfigFile> config;
|
||||
if (params->paths.size()) {
|
||||
String path = params->paths[0];
|
||||
config.instantiate();
|
||||
Error err = config->load(path + ".import");
|
||||
if (err != OK) {
|
||||
config.unref();
|
||||
}
|
||||
}
|
||||
_update_options(params->base_options_path, config);
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDock::_preset_selected(int p_idx) {
|
||||
int item_id = preset->get_popup()->get_item_id(p_idx);
|
||||
String setting_name = "importer_defaults/" + params->importer->get_importer_name();
|
||||
|
||||
switch (item_id) {
|
||||
case ITEM_SET_AS_DEFAULT: {
|
||||
Dictionary import_settings;
|
||||
// When import settings already exist, we will update these settings
|
||||
// to ensure that the dictionary retains settings that are not displayed in the
|
||||
// editor. For Scene, the dictionary is the same for FBX, GLTF, and Blender, but each
|
||||
// file type has some different settings.
|
||||
if (ProjectSettings::get_singleton()->has_setting(setting_name)) {
|
||||
import_settings = GLOBAL_GET(setting_name);
|
||||
}
|
||||
|
||||
for (const PropertyInfo &E : params->properties) {
|
||||
import_settings[E.name] = params->values[E.name];
|
||||
}
|
||||
|
||||
ProjectSettings::get_singleton()->set(setting_name, import_settings);
|
||||
ProjectSettings::get_singleton()->save();
|
||||
_update_preset_menu();
|
||||
} break;
|
||||
case ITEM_LOAD_DEFAULT: {
|
||||
ERR_FAIL_COND(!ProjectSettings::get_singleton()->has_setting(setting_name));
|
||||
|
||||
Dictionary import_settings = GLOBAL_GET(setting_name);
|
||||
|
||||
if (params->checking) {
|
||||
params->checked.clear();
|
||||
}
|
||||
for (const KeyValue<Variant, Variant> &kv : import_settings) {
|
||||
params->values[kv.key] = kv.value;
|
||||
if (params->checking) {
|
||||
params->checked.insert(kv.key);
|
||||
}
|
||||
}
|
||||
params->update();
|
||||
} break;
|
||||
case ITEM_CLEAR_DEFAULT: {
|
||||
ProjectSettings::get_singleton()->set(setting_name, Variant());
|
||||
ProjectSettings::get_singleton()->save();
|
||||
_update_preset_menu();
|
||||
} break;
|
||||
default: {
|
||||
List<ResourceImporter::ImportOption> options;
|
||||
|
||||
params->importer->get_import_options(params->base_options_path, &options, p_idx);
|
||||
|
||||
if (params->checking) {
|
||||
params->checked.clear();
|
||||
}
|
||||
for (const ResourceImporter::ImportOption &E : options) {
|
||||
params->values[E.option.name] = E.default_value;
|
||||
if (params->checking) {
|
||||
params->checked.insert(E.option.name);
|
||||
}
|
||||
}
|
||||
params->update();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDock::clear() {
|
||||
imported->set_text("");
|
||||
import->set_disabled(true);
|
||||
import_as->clear();
|
||||
import_as->set_disabled(true);
|
||||
preset->set_disabled(true);
|
||||
params->values.clear();
|
||||
params->properties.clear();
|
||||
params->update();
|
||||
preset->get_popup()->clear();
|
||||
content->hide();
|
||||
select_a_resource->show();
|
||||
}
|
||||
|
||||
static bool _find_owners(EditorFileSystemDirectory *efsd, const String &p_path) {
|
||||
if (!efsd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < efsd->get_subdir_count(); i++) {
|
||||
if (_find_owners(efsd->get_subdir(i), p_path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < efsd->get_file_count(); i++) {
|
||||
Vector<String> deps = efsd->get_file_deps(i);
|
||||
if (deps.has(p_path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ImportDock::_reimport_pressed() {
|
||||
_reimport_attempt();
|
||||
|
||||
if (params->importer.is_valid() && params->paths.size() == 1 && params->importer->has_advanced_options()) {
|
||||
advanced->show();
|
||||
advanced_spacer->show();
|
||||
} else {
|
||||
advanced->hide();
|
||||
advanced_spacer->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDock::_reimport_attempt() {
|
||||
bool used_in_resources = false;
|
||||
|
||||
String importer_name;
|
||||
if (params->importer.is_valid()) {
|
||||
importer_name = params->importer->get_importer_name();
|
||||
} else {
|
||||
if (params->skip) {
|
||||
importer_name = "skip";
|
||||
} else {
|
||||
importer_name = "keep";
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < params->paths.size(); i++) {
|
||||
Ref<ConfigFile> config;
|
||||
config.instantiate();
|
||||
Error err = config->load(params->paths[i] + ".import");
|
||||
ERR_CONTINUE(err != OK);
|
||||
|
||||
String imported_with = config->get_value("remap", "importer");
|
||||
if (imported_with != importer_name && imported_with != "keep" && imported_with != "skip") {
|
||||
Ref<Resource> resource = ResourceLoader::load(params->paths[i]);
|
||||
if (resource.is_valid()) {
|
||||
need_cleanup.push_back(params->paths[i]);
|
||||
if (_find_owners(EditorFileSystem::get_singleton()->get_filesystem(), params->paths[i])) {
|
||||
used_in_resources = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!need_cleanup.is_empty() || used_in_resources) {
|
||||
cleanup_warning->set_visible(!need_cleanup.is_empty());
|
||||
label_warning->set_visible(used_in_resources);
|
||||
reimport_confirm->popup_centered();
|
||||
return;
|
||||
}
|
||||
|
||||
_reimport();
|
||||
}
|
||||
|
||||
void ImportDock::_reimport_and_cleanup() {
|
||||
HashMap<String, Ref<Resource>> old_resources;
|
||||
|
||||
for (const String &path : need_cleanup) {
|
||||
Ref<Resource> res = ResourceLoader::load(path);
|
||||
res->set_path("");
|
||||
res->set_meta(SNAME("_skip_save_"), true);
|
||||
old_resources[path] = res;
|
||||
}
|
||||
|
||||
EditorResourcePreview::get_singleton()->stop(); // Don't try to re-create previews after import.
|
||||
_reimport();
|
||||
|
||||
if (need_cleanup.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// After changing resource type we need to make sure that all old instances are unloaded or replaced.
|
||||
EditorNode::get_singleton()->push_item(nullptr);
|
||||
EditorUndoRedoManager::get_singleton()->clear_history();
|
||||
|
||||
List<Ref<Resource>> external_resources;
|
||||
ResourceCache::get_cached_resources(&external_resources);
|
||||
|
||||
Vector<Ref<Resource>> old_resources_to_replace;
|
||||
Vector<Ref<Resource>> new_resources_to_replace;
|
||||
for (const String &path : need_cleanup) {
|
||||
Ref<Resource> old_res = old_resources[path];
|
||||
if (params->importer.is_valid()) {
|
||||
Ref<Resource> new_res = ResourceLoader::load(path);
|
||||
if (new_res.is_valid()) {
|
||||
old_resources_to_replace.append(old_res);
|
||||
new_resources_to_replace.append(new_res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditorNode::get_singleton()->replace_resources_in_scenes(old_resources_to_replace, new_resources_to_replace);
|
||||
|
||||
for (Ref<Resource> res : external_resources) {
|
||||
EditorNode::get_singleton()->replace_resources_in_object(res.ptr(), old_resources_to_replace, new_resources_to_replace);
|
||||
}
|
||||
|
||||
need_cleanup.clear();
|
||||
}
|
||||
|
||||
void ImportDock::_advanced_options() {
|
||||
if (params->paths.size() == 1 && params->importer.is_valid()) {
|
||||
params->importer->show_advanced_options(params->paths[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDock::_reimport() {
|
||||
for (int i = 0; i < params->paths.size(); i++) {
|
||||
Ref<ConfigFile> config;
|
||||
config.instantiate();
|
||||
Error err = config->load(params->paths[i] + ".import");
|
||||
ERR_CONTINUE(err != OK);
|
||||
|
||||
if (params->importer.is_valid()) {
|
||||
String importer_name = params->importer->get_importer_name();
|
||||
|
||||
if (params->checking && config->get_value("remap", "importer") == params->importer->get_importer_name()) {
|
||||
//update only what is edited (checkboxes) if the importer is the same
|
||||
for (const PropertyInfo &E : params->properties) {
|
||||
if (params->checked.has(E.name)) {
|
||||
config->set_value("params", E.name, params->values[E.name]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//override entirely
|
||||
config->set_value("remap", "importer", importer_name);
|
||||
if (config->has_section("params")) {
|
||||
config->erase_section("params");
|
||||
}
|
||||
|
||||
for (const PropertyInfo &E : params->properties) {
|
||||
config->set_value("params", E.name, params->values[E.name]);
|
||||
}
|
||||
}
|
||||
|
||||
//handle group file
|
||||
Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name);
|
||||
ERR_CONTINUE(importer.is_null());
|
||||
String group_file_property = importer->get_option_group_file();
|
||||
if (!group_file_property.is_empty()) {
|
||||
//can import from a group (as in, atlas)
|
||||
ERR_CONTINUE(!params->values.has(group_file_property));
|
||||
String group_file = params->values[group_file_property];
|
||||
config->set_value("remap", "group_file", group_file);
|
||||
} else {
|
||||
config->set_value("remap", "group_file", Variant()); //clear group file if unused
|
||||
}
|
||||
|
||||
} else {
|
||||
//set to no import
|
||||
config->clear();
|
||||
if (params->skip) {
|
||||
config->set_value("remap", "importer", "skip");
|
||||
} else {
|
||||
config->set_value("remap", "importer", "keep");
|
||||
}
|
||||
}
|
||||
|
||||
config->save(params->paths[i] + ".import");
|
||||
}
|
||||
|
||||
EditorFileSystem::get_singleton()->reimport_files(params->paths);
|
||||
EditorFileSystem::get_singleton()->emit_signal(SNAME("filesystem_changed")); //it changed, so force emitting the signal
|
||||
|
||||
_set_dirty(false);
|
||||
}
|
||||
|
||||
void ImportDock::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
|
||||
if (EditorThemeManager::is_generated_theme_outdated()) {
|
||||
imported->add_theme_style_override(CoreStringName(normal), get_theme_stylebox(CoreStringName(normal), SNAME("LineEdit")));
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
import_opts->edit(params);
|
||||
label_warning->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDock::_property_edited(const StringName &p_prop) {
|
||||
_set_dirty(true);
|
||||
}
|
||||
|
||||
void ImportDock::_set_dirty(bool p_dirty) {
|
||||
if (p_dirty) {
|
||||
// Add a dirty marker to notify the user that they should reimport the selected resource to see changes.
|
||||
import->set_text(TTR("Reimport") + " (*)");
|
||||
import->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
|
||||
import->set_tooltip_text(TTRC("You have pending changes that haven't been applied yet. Click Reimport to apply changes made to the import options.\nSelecting another resource in the FileSystem dock without clicking Reimport first will discard changes made in the Import dock."));
|
||||
} else {
|
||||
// Remove the dirty marker on the Reimport button.
|
||||
import->set_text(TTRC("Reimport"));
|
||||
import->remove_theme_color_override(SceneStringName(font_color));
|
||||
import->set_tooltip_text("");
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDock::_property_toggled(const StringName &p_prop, bool p_checked) {
|
||||
if (p_checked) {
|
||||
params->checked.insert(p_prop);
|
||||
} else {
|
||||
params->checked.erase(p_prop);
|
||||
}
|
||||
}
|
||||
|
||||
void ImportDock::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_reimport"), &ImportDock::_reimport);
|
||||
}
|
||||
|
||||
void ImportDock::initialize_import_options() const {
|
||||
ERR_FAIL_COND(!import_opts || !params);
|
||||
|
||||
import_opts->edit(params);
|
||||
}
|
||||
|
||||
ImportDock::ImportDock() {
|
||||
singleton = this;
|
||||
set_name("Import");
|
||||
|
||||
content = memnew(VBoxContainer);
|
||||
content->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
add_child(content);
|
||||
content->hide();
|
||||
|
||||
imported = memnew(Label);
|
||||
imported->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
imported->add_theme_style_override(CoreStringName(normal), EditorNode::get_singleton()->get_editor_theme()->get_stylebox(CoreStringName(normal), SNAME("LineEdit")));
|
||||
imported->set_clip_text(true);
|
||||
content->add_child(imported);
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
content->add_margin_child(TTRC("Import As:"), hb);
|
||||
import_as = memnew(OptionButton);
|
||||
import_as->set_disabled(true);
|
||||
import_as->set_fit_to_longest_item(false);
|
||||
import_as->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
|
||||
import_as->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
import_as->connect(SceneStringName(item_selected), callable_mp(this, &ImportDock::_importer_selected));
|
||||
import_as->set_accessibility_name(TTRC("Import As:"));
|
||||
hb->add_child(import_as);
|
||||
import_as->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
preset = memnew(MenuButton);
|
||||
preset->set_text(TTRC("Preset"));
|
||||
preset->set_disabled(true);
|
||||
preset->get_popup()->connect("index_pressed", callable_mp(this, &ImportDock::_preset_selected));
|
||||
hb->add_child(preset);
|
||||
|
||||
import_opts = memnew(EditorInspector);
|
||||
content->add_child(import_opts);
|
||||
import_opts->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
import_opts->connect("property_edited", callable_mp(this, &ImportDock::_property_edited));
|
||||
import_opts->connect("property_toggled", callable_mp(this, &ImportDock::_property_toggled));
|
||||
// Make it possible to display tooltips stored in the XML class reference.
|
||||
// The object name is set when the importer changes in `_update_options()`.
|
||||
import_opts->set_use_doc_hints(true);
|
||||
|
||||
hb = memnew(HBoxContainer);
|
||||
content->add_child(hb);
|
||||
import = memnew(Button);
|
||||
import->set_text(TTRC("Reimport"));
|
||||
import->set_disabled(true);
|
||||
import->connect(SceneStringName(pressed), callable_mp(this, &ImportDock::_reimport_pressed));
|
||||
advanced_spacer = hb->add_spacer();
|
||||
advanced = memnew(Button);
|
||||
advanced->set_text(TTRC("Advanced..."));
|
||||
hb->add_child(advanced);
|
||||
hb->add_spacer();
|
||||
hb->add_child(import);
|
||||
hb->add_spacer();
|
||||
|
||||
advanced->hide();
|
||||
advanced_spacer->hide();
|
||||
advanced->connect(SceneStringName(pressed), callable_mp(this, &ImportDock::_advanced_options));
|
||||
|
||||
reimport_confirm = memnew(ConfirmationDialog);
|
||||
content->add_child(reimport_confirm);
|
||||
reimport_confirm->connect(SceneStringName(confirmed), callable_mp(this, &ImportDock::_reimport_and_cleanup));
|
||||
|
||||
VBoxContainer *vbc_confirm = memnew(VBoxContainer());
|
||||
cleanup_warning = memnew(Label(TTRC("The imported resource is currently loaded. All instances will be replaced and undo history will be cleared.")));
|
||||
vbc_confirm->add_child(cleanup_warning);
|
||||
label_warning = memnew(Label(TTRC("WARNING: Assets exist that use this resource. They may stop loading properly after changing type.")));
|
||||
vbc_confirm->add_child(label_warning);
|
||||
reimport_confirm->add_child(vbc_confirm);
|
||||
|
||||
params = memnew(ImportDockParameters);
|
||||
|
||||
select_a_resource = memnew(Label);
|
||||
select_a_resource->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
select_a_resource->set_text(TTRC("Select a resource file in the filesystem or in the inspector to adjust import settings."));
|
||||
select_a_resource->set_autowrap_mode(TextServer::AUTOWRAP_WORD);
|
||||
select_a_resource->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
|
||||
select_a_resource->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
select_a_resource->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
select_a_resource->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
|
||||
add_child(select_a_resource);
|
||||
}
|
||||
|
||||
ImportDock::~ImportDock() {
|
||||
singleton = nullptr;
|
||||
memdelete(params);
|
||||
}
|
109
editor/docks/import_dock.h
Normal file
109
editor/docks/import_dock.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/**************************************************************************/
|
||||
/* import_dock.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/config_file.h"
|
||||
#include "core/io/resource_importer.h"
|
||||
#include "editor/file_system/editor_file_system.h"
|
||||
#include "editor/inspector/editor_inspector.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
|
||||
class ImportDockParameters;
|
||||
class ImportDock : public VBoxContainer {
|
||||
GDCLASS(ImportDock, VBoxContainer);
|
||||
|
||||
Label *imported = nullptr;
|
||||
OptionButton *import_as = nullptr;
|
||||
MenuButton *preset = nullptr;
|
||||
EditorInspector *import_opts = nullptr;
|
||||
|
||||
List<PropertyInfo> properties;
|
||||
HashMap<StringName, Variant> property_values;
|
||||
|
||||
ConfirmationDialog *reimport_confirm = nullptr;
|
||||
Label *cleanup_warning = nullptr;
|
||||
Label *label_warning = nullptr;
|
||||
Button *import = nullptr;
|
||||
List<String> need_cleanup;
|
||||
|
||||
Control *advanced_spacer = nullptr;
|
||||
Button *advanced = nullptr;
|
||||
|
||||
ImportDockParameters *params = nullptr;
|
||||
|
||||
VBoxContainer *content = nullptr;
|
||||
Label *select_a_resource = nullptr;
|
||||
|
||||
void _preset_selected(int p_idx);
|
||||
void _importer_selected(int i_idx);
|
||||
void _update_options(const String &p_path, const Ref<ConfigFile> &p_config = Ref<ConfigFile>());
|
||||
void _update_preset_menu();
|
||||
void _add_keep_import_option(const String &p_importer_name);
|
||||
|
||||
void _property_edited(const StringName &p_prop);
|
||||
void _property_toggled(const StringName &p_prop, bool p_checked);
|
||||
void _set_dirty(bool p_dirty);
|
||||
void _reimport_pressed();
|
||||
void _reimport_attempt();
|
||||
void _reimport_and_cleanup();
|
||||
void _reimport();
|
||||
|
||||
void _advanced_options();
|
||||
enum {
|
||||
ITEM_SET_AS_DEFAULT = 100,
|
||||
ITEM_LOAD_DEFAULT,
|
||||
ITEM_CLEAR_DEFAULT,
|
||||
};
|
||||
|
||||
private:
|
||||
static ImportDock *singleton;
|
||||
|
||||
public:
|
||||
static ImportDock *get_singleton() { return singleton; }
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void set_edit_path(const String &p_path);
|
||||
void set_edit_multiple_paths(const Vector<String> &p_paths);
|
||||
void reimport_resources(const Vector<String> &p_paths);
|
||||
void initialize_import_options() const;
|
||||
void clear();
|
||||
|
||||
ImportDock();
|
||||
~ImportDock();
|
||||
};
|
883
editor/docks/inspector_dock.cpp
Normal file
883
editor/docks/inspector_dock.cpp
Normal file
@@ -0,0 +1,883 @@
|
||||
/**************************************************************************/
|
||||
/* inspector_dock.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "inspector_dock.h"
|
||||
|
||||
#include "editor/debugger/editor_debugger_inspector.h"
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/docks/filesystem_dock.h"
|
||||
#include "editor/editor_main_screen.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/gui/editor_file_dialog.h"
|
||||
#include "editor/gui/editor_object_selector.h"
|
||||
#include "editor/script/script_editor_plugin.h"
|
||||
#include "editor/settings/editor_settings.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
|
||||
InspectorDock *InspectorDock::singleton = nullptr;
|
||||
|
||||
void InspectorDock::_prepare_menu() {
|
||||
PopupMenu *menu = object_menu->get_popup();
|
||||
for (int i = EditorPropertyNameProcessor::STYLE_RAW; i <= EditorPropertyNameProcessor::STYLE_LOCALIZED; i++) {
|
||||
menu->set_item_checked(menu->get_item_index(PROPERTY_NAME_STYLE_RAW + i), i == property_name_style);
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDock::_menu_option(int p_option) {
|
||||
_menu_option_confirm(p_option, false);
|
||||
}
|
||||
|
||||
void InspectorDock::_menu_confirm_current() {
|
||||
_menu_option_confirm(current_option, true);
|
||||
}
|
||||
|
||||
void InspectorDock::_menu_option_confirm(int p_option, bool p_confirmed) {
|
||||
if (!p_confirmed) {
|
||||
current_option = p_option;
|
||||
}
|
||||
|
||||
switch (p_option) {
|
||||
case EXPAND_ALL: {
|
||||
_menu_expandall();
|
||||
} break;
|
||||
case COLLAPSE_ALL: {
|
||||
_menu_collapseall();
|
||||
} break;
|
||||
case EXPAND_REVERTABLE: {
|
||||
_menu_expand_revertable();
|
||||
} break;
|
||||
|
||||
case RESOURCE_SAVE: {
|
||||
_save_resource(false);
|
||||
} break;
|
||||
case RESOURCE_SAVE_AS: {
|
||||
_save_resource(true);
|
||||
} break;
|
||||
|
||||
case RESOURCE_MAKE_BUILT_IN: {
|
||||
_unref_resource();
|
||||
} break;
|
||||
case RESOURCE_COPY: {
|
||||
_copy_resource();
|
||||
} break;
|
||||
case RESOURCE_EDIT_CLIPBOARD: {
|
||||
_paste_resource();
|
||||
} break;
|
||||
case RESOURCE_SHOW_IN_FILESYSTEM: {
|
||||
Ref<Resource> current_res = _get_current_resource();
|
||||
ERR_FAIL_COND(current_res.is_null());
|
||||
FileSystemDock::get_singleton()->navigate_to_path(current_res->get_path());
|
||||
} break;
|
||||
|
||||
case OBJECT_REQUEST_HELP: {
|
||||
if (current) {
|
||||
EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT);
|
||||
emit_signal(SNAME("request_help"), current->get_class());
|
||||
}
|
||||
} break;
|
||||
|
||||
case OBJECT_COPY_PARAMS: {
|
||||
editor_data->apply_changes_in_editors();
|
||||
if (current) {
|
||||
editor_data->copy_object_params(current);
|
||||
}
|
||||
} break;
|
||||
|
||||
case OBJECT_PASTE_PARAMS: {
|
||||
editor_data->apply_changes_in_editors();
|
||||
if (current) {
|
||||
editor_data->paste_object_params(current);
|
||||
}
|
||||
} break;
|
||||
|
||||
case OBJECT_UNIQUE_RESOURCES: {
|
||||
if (!p_confirmed) {
|
||||
Vector<String> resource_propnames;
|
||||
|
||||
if (current) {
|
||||
List<PropertyInfo> props;
|
||||
current->get_property_list(&props);
|
||||
|
||||
for (const PropertyInfo &property : props) {
|
||||
if (!(property.usage & PROPERTY_USAGE_STORAGE)) {
|
||||
continue;
|
||||
}
|
||||
if (property.usage & PROPERTY_USAGE_NEVER_DUPLICATE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Variant v = current->get(property.name);
|
||||
Ref<RefCounted> ref = v;
|
||||
Ref<Resource> res = ref;
|
||||
if (v.is_ref_counted() && ref.is_valid() && res.is_valid()) {
|
||||
// Valid resource which would be duplicated if action is confirmed.
|
||||
resource_propnames.append(property.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unique_resources_list_tree->clear();
|
||||
if (resource_propnames.size()) {
|
||||
const EditorPropertyNameProcessor::Style name_style = inspector->get_property_name_style();
|
||||
|
||||
TreeItem *root = unique_resources_list_tree->create_item();
|
||||
for (const String &E : resource_propnames) {
|
||||
const String propname = EditorPropertyNameProcessor::get_singleton()->process_name(E, name_style);
|
||||
|
||||
TreeItem *ti = unique_resources_list_tree->create_item(root);
|
||||
ti->set_text(0, propname);
|
||||
}
|
||||
|
||||
unique_resources_label->set_text(TTRC("The following resources will be duplicated and embedded within this resource/object."));
|
||||
unique_resources_confirmation->popup_centered();
|
||||
} else {
|
||||
current_option = -1;
|
||||
unique_resources_label->set_text(TTRC("This object has no resources."));
|
||||
unique_resources_confirmation->popup_centered();
|
||||
}
|
||||
} else {
|
||||
editor_data->apply_changes_in_editors();
|
||||
|
||||
if (current) {
|
||||
List<PropertyInfo> props;
|
||||
current->get_property_list(&props);
|
||||
HashMap<Ref<Resource>, Ref<Resource>> duplicates;
|
||||
for (const PropertyInfo &prop_info : props) {
|
||||
if (!(prop_info.usage & PROPERTY_USAGE_STORAGE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Variant v = current->get(prop_info.name);
|
||||
if (v.is_ref_counted()) {
|
||||
Ref<RefCounted> ref = v;
|
||||
if (ref.is_valid()) {
|
||||
Ref<Resource> res = ref;
|
||||
if (res.is_valid()) {
|
||||
if (!duplicates.has(res)) {
|
||||
duplicates[res] = res->duplicate();
|
||||
}
|
||||
res = duplicates[res];
|
||||
|
||||
current->set(prop_info.name, res);
|
||||
get_inspector_singleton()->update_property(prop_info.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int history_id = EditorUndoRedoManager::get_singleton()->get_history_id_for_object(current);
|
||||
EditorUndoRedoManager::get_singleton()->clear_history(history_id);
|
||||
|
||||
EditorNode::get_singleton()->edit_item(current, inspector);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case PROPERTY_NAME_STYLE_RAW:
|
||||
case PROPERTY_NAME_STYLE_CAPITALIZED:
|
||||
case PROPERTY_NAME_STYLE_LOCALIZED: {
|
||||
property_name_style = (EditorPropertyNameProcessor::Style)(p_option - PROPERTY_NAME_STYLE_RAW);
|
||||
inspector->set_property_name_style(property_name_style);
|
||||
} break;
|
||||
|
||||
default: {
|
||||
if (p_option >= OBJECT_METHOD_BASE) {
|
||||
ERR_FAIL_NULL(current);
|
||||
|
||||
int idx = p_option - OBJECT_METHOD_BASE;
|
||||
|
||||
List<MethodInfo> methods;
|
||||
current->get_method_list(&methods);
|
||||
|
||||
ERR_FAIL_INDEX(idx, methods.size());
|
||||
String name = methods.get(idx).name;
|
||||
|
||||
current->call(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDock::_new_resource() {
|
||||
new_resource_dialog->popup_create(true);
|
||||
}
|
||||
|
||||
void InspectorDock::_load_resource(const String &p_type) {
|
||||
load_resource_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
|
||||
|
||||
List<String> extensions;
|
||||
ResourceLoader::get_recognized_extensions_for_type(p_type, &extensions);
|
||||
|
||||
load_resource_dialog->clear_filters();
|
||||
for (const String &extension : extensions) {
|
||||
load_resource_dialog->add_filter("*." + extension, extension.to_upper());
|
||||
}
|
||||
|
||||
const Vector<String> textfile_ext = ((String)(EDITOR_GET("docks/filesystem/textfile_extensions"))).split(",", false);
|
||||
for (int i = 0; i < textfile_ext.size(); i++) {
|
||||
load_resource_dialog->add_filter("*." + textfile_ext[i], textfile_ext[i].to_upper());
|
||||
}
|
||||
|
||||
load_resource_dialog->popup_file_dialog();
|
||||
}
|
||||
|
||||
void InspectorDock::_resource_file_selected(const String &p_file) {
|
||||
Ref<Resource> res;
|
||||
if (ResourceLoader::exists(p_file, "")) {
|
||||
res = ResourceLoader::load(p_file);
|
||||
} else {
|
||||
const Vector<String> textfile_ext = ((String)(EDITOR_GET("docks/filesystem/textfile_extensions"))).split(",", false);
|
||||
if (textfile_ext.has(p_file.get_extension())) {
|
||||
res = ScriptEditor::get_singleton()->open_file(p_file);
|
||||
}
|
||||
}
|
||||
|
||||
if (res.is_null()) {
|
||||
info_dialog->set_text(TTRC("Failed to load resource."));
|
||||
return;
|
||||
};
|
||||
|
||||
EditorNode::get_singleton()->push_item(res.operator->());
|
||||
}
|
||||
|
||||
void InspectorDock::_save_resource(bool save_as) {
|
||||
Ref<Resource> current_res = _get_current_resource();
|
||||
ERR_FAIL_COND(current_res.is_null());
|
||||
|
||||
if (save_as) {
|
||||
EditorNode::get_singleton()->save_resource_as(current_res);
|
||||
} else {
|
||||
EditorNode::get_singleton()->save_resource(current_res);
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDock::_unref_resource() {
|
||||
Ref<Resource> current_res = _get_current_resource();
|
||||
ERR_FAIL_COND(current_res.is_null());
|
||||
current_res->set_path("");
|
||||
EditorNode::get_singleton()->edit_current();
|
||||
}
|
||||
|
||||
void InspectorDock::_copy_resource() {
|
||||
Ref<Resource> current_res = _get_current_resource();
|
||||
ERR_FAIL_COND(current_res.is_null());
|
||||
EditorSettings::get_singleton()->set_resource_clipboard(current_res);
|
||||
}
|
||||
|
||||
void InspectorDock::_paste_resource() {
|
||||
Ref<Resource> r = EditorSettings::get_singleton()->get_resource_clipboard();
|
||||
if (r.is_valid()) {
|
||||
EditorNode::get_singleton()->push_item(EditorSettings::get_singleton()->get_resource_clipboard().ptr(), String());
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDock::_prepare_resource_extra_popup() {
|
||||
Ref<Resource> r = EditorSettings::get_singleton()->get_resource_clipboard();
|
||||
PopupMenu *popup = resource_extra_button->get_popup();
|
||||
popup->set_item_disabled(popup->get_item_index(RESOURCE_EDIT_CLIPBOARD), r.is_null());
|
||||
|
||||
Ref<Resource> current_res = _get_current_resource();
|
||||
popup->set_item_disabled(popup->get_item_index(RESOURCE_SHOW_IN_FILESYSTEM), current_res.is_null() || current_res->is_built_in());
|
||||
}
|
||||
|
||||
Ref<Resource> InspectorDock::_get_current_resource() const {
|
||||
ObjectID current_id = EditorNode::get_singleton()->get_editor_selection_history()->get_current();
|
||||
Object *current_obj = current_id.is_valid() ? ObjectDB::get_instance(current_id) : nullptr;
|
||||
return Ref<Resource>(Object::cast_to<Resource>(current_obj));
|
||||
}
|
||||
|
||||
void InspectorDock::_prepare_history() {
|
||||
EditorSelectionHistory *editor_history = EditorNode::get_singleton()->get_editor_selection_history();
|
||||
editor_history->cleanup_history();
|
||||
|
||||
int history_to = MAX(0, editor_history->get_history_len() - 25);
|
||||
|
||||
history_menu->get_popup()->clear();
|
||||
|
||||
HashSet<ObjectID> already;
|
||||
for (int i = editor_history->get_history_len() - 1; i >= history_to; i--) {
|
||||
ObjectID id = editor_history->get_history_obj(i);
|
||||
Object *obj = ObjectDB::get_instance(id);
|
||||
if (!obj || already.has(id)) {
|
||||
if (history_to > 0) {
|
||||
history_to--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
already.insert(id);
|
||||
|
||||
Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(obj, "Object");
|
||||
|
||||
String text;
|
||||
if (obj->has_method("_get_editor_name")) {
|
||||
text = obj->call("_get_editor_name");
|
||||
} else if (Object::cast_to<Resource>(obj)) {
|
||||
Resource *r = Object::cast_to<Resource>(obj);
|
||||
if (r->get_path().is_resource_file()) {
|
||||
text = r->get_path().get_file();
|
||||
} else if (!r->get_name().is_empty()) {
|
||||
text = r->get_name();
|
||||
} else {
|
||||
text = r->get_class();
|
||||
}
|
||||
} else if (Object::cast_to<Node>(obj)) {
|
||||
text = Object::cast_to<Node>(obj)->get_name();
|
||||
} else if (obj->is_class("EditorDebuggerRemoteObjects")) {
|
||||
text = obj->call("get_title");
|
||||
} else {
|
||||
text = obj->get_class();
|
||||
}
|
||||
|
||||
if (i == editor_history->get_history_pos() && current) {
|
||||
text += " " + TTR("(Current)");
|
||||
}
|
||||
history_menu->get_popup()->add_icon_item(icon, text, i);
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDock::_select_history(int p_idx) {
|
||||
// Push it to the top, it is not correct, but it's more useful.
|
||||
ObjectID id = EditorNode::get_singleton()->get_editor_selection_history()->get_history_obj(p_idx);
|
||||
Object *obj = ObjectDB::get_instance(id);
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
EditorNode::get_singleton()->push_item(obj);
|
||||
|
||||
if (const EditorDebuggerRemoteObjects *robjs = Object::cast_to<EditorDebuggerRemoteObjects>(obj)) {
|
||||
EditorDebuggerNode::get_singleton()->set_remote_selection(robjs->remote_object_ids.duplicate());
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDock::_resource_created() {
|
||||
Variant c = new_resource_dialog->instantiate_selected();
|
||||
|
||||
ERR_FAIL_COND(!c);
|
||||
Resource *r = Object::cast_to<Resource>(c);
|
||||
ERR_FAIL_NULL(r);
|
||||
|
||||
EditorNode::get_singleton()->push_item(r);
|
||||
}
|
||||
|
||||
void InspectorDock::_resource_selected(const Ref<Resource> &p_res, const String &p_property) {
|
||||
if (p_res.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<Resource> r = p_res;
|
||||
EditorNode::get_singleton()->push_item(r.operator->(), p_property);
|
||||
}
|
||||
|
||||
void InspectorDock::_files_moved(const String &p_old_file, const String &p_new_file) {
|
||||
// Because only the file name is shown, we care about changes on the file name.
|
||||
if (p_old_file.get_file() == p_new_file.get_file()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectID current_id = EditorNode::get_singleton()->get_editor_selection_history()->get_current();
|
||||
Ref<Resource> res(current_id.is_valid() ? ObjectDB::get_instance(current_id) : nullptr);
|
||||
// We only care about updating the path if the current object is the one being renamed.
|
||||
if (res.is_valid() && p_old_file == res->get_path()) {
|
||||
res->set_path(p_new_file);
|
||||
object_selector->update_path();
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDock::_edit_forward() {
|
||||
if (EditorNode::get_singleton()->get_editor_selection_history()->next()) {
|
||||
EditorNode::get_singleton()->edit_current();
|
||||
|
||||
if (const EditorDebuggerRemoteObjects *robjs = Object::cast_to<EditorDebuggerRemoteObjects>(current)) {
|
||||
EditorDebuggerNode::get_singleton()->set_remote_selection(robjs->remote_object_ids.duplicate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDock::_edit_back() {
|
||||
EditorSelectionHistory *editor_history = EditorNode::get_singleton()->get_editor_selection_history();
|
||||
if ((current && editor_history->previous()) || editor_history->get_path_size() == 1) {
|
||||
EditorNode::get_singleton()->edit_current();
|
||||
|
||||
if (const EditorDebuggerRemoteObjects *robjs = Object::cast_to<EditorDebuggerRemoteObjects>(current)) {
|
||||
EditorDebuggerNode::get_singleton()->set_remote_selection(robjs->remote_object_ids.duplicate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDock::_menu_collapseall() {
|
||||
inspector->collapse_all_folding();
|
||||
}
|
||||
|
||||
void InspectorDock::_menu_expandall() {
|
||||
inspector->expand_all_folding();
|
||||
}
|
||||
|
||||
void InspectorDock::_menu_expand_revertable() {
|
||||
inspector->expand_revertable();
|
||||
}
|
||||
|
||||
void InspectorDock::_info_pressed() {
|
||||
info_dialog->popup_centered();
|
||||
}
|
||||
|
||||
Container *InspectorDock::get_addon_area() {
|
||||
return this;
|
||||
}
|
||||
|
||||
void InspectorDock::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_TRANSLATION_CHANGED: {
|
||||
update(current);
|
||||
[[fallthrough]];
|
||||
}
|
||||
case NOTIFICATION_THEME_CHANGED:
|
||||
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
|
||||
resource_new_button->set_button_icon(get_editor_theme_icon(SNAME("New")));
|
||||
resource_load_button->set_button_icon(get_editor_theme_icon(SNAME("Load")));
|
||||
resource_save_button->set_button_icon(get_editor_theme_icon(SNAME("Save")));
|
||||
resource_extra_button->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
|
||||
open_docs_button->set_button_icon(get_editor_theme_icon(SNAME("HelpSearch")));
|
||||
|
||||
PopupMenu *resource_extra_popup = resource_extra_button->get_popup();
|
||||
resource_extra_popup->set_item_icon(resource_extra_popup->get_item_index(RESOURCE_EDIT_CLIPBOARD), get_editor_theme_icon(SNAME("ActionPaste")));
|
||||
resource_extra_popup->set_item_icon(resource_extra_popup->get_item_index(RESOURCE_COPY), get_editor_theme_icon(SNAME("ActionCopy")));
|
||||
resource_extra_popup->set_item_icon(resource_extra_popup->get_item_index(RESOURCE_SHOW_IN_FILESYSTEM), get_editor_theme_icon(SNAME("ShowInFileSystem")));
|
||||
|
||||
if (is_layout_rtl()) {
|
||||
backward_button->set_button_icon(get_editor_theme_icon(SNAME("Forward")));
|
||||
forward_button->set_button_icon(get_editor_theme_icon(SNAME("Back")));
|
||||
} else {
|
||||
backward_button->set_button_icon(get_editor_theme_icon(SNAME("Back")));
|
||||
forward_button->set_button_icon(get_editor_theme_icon(SNAME("Forward")));
|
||||
}
|
||||
|
||||
const int icon_width = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
|
||||
history_menu->get_popup()->add_theme_constant_override("icon_max_width", icon_width);
|
||||
|
||||
history_menu->set_button_icon(get_editor_theme_icon(SNAME("History")));
|
||||
object_menu->set_button_icon(get_editor_theme_icon(SNAME("Tools")));
|
||||
search->set_right_icon(get_editor_theme_icon(SNAME("Search")));
|
||||
if (info_is_warning) {
|
||||
info->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));
|
||||
info->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
|
||||
} else {
|
||||
info->set_button_icon(get_editor_theme_icon(SNAME("NodeInfo")));
|
||||
info->add_theme_color_override(SceneStringName(font_color), get_theme_color(SceneStringName(font_color), EditorStringName(Editor)));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDock::_bind_methods() {
|
||||
ClassDB::bind_method("store_script_properties", &InspectorDock::store_script_properties);
|
||||
ClassDB::bind_method("apply_script_properties", &InspectorDock::apply_script_properties);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("request_help"));
|
||||
}
|
||||
|
||||
void InspectorDock::edit_resource(const Ref<Resource> &p_resource) {
|
||||
_resource_selected(p_resource, "");
|
||||
}
|
||||
|
||||
void InspectorDock::open_resource(const String &p_type) {
|
||||
_load_resource(p_type);
|
||||
}
|
||||
|
||||
void InspectorDock::set_info(const String &p_button_text, const String &p_message, bool p_is_warning) {
|
||||
info->hide();
|
||||
info_is_warning = p_is_warning;
|
||||
|
||||
if (info_is_warning) {
|
||||
info->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));
|
||||
info->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
|
||||
} else {
|
||||
info->set_button_icon(get_editor_theme_icon(SNAME("NodeInfo")));
|
||||
info->add_theme_color_override(SceneStringName(font_color), get_theme_color(SceneStringName(font_color), EditorStringName(Editor)));
|
||||
}
|
||||
|
||||
if (!p_button_text.is_empty() && !p_message.is_empty()) {
|
||||
info->show();
|
||||
info->set_text(p_button_text);
|
||||
info_dialog->set_text(p_message);
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDock::clear() {
|
||||
}
|
||||
|
||||
void InspectorDock::update(Object *p_object) {
|
||||
EditorSelectionHistory *editor_history = EditorNode::get_singleton()->get_editor_selection_history();
|
||||
|
||||
backward_button->set_disabled(editor_history->is_at_beginning());
|
||||
forward_button->set_disabled(editor_history->is_at_end());
|
||||
|
||||
history_menu->set_disabled(true);
|
||||
if (editor_history->get_history_len() > 0) {
|
||||
history_menu->set_disabled(false);
|
||||
}
|
||||
object_selector->update_path();
|
||||
|
||||
current = p_object;
|
||||
|
||||
const bool is_object = p_object != nullptr;
|
||||
const bool is_resource = is_object && p_object->is_class("Resource");
|
||||
const bool is_text_file = is_object && p_object->is_class("TextFile");
|
||||
const bool is_node = is_object && p_object->is_class("Node");
|
||||
|
||||
object_menu->set_disabled(!is_object || is_text_file);
|
||||
search->set_editable(is_object && !is_text_file);
|
||||
resource_save_button->set_disabled(!is_resource || is_text_file);
|
||||
open_docs_button->set_disabled(is_text_file || (!is_resource && !is_node));
|
||||
|
||||
PopupMenu *resource_extra_popup = resource_extra_button->get_popup();
|
||||
resource_extra_popup->set_item_disabled(resource_extra_popup->get_item_index(RESOURCE_COPY), !is_resource || is_text_file);
|
||||
resource_extra_popup->set_item_disabled(resource_extra_popup->get_item_index(RESOURCE_MAKE_BUILT_IN), !is_resource || is_text_file);
|
||||
|
||||
if (!is_object || is_text_file) {
|
||||
info->hide();
|
||||
object_selector->clear_path();
|
||||
return;
|
||||
}
|
||||
|
||||
object_selector->enable_path();
|
||||
|
||||
PopupMenu *p = object_menu->get_popup();
|
||||
|
||||
p->clear();
|
||||
p->add_icon_shortcut(get_editor_theme_icon(SNAME("GuiTreeArrowDown")), ED_SHORTCUT("property_editor/expand_all", TTRC("Expand All")), EXPAND_ALL);
|
||||
p->add_icon_shortcut(get_editor_theme_icon(SNAME("GuiTreeArrowRight")), ED_SHORTCUT("property_editor/collapse_all", TTRC("Collapse All")), COLLAPSE_ALL);
|
||||
// Calling it 'revertable' internally, because that's what the implementation is based on, but labeling it as 'non-default' because that's more user friendly, even if not 100% accurate.
|
||||
p->add_shortcut(ED_SHORTCUT("property_editor/expand_revertable", TTRC("Expand Non-Default")), EXPAND_REVERTABLE);
|
||||
|
||||
p->add_separator(TTRC("Property Name Style"));
|
||||
p->add_radio_check_item(vformat(TTR("Raw (e.g. \"%s\")"), "z_index"), PROPERTY_NAME_STYLE_RAW);
|
||||
p->add_radio_check_item(vformat(TTR("Capitalized (e.g. \"%s\")"), "Z Index"), PROPERTY_NAME_STYLE_CAPITALIZED);
|
||||
// TRANSLATORS: "Z Index" should match the existing translated CanvasItem property name in the current language you're working on.
|
||||
p->add_radio_check_item(TTR("Localized (e.g. \"Z Index\")"), PROPERTY_NAME_STYLE_LOCALIZED);
|
||||
|
||||
if (!EditorPropertyNameProcessor::is_localization_available()) {
|
||||
const int index = p->get_item_index(PROPERTY_NAME_STYLE_LOCALIZED);
|
||||
p->set_item_disabled(index, true);
|
||||
p->set_item_tooltip(index, TTRC("Localization not available for current language."));
|
||||
}
|
||||
|
||||
p->add_separator();
|
||||
p->add_shortcut(ED_SHORTCUT("property_editor/copy_params", TTRC("Copy Properties")), OBJECT_COPY_PARAMS);
|
||||
p->add_shortcut(ED_SHORTCUT("property_editor/paste_params", TTRC("Paste Properties")), OBJECT_PASTE_PARAMS);
|
||||
|
||||
if (is_resource || is_node) {
|
||||
p->add_separator();
|
||||
p->add_shortcut(ED_SHORTCUT("property_editor/make_subresources_unique", TTRC("Make Sub-Resources Unique")), OBJECT_UNIQUE_RESOURCES);
|
||||
}
|
||||
|
||||
List<MethodInfo> methods;
|
||||
p_object->get_method_list(&methods);
|
||||
|
||||
if (!methods.is_empty()) {
|
||||
bool found = false;
|
||||
List<MethodInfo>::Element *I = methods.front();
|
||||
int i = 0;
|
||||
while (I) {
|
||||
if (I->get().flags & METHOD_FLAG_EDITOR) {
|
||||
if (!found) {
|
||||
p->add_separator();
|
||||
found = true;
|
||||
}
|
||||
p->add_item(I->get().name.capitalize(), OBJECT_METHOD_BASE + i);
|
||||
}
|
||||
i++;
|
||||
I = I->next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDock::go_back() {
|
||||
_edit_back();
|
||||
}
|
||||
|
||||
EditorPropertyNameProcessor::Style InspectorDock::get_property_name_style() const {
|
||||
return property_name_style;
|
||||
}
|
||||
|
||||
void InspectorDock::store_script_properties(Object *p_object) {
|
||||
ERR_FAIL_NULL(p_object);
|
||||
ScriptInstance *si = p_object->get_script_instance();
|
||||
if (!si) {
|
||||
return;
|
||||
}
|
||||
si->get_property_state(stored_properties);
|
||||
}
|
||||
|
||||
void InspectorDock::apply_script_properties(Object *p_object) {
|
||||
ERR_FAIL_NULL(p_object);
|
||||
ScriptInstance *si = p_object->get_script_instance();
|
||||
if (!si) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<PropertyInfo> properties;
|
||||
si->get_property_list(&properties);
|
||||
|
||||
for (const Pair<StringName, Variant> &E : stored_properties) {
|
||||
Variant current_prop;
|
||||
if (si->get(E.first, current_prop) && current_prop.get_type() == E.second.get_type()) {
|
||||
si->set(E.first, E.second);
|
||||
} else if (E.second.get_type() == Variant::OBJECT) {
|
||||
for (const PropertyInfo &pi : properties) {
|
||||
if (E.first != pi.name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pi.type != Variant::OBJECT) {
|
||||
break;
|
||||
}
|
||||
|
||||
Object *p_property_object = E.second;
|
||||
|
||||
if (p_property_object->is_class(pi.hint_string)) {
|
||||
si->set(E.first, E.second);
|
||||
break;
|
||||
}
|
||||
|
||||
Ref<Script> base_script = p_property_object->get_script();
|
||||
while (base_script.is_valid()) {
|
||||
if (base_script->get_global_name() == pi.hint_string) {
|
||||
si->set(E.first, E.second);
|
||||
break;
|
||||
}
|
||||
base_script = base_script->get_base_script();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stored_properties.clear();
|
||||
}
|
||||
|
||||
void InspectorDock::shortcut_input(const Ref<InputEvent> &p_event) {
|
||||
ERR_FAIL_COND(p_event.is_null());
|
||||
|
||||
Ref<InputEventKey> key = p_event;
|
||||
|
||||
if (key.is_null() || !key->is_pressed() || key->is_echo()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_visible() || !inspector->get_rect().has_point(inspector->get_local_mouse_position())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ED_IS_SHORTCUT("editor/open_search", p_event)) {
|
||||
search->grab_focus();
|
||||
search->select_all();
|
||||
accept_event();
|
||||
}
|
||||
}
|
||||
|
||||
InspectorDock::InspectorDock(EditorData &p_editor_data) {
|
||||
singleton = this;
|
||||
set_name("Inspector");
|
||||
|
||||
editor_data = &p_editor_data;
|
||||
|
||||
property_name_style = EditorPropertyNameProcessor::get_default_inspector_style();
|
||||
|
||||
HBoxContainer *general_options_hb = memnew(HBoxContainer);
|
||||
add_child(general_options_hb);
|
||||
|
||||
resource_new_button = memnew(Button);
|
||||
resource_new_button->set_theme_type_variation("FlatMenuButton");
|
||||
resource_new_button->set_tooltip_text(TTRC("Create a new resource in memory and edit it."));
|
||||
general_options_hb->add_child(resource_new_button);
|
||||
resource_new_button->connect(SceneStringName(pressed), callable_mp(this, &InspectorDock::_new_resource));
|
||||
resource_new_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
|
||||
|
||||
resource_load_button = memnew(Button);
|
||||
resource_load_button->set_theme_type_variation("FlatMenuButton");
|
||||
resource_load_button->set_tooltip_text(TTRC("Load an existing resource from disk and edit it."));
|
||||
general_options_hb->add_child(resource_load_button);
|
||||
resource_load_button->connect(SceneStringName(pressed), callable_mp(this, &InspectorDock::_open_resource_selector));
|
||||
resource_load_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
|
||||
|
||||
resource_save_button = memnew(MenuButton);
|
||||
resource_save_button->set_flat(false);
|
||||
resource_save_button->set_theme_type_variation("FlatMenuButton");
|
||||
resource_save_button->set_tooltip_text(TTRC("Save the currently edited resource."));
|
||||
general_options_hb->add_child(resource_save_button);
|
||||
resource_save_button->get_popup()->add_item(TTRC("Save"), RESOURCE_SAVE);
|
||||
resource_save_button->get_popup()->add_item(TTRC("Save As..."), RESOURCE_SAVE_AS);
|
||||
resource_save_button->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &InspectorDock::_menu_option));
|
||||
resource_save_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
|
||||
resource_save_button->set_disabled(true);
|
||||
|
||||
resource_extra_button = memnew(MenuButton);
|
||||
resource_extra_button->set_flat(false);
|
||||
resource_extra_button->set_theme_type_variation("FlatMenuButton");
|
||||
resource_extra_button->set_tooltip_text(TTRC("Extra resource options."));
|
||||
general_options_hb->add_child(resource_extra_button);
|
||||
resource_extra_button->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_resource_extra_popup));
|
||||
resource_extra_button->get_popup()->add_shortcut(ED_SHORTCUT("property_editor/paste_resource", TTRC("Edit Resource from Clipboard")), RESOURCE_EDIT_CLIPBOARD);
|
||||
resource_extra_button->get_popup()->add_shortcut(ED_SHORTCUT("property_editor/copy_resource", TTRC("Copy Resource")), RESOURCE_COPY);
|
||||
resource_extra_button->get_popup()->set_item_disabled(1, true);
|
||||
resource_extra_button->get_popup()->add_separator();
|
||||
resource_extra_button->get_popup()->add_shortcut(ED_SHORTCUT("property_editor/show_in_filesystem", TTRC("Show in FileSystem")), RESOURCE_SHOW_IN_FILESYSTEM);
|
||||
resource_extra_button->get_popup()->add_shortcut(ED_SHORTCUT("property_editor/unref_resource", TTRC("Make Resource Built-In")), RESOURCE_MAKE_BUILT_IN);
|
||||
resource_extra_button->get_popup()->set_item_disabled(3, true);
|
||||
resource_extra_button->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &InspectorDock::_menu_option));
|
||||
|
||||
general_options_hb->add_spacer();
|
||||
|
||||
backward_button = memnew(Button);
|
||||
backward_button->set_flat(true);
|
||||
general_options_hb->add_child(backward_button);
|
||||
backward_button->set_tooltip_text(TTRC("Go to previous edited object in history."));
|
||||
backward_button->set_disabled(true);
|
||||
backward_button->connect(SceneStringName(pressed), callable_mp(this, &InspectorDock::_edit_back));
|
||||
|
||||
forward_button = memnew(Button);
|
||||
forward_button->set_flat(true);
|
||||
general_options_hb->add_child(forward_button);
|
||||
forward_button->set_tooltip_text(TTRC("Go to next edited object in history."));
|
||||
forward_button->set_disabled(true);
|
||||
forward_button->connect(SceneStringName(pressed), callable_mp(this, &InspectorDock::_edit_forward));
|
||||
|
||||
history_menu = memnew(MenuButton);
|
||||
history_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
history_menu->set_flat(false);
|
||||
history_menu->set_theme_type_variation("FlatMenuButton");
|
||||
history_menu->set_tooltip_text(TTRC("History of recently edited objects."));
|
||||
general_options_hb->add_child(history_menu);
|
||||
history_menu->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_history));
|
||||
history_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &InspectorDock::_select_history));
|
||||
|
||||
HBoxContainer *subresource_hb = memnew(HBoxContainer);
|
||||
add_child(subresource_hb);
|
||||
object_selector = memnew(EditorObjectSelector(EditorNode::get_singleton()->get_editor_selection_history()));
|
||||
object_selector->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
subresource_hb->add_child(object_selector);
|
||||
|
||||
open_docs_button = memnew(Button);
|
||||
open_docs_button->set_theme_type_variation("FlatMenuButton");
|
||||
open_docs_button->set_disabled(true);
|
||||
open_docs_button->set_tooltip_text(TTRC("Open documentation for this object."));
|
||||
open_docs_button->set_shortcut(ED_SHORTCUT("property_editor/open_help", TTRC("Open Documentation")));
|
||||
subresource_hb->add_child(open_docs_button);
|
||||
open_docs_button->connect(SceneStringName(pressed), callable_mp(this, &InspectorDock::_menu_option).bind(OBJECT_REQUEST_HELP));
|
||||
|
||||
new_resource_dialog = memnew(CreateDialog);
|
||||
EditorNode::get_singleton()->get_gui_base()->add_child(new_resource_dialog);
|
||||
new_resource_dialog->set_base_type("Resource");
|
||||
new_resource_dialog->connect("create", callable_mp(this, &InspectorDock::_resource_created));
|
||||
|
||||
HBoxContainer *property_tools_hb = memnew(HBoxContainer);
|
||||
add_child(property_tools_hb);
|
||||
|
||||
search = memnew(LineEdit);
|
||||
search->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
search->set_placeholder(TTRC("Filter Properties"));
|
||||
search->set_clear_button_enabled(true);
|
||||
property_tools_hb->add_child(search);
|
||||
|
||||
object_menu = memnew(MenuButton);
|
||||
object_menu->set_flat(false);
|
||||
object_menu->set_theme_type_variation("FlatMenuButton");
|
||||
property_tools_hb->add_child(object_menu);
|
||||
object_menu->set_tooltip_text(TTRC("Manage object properties."));
|
||||
object_menu->get_popup()->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_menu));
|
||||
object_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &InspectorDock::_menu_option));
|
||||
|
||||
info = memnew(Button);
|
||||
add_child(info);
|
||||
info->set_clip_text(true);
|
||||
info->set_accessibility_name(TTRC("Information"));
|
||||
info->hide();
|
||||
info->connect(SceneStringName(pressed), callable_mp(this, &InspectorDock::_info_pressed));
|
||||
|
||||
unique_resources_confirmation = memnew(ConfirmationDialog);
|
||||
add_child(unique_resources_confirmation);
|
||||
|
||||
VBoxContainer *container = memnew(VBoxContainer);
|
||||
unique_resources_confirmation->add_child(container);
|
||||
|
||||
unique_resources_label = memnew(Label);
|
||||
unique_resources_label->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
container->add_child(unique_resources_label);
|
||||
|
||||
unique_resources_list_tree = memnew(Tree);
|
||||
unique_resources_list_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
unique_resources_list_tree->set_hide_root(true);
|
||||
unique_resources_list_tree->set_columns(1);
|
||||
unique_resources_list_tree->set_custom_minimum_size(Size2(0, 200 * EDSCALE));
|
||||
container->add_child(unique_resources_list_tree);
|
||||
|
||||
Label *bottom_label = memnew(Label);
|
||||
bottom_label->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
bottom_label->set_text(TTRC("This cannot be undone. Are you sure?"));
|
||||
container->add_child(bottom_label);
|
||||
|
||||
unique_resources_confirmation->connect(SceneStringName(confirmed), callable_mp(this, &InspectorDock::_menu_confirm_current));
|
||||
|
||||
info_dialog = memnew(AcceptDialog);
|
||||
EditorNode::get_singleton()->get_gui_base()->add_child(info_dialog);
|
||||
|
||||
load_resource_dialog = memnew(EditorFileDialog);
|
||||
add_child(load_resource_dialog);
|
||||
load_resource_dialog->set_current_dir("res://");
|
||||
load_resource_dialog->connect("file_selected", callable_mp(this, &InspectorDock::_resource_file_selected));
|
||||
|
||||
inspector = memnew(EditorInspector);
|
||||
add_child(inspector);
|
||||
inspector->set_autoclear(true);
|
||||
inspector->set_show_categories(true, true);
|
||||
inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
inspector->set_use_doc_hints(true);
|
||||
inspector->set_hide_script(false);
|
||||
inspector->set_hide_metadata(false);
|
||||
inspector->set_use_settings_name_style(false);
|
||||
inspector->set_property_name_style(property_name_style);
|
||||
inspector->set_use_folding(!bool(EDITOR_GET("interface/inspector/disable_folding")));
|
||||
inspector->register_text_enter(search);
|
||||
|
||||
inspector->set_use_filter(true);
|
||||
|
||||
inspector->connect("resource_selected", callable_mp(this, &InspectorDock::_resource_selected));
|
||||
|
||||
FileSystemDock::get_singleton()->connect("files_moved", callable_mp(this, &InspectorDock::_files_moved));
|
||||
|
||||
set_process_shortcut_input(true);
|
||||
}
|
||||
|
||||
InspectorDock::~InspectorDock() {
|
||||
singleton = nullptr;
|
||||
}
|
166
editor/docks/inspector_dock.h
Normal file
166
editor/docks/inspector_dock.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/**************************************************************************/
|
||||
/* inspector_dock.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/editor_data.h"
|
||||
#include "editor/gui/create_dialog.h"
|
||||
#include "editor/inspector/editor_inspector.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
class EditorFileDialog;
|
||||
class EditorObjectSelector;
|
||||
|
||||
class InspectorDock : public VBoxContainer {
|
||||
GDCLASS(InspectorDock, VBoxContainer);
|
||||
|
||||
enum MenuOptions {
|
||||
RESOURCE_LOAD,
|
||||
RESOURCE_SAVE,
|
||||
RESOURCE_SAVE_AS,
|
||||
RESOURCE_SHOW_IN_FILESYSTEM,
|
||||
RESOURCE_MAKE_BUILT_IN,
|
||||
RESOURCE_COPY,
|
||||
RESOURCE_EDIT_CLIPBOARD,
|
||||
OBJECT_COPY_PARAMS,
|
||||
OBJECT_PASTE_PARAMS,
|
||||
OBJECT_UNIQUE_RESOURCES,
|
||||
OBJECT_REQUEST_HELP,
|
||||
|
||||
COLLAPSE_ALL,
|
||||
EXPAND_ALL,
|
||||
EXPAND_REVERTABLE,
|
||||
|
||||
// Matches `EditorPropertyNameProcessor::Style`.
|
||||
PROPERTY_NAME_STYLE_RAW,
|
||||
PROPERTY_NAME_STYLE_CAPITALIZED,
|
||||
PROPERTY_NAME_STYLE_LOCALIZED,
|
||||
|
||||
OBJECT_METHOD_BASE = 500
|
||||
};
|
||||
|
||||
EditorData *editor_data = nullptr;
|
||||
|
||||
EditorInspector *inspector = nullptr;
|
||||
|
||||
Object *current = nullptr;
|
||||
|
||||
Button *backward_button = nullptr;
|
||||
Button *forward_button = nullptr;
|
||||
|
||||
EditorFileDialog *load_resource_dialog = nullptr;
|
||||
CreateDialog *new_resource_dialog = nullptr;
|
||||
Button *resource_new_button = nullptr;
|
||||
Button *resource_load_button = nullptr;
|
||||
MenuButton *resource_save_button = nullptr;
|
||||
MenuButton *resource_extra_button = nullptr;
|
||||
MenuButton *history_menu = nullptr;
|
||||
LineEdit *search = nullptr;
|
||||
|
||||
Button *open_docs_button = nullptr;
|
||||
MenuButton *object_menu = nullptr;
|
||||
EditorObjectSelector *object_selector = nullptr;
|
||||
|
||||
bool info_is_warning = false; // Display in yellow and use warning icon if true.
|
||||
Button *info = nullptr;
|
||||
AcceptDialog *info_dialog = nullptr;
|
||||
|
||||
int current_option = -1;
|
||||
ConfirmationDialog *unique_resources_confirmation = nullptr;
|
||||
Label *unique_resources_label = nullptr;
|
||||
Tree *unique_resources_list_tree = nullptr;
|
||||
|
||||
EditorPropertyNameProcessor::Style property_name_style;
|
||||
List<Pair<StringName, Variant>> stored_properties;
|
||||
|
||||
void _prepare_menu();
|
||||
void _menu_option(int p_option);
|
||||
void _menu_confirm_current();
|
||||
void _menu_option_confirm(int p_option, bool p_confirmed);
|
||||
|
||||
void _new_resource();
|
||||
void _load_resource(const String &p_type = "");
|
||||
void _open_resource_selector() { _load_resource(); } // just used to call from arg-less signal
|
||||
void _resource_file_selected(const String &p_file);
|
||||
void _save_resource(bool save_as);
|
||||
void _unref_resource();
|
||||
void _copy_resource();
|
||||
void _paste_resource();
|
||||
void _prepare_resource_extra_popup();
|
||||
Ref<Resource> _get_current_resource() const;
|
||||
|
||||
void _info_pressed();
|
||||
void _resource_created();
|
||||
void _resource_selected(const Ref<Resource> &p_res, const String &p_property);
|
||||
void _files_moved(const String &p_old_file, const String &p_new_file);
|
||||
void _edit_forward();
|
||||
void _edit_back();
|
||||
void _menu_collapseall();
|
||||
void _menu_expandall();
|
||||
void _menu_expand_revertable();
|
||||
void _select_history(int p_idx);
|
||||
void _prepare_history();
|
||||
|
||||
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
private:
|
||||
static InspectorDock *singleton;
|
||||
|
||||
public:
|
||||
static InspectorDock *get_singleton() { return singleton; }
|
||||
static EditorInspector *get_inspector_singleton() { return singleton->inspector; }
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void go_back();
|
||||
void edit_resource(const Ref<Resource> &p_resource);
|
||||
void open_resource(const String &p_type);
|
||||
void clear();
|
||||
void set_info(const String &p_button_text, const String &p_message, bool p_is_warning);
|
||||
void update(Object *p_object);
|
||||
Container *get_addon_area();
|
||||
EditorInspector *get_inspector() { return inspector; }
|
||||
|
||||
EditorPropertyNameProcessor::Style get_property_name_style() const;
|
||||
|
||||
void store_script_properties(Object *p_object);
|
||||
void apply_script_properties(Object *p_object);
|
||||
|
||||
InspectorDock(EditorData &p_editor_data);
|
||||
~InspectorDock();
|
||||
};
|
162
editor/docks/node_dock.cpp
Normal file
162
editor/docks/node_dock.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
/**************************************************************************/
|
||||
/* node_dock.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "node_dock.h"
|
||||
|
||||
#include "core/io/config_file.h"
|
||||
#include "editor/scene/connections_dialog.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
|
||||
void NodeDock::show_groups() {
|
||||
groups_button->set_pressed(true);
|
||||
connections_button->set_pressed(false);
|
||||
groups->show();
|
||||
connections->hide();
|
||||
}
|
||||
|
||||
void NodeDock::show_connections() {
|
||||
groups_button->set_pressed(false);
|
||||
connections_button->set_pressed(true);
|
||||
groups->hide();
|
||||
connections->show();
|
||||
}
|
||||
|
||||
void NodeDock::_save_layout_to_config(Ref<ConfigFile> p_layout, const String &p_section) const {
|
||||
p_layout->set_value(p_section, "dock_node_current_tab", int(groups_button->is_pressed()));
|
||||
}
|
||||
|
||||
void NodeDock::_load_layout_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
|
||||
const int current_tab = p_layout->get_value(p_section, "dock_node_current_tab", 0);
|
||||
if (select_a_node->is_visible()) {
|
||||
if (current_tab == 0) {
|
||||
groups_button->set_pressed_no_signal(false);
|
||||
connections_button->set_pressed_no_signal(true);
|
||||
} else if (current_tab == 1) {
|
||||
groups_button->set_pressed_no_signal(true);
|
||||
connections_button->set_pressed_no_signal(false);
|
||||
}
|
||||
} else if (current_tab == 0) {
|
||||
show_connections();
|
||||
} else if (current_tab == 1) {
|
||||
show_groups();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeDock::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
connections_button->set_button_icon(get_editor_theme_icon(SNAME("Signals")));
|
||||
groups_button->set_button_icon(get_editor_theme_icon(SNAME("Groups")));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeDock::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_save_layout_to_config"), &NodeDock::_save_layout_to_config);
|
||||
ClassDB::bind_method(D_METHOD("_load_layout_from_config"), &NodeDock::_load_layout_from_config);
|
||||
}
|
||||
|
||||
void NodeDock::update_lists() {
|
||||
connections->update_tree();
|
||||
}
|
||||
|
||||
void NodeDock::set_node(Node *p_node) {
|
||||
connections->set_node(p_node);
|
||||
groups->set_current(p_node);
|
||||
|
||||
if (p_node) {
|
||||
if (connections_button->is_pressed()) {
|
||||
connections->show();
|
||||
} else {
|
||||
groups->show();
|
||||
}
|
||||
|
||||
mode_hb->show();
|
||||
select_a_node->hide();
|
||||
} else {
|
||||
connections->hide();
|
||||
groups->hide();
|
||||
mode_hb->hide();
|
||||
select_a_node->show();
|
||||
}
|
||||
}
|
||||
|
||||
NodeDock::NodeDock() {
|
||||
singleton = this;
|
||||
|
||||
set_name("Node");
|
||||
mode_hb = memnew(HBoxContainer);
|
||||
add_child(mode_hb);
|
||||
mode_hb->hide();
|
||||
|
||||
connections_button = memnew(Button);
|
||||
connections_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
connections_button->set_text(TTRC("Signals"));
|
||||
connections_button->set_toggle_mode(true);
|
||||
connections_button->set_pressed(true);
|
||||
connections_button->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
connections_button->set_clip_text(true);
|
||||
mode_hb->add_child(connections_button);
|
||||
connections_button->connect(SceneStringName(pressed), callable_mp(this, &NodeDock::show_connections));
|
||||
|
||||
groups_button = memnew(Button);
|
||||
groups_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
groups_button->set_text(TTRC("Groups"));
|
||||
groups_button->set_toggle_mode(true);
|
||||
groups_button->set_pressed(false);
|
||||
groups_button->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
groups_button->set_clip_text(true);
|
||||
mode_hb->add_child(groups_button);
|
||||
groups_button->connect(SceneStringName(pressed), callable_mp(this, &NodeDock::show_groups));
|
||||
|
||||
connections = memnew(ConnectionsDock);
|
||||
add_child(connections);
|
||||
connections->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
connections->hide();
|
||||
|
||||
groups = memnew(GroupsEditor);
|
||||
add_child(groups);
|
||||
groups->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
groups->hide();
|
||||
|
||||
select_a_node = memnew(Label);
|
||||
select_a_node->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
select_a_node->set_text(TTRC("Select a single node to edit its signals and groups."));
|
||||
select_a_node->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
|
||||
select_a_node->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
select_a_node->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
|
||||
select_a_node->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
select_a_node->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
|
||||
add_child(select_a_node);
|
||||
}
|
||||
|
||||
NodeDock::~NodeDock() {
|
||||
singleton = nullptr;
|
||||
}
|
74
editor/docks/node_dock.h
Normal file
74
editor/docks/node_dock.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/**************************************************************************/
|
||||
/* node_dock.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "groups_editor.h"
|
||||
|
||||
class ConfigFile;
|
||||
class ConnectionsDock;
|
||||
|
||||
class NodeDock : public VBoxContainer {
|
||||
GDCLASS(NodeDock, VBoxContainer);
|
||||
|
||||
Button *connections_button = nullptr;
|
||||
Button *groups_button = nullptr;
|
||||
|
||||
ConnectionsDock *connections = nullptr;
|
||||
GroupsEditor *groups = nullptr;
|
||||
|
||||
HBoxContainer *mode_hb = nullptr;
|
||||
|
||||
Label *select_a_node = nullptr;
|
||||
|
||||
void _save_layout_to_config(Ref<ConfigFile> p_layout, const String &p_section) const;
|
||||
void _load_layout_from_config(Ref<ConfigFile> p_layout, const String &p_section);
|
||||
|
||||
private:
|
||||
inline static NodeDock *singleton = nullptr;
|
||||
|
||||
public:
|
||||
static NodeDock *get_singleton() { return singleton; }
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_node(Node *p_node);
|
||||
|
||||
void show_groups();
|
||||
void show_connections();
|
||||
|
||||
void update_lists();
|
||||
|
||||
NodeDock();
|
||||
~NodeDock();
|
||||
};
|
4944
editor/docks/scene_tree_dock.cpp
Normal file
4944
editor/docks/scene_tree_dock.cpp
Normal file
File diff suppressed because it is too large
Load Diff
362
editor/docks/scene_tree_dock.h
Normal file
362
editor/docks/scene_tree_dock.h
Normal file
@@ -0,0 +1,362 @@
|
||||
/**************************************************************************/
|
||||
/* scene_tree_dock.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/scene/scene_tree_editor.h"
|
||||
#include "editor/script/script_create_dialog.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/resources/animation.h"
|
||||
|
||||
class CheckBox;
|
||||
class EditorData;
|
||||
class EditorSelection;
|
||||
class MenuButton;
|
||||
class RenameDialog;
|
||||
class ReparentDialog;
|
||||
class ShaderCreateDialog;
|
||||
class TextureRect;
|
||||
|
||||
class SceneTreeDock : public VBoxContainer {
|
||||
GDCLASS(SceneTreeDock, VBoxContainer);
|
||||
|
||||
enum Tool {
|
||||
TOOL_NEW,
|
||||
TOOL_INSTANTIATE,
|
||||
TOOL_EXPAND_COLLAPSE,
|
||||
TOOL_CUT,
|
||||
TOOL_COPY,
|
||||
TOOL_PASTE,
|
||||
TOOL_PASTE_AS_SIBLING,
|
||||
TOOL_RENAME,
|
||||
TOOL_BATCH_RENAME,
|
||||
TOOL_REPLACE,
|
||||
TOOL_EXTEND_SCRIPT,
|
||||
TOOL_ATTACH_SCRIPT,
|
||||
TOOL_DETACH_SCRIPT,
|
||||
TOOL_MOVE_UP,
|
||||
TOOL_MOVE_DOWN,
|
||||
TOOL_DUPLICATE,
|
||||
TOOL_REPARENT,
|
||||
TOOL_REPARENT_TO_NEW_NODE,
|
||||
TOOL_MAKE_ROOT,
|
||||
TOOL_NEW_SCENE_FROM,
|
||||
TOOL_MULTI_EDIT,
|
||||
TOOL_ERASE,
|
||||
TOOL_COPY_NODE_PATH,
|
||||
TOOL_SHOW_IN_FILE_SYSTEM,
|
||||
TOOL_OPEN_DOCUMENTATION,
|
||||
TOOL_AUTO_EXPAND,
|
||||
TOOL_SCENE_EDITABLE_CHILDREN,
|
||||
TOOL_SCENE_USE_PLACEHOLDER,
|
||||
TOOL_SCENE_MAKE_LOCAL,
|
||||
TOOL_SCENE_OPEN,
|
||||
TOOL_SCENE_CLEAR_INHERITANCE,
|
||||
TOOL_SCENE_CLEAR_INHERITANCE_CONFIRM,
|
||||
TOOL_SCENE_OPEN_INHERITED,
|
||||
TOOL_TOGGLE_SCENE_UNIQUE_NAME,
|
||||
TOOL_CREATE_2D_SCENE,
|
||||
TOOL_CREATE_3D_SCENE,
|
||||
TOOL_CREATE_USER_INTERFACE,
|
||||
TOOL_CREATE_FAVORITE,
|
||||
TOOL_CENTER_PARENT,
|
||||
TOOL_HIDE_FILTERED_OUT_PARENTS,
|
||||
TOOL_ACCESSIBILITY_WARNINGS,
|
||||
};
|
||||
|
||||
enum {
|
||||
EDIT_SUBRESOURCE_BASE = 100
|
||||
};
|
||||
|
||||
Vector<ObjectID> subresources;
|
||||
|
||||
bool reset_create_dialog = false;
|
||||
|
||||
int current_option = 0;
|
||||
CreateDialog *create_dialog = nullptr;
|
||||
RenameDialog *rename_dialog = nullptr;
|
||||
|
||||
Button *button_add = nullptr;
|
||||
Button *button_instance = nullptr;
|
||||
Button *button_create_script = nullptr;
|
||||
Button *button_detach_script = nullptr;
|
||||
Button *button_extend_script = nullptr;
|
||||
MenuButton *button_tree_menu = nullptr;
|
||||
|
||||
Button *node_shortcuts_toggle = nullptr;
|
||||
VBoxContainer *beginner_node_shortcuts = nullptr;
|
||||
VBoxContainer *favorite_node_shortcuts = nullptr;
|
||||
|
||||
Button *button_2d = nullptr;
|
||||
Button *button_3d = nullptr;
|
||||
Button *button_ui = nullptr;
|
||||
Button *button_custom = nullptr;
|
||||
Button *button_clipboard = nullptr;
|
||||
|
||||
HBoxContainer *button_hb = nullptr;
|
||||
Button *edit_local, *edit_remote;
|
||||
SceneTreeEditor *scene_tree = nullptr;
|
||||
Control *remote_tree = nullptr;
|
||||
|
||||
HBoxContainer *tool_hbc = nullptr;
|
||||
void _tool_selected(int p_tool, bool p_confirm_override = false);
|
||||
void _property_selected(int p_idx);
|
||||
|
||||
Node *property_drop_node = nullptr;
|
||||
String resource_drop_path;
|
||||
void _perform_property_drop(Node *p_node, const String &p_property, Ref<Resource> p_res);
|
||||
|
||||
EditorData *editor_data = nullptr;
|
||||
EditorSelection *editor_selection = nullptr;
|
||||
LocalVector<ObjectID> node_previous_selection;
|
||||
bool update_script_button_queued = false;
|
||||
|
||||
List<Node *> node_clipboard;
|
||||
HashSet<Node *> node_clipboard_edited_scene_owned;
|
||||
String clipboard_source_scene;
|
||||
HashMap<String, HashMap<Ref<Resource>, Ref<Resource>>> clipboard_resource_remap;
|
||||
|
||||
ScriptCreateDialog *script_create_dialog = nullptr;
|
||||
ShaderCreateDialog *shader_create_dialog = nullptr;
|
||||
AcceptDialog *accept = nullptr;
|
||||
ConfirmationDialog *delete_dialog = nullptr;
|
||||
Label *delete_dialog_label = nullptr;
|
||||
CheckBox *delete_tracks_checkbox = nullptr;
|
||||
ConfirmationDialog *editable_instance_remove_dialog = nullptr;
|
||||
ConfirmationDialog *placeholder_editable_instance_remove_dialog = nullptr;
|
||||
|
||||
ReparentDialog *reparent_dialog = nullptr;
|
||||
EditorFileDialog *new_scene_from_dialog = nullptr;
|
||||
|
||||
enum FilterMenuItems {
|
||||
FILTER_BY_TYPE = 64, // Used in the same menus as the Tool enum.
|
||||
FILTER_BY_GROUP,
|
||||
};
|
||||
|
||||
LineEdit *filter = nullptr;
|
||||
PopupMenu *filter_quick_menu = nullptr;
|
||||
TextureRect *filter_icon = nullptr;
|
||||
|
||||
PopupMenu *menu = nullptr;
|
||||
PopupMenu *menu_subresources = nullptr;
|
||||
PopupMenu *menu_properties = nullptr;
|
||||
ConfirmationDialog *clear_inherit_confirm = nullptr;
|
||||
|
||||
bool first_enter = true;
|
||||
|
||||
void _create();
|
||||
Node *_do_create(Node *p_parent);
|
||||
void _post_do_create(Node *p_child);
|
||||
Node *scene_root = nullptr;
|
||||
Node *edited_scene = nullptr;
|
||||
Node *pending_click_select = nullptr;
|
||||
bool tree_clicked = false;
|
||||
|
||||
VBoxContainer *create_root_dialog = nullptr;
|
||||
String selected_favorite_root;
|
||||
|
||||
Ref<ShaderMaterial> selected_shader_material;
|
||||
|
||||
void _add_children_to_popup(Object *p_obj, int p_depth);
|
||||
|
||||
void _node_reparent(NodePath p_path, bool p_keep_global_xform);
|
||||
void _do_reparent(Node *p_new_parent, int p_position_in_parent, Vector<Node *> p_nodes, bool p_keep_global_xform);
|
||||
|
||||
void _set_owners(Node *p_owner, const Array &p_nodes);
|
||||
|
||||
enum ReplaceOwnerMode {
|
||||
MODE_BIDI,
|
||||
MODE_DO,
|
||||
MODE_UNDO
|
||||
};
|
||||
|
||||
void _node_replace_owner(Node *p_base, Node *p_node, Node *p_root, ReplaceOwnerMode p_mode = MODE_BIDI);
|
||||
void _node_strip_signal_inheritance(Node *p_node);
|
||||
void _load_request(const String &p_path);
|
||||
void _script_open_request(const Ref<Script> &p_script);
|
||||
void _push_item(Object *p_object);
|
||||
void _handle_select(Node *p_node);
|
||||
|
||||
bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node);
|
||||
bool _track_inherit(const String &p_target_scene_path, Node *p_desired_node);
|
||||
|
||||
void _node_selected();
|
||||
void _node_renamed();
|
||||
void _script_created(Ref<Script> p_script);
|
||||
void _shader_created(Ref<Shader> p_shader);
|
||||
void _script_creation_closed();
|
||||
void _shader_creation_closed();
|
||||
|
||||
void _delete_confirm(bool p_cut = false);
|
||||
void _delete_dialog_closed();
|
||||
|
||||
void _toggle_editable_children_from_selection();
|
||||
|
||||
void _reparent_nodes_to_root(Node *p_root, const Array &p_nodes, Node *p_owner);
|
||||
void _reparent_nodes_to_paths_with_transform_and_name(Node *p_root, const Array &p_nodes, const Array &p_paths, const Array &p_transforms, const Array &p_names, Node *p_owner);
|
||||
void _toggle_editable_children(Node *p_node);
|
||||
|
||||
void _toggle_placeholder_from_selection();
|
||||
|
||||
void _node_prerenamed(Node *p_node, const String &p_new_name);
|
||||
|
||||
void _nodes_drag_begin();
|
||||
|
||||
void _handle_hover_to_inspect();
|
||||
void _inspect_hovered_node();
|
||||
void _reset_hovering_timer();
|
||||
Timer *inspect_hovered_node_delay = nullptr;
|
||||
TreeItem *tree_item_inspected = nullptr;
|
||||
Node *node_hovered_now = nullptr;
|
||||
Node *node_hovered_previously = nullptr;
|
||||
bool select_node_hovered_at_end_of_drag = false;
|
||||
bool hovered_but_reparenting = false;
|
||||
|
||||
virtual void input(const Ref<InputEvent> &p_event) override;
|
||||
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
|
||||
void _scene_tree_gui_input(Ref<InputEvent> p_event);
|
||||
|
||||
void _new_scene_from(const String &p_file);
|
||||
void _set_node_owner_recursive(Node *p_node, Node *p_owner, const HashMap<const Node *, Node *> &p_inverse_duplimap);
|
||||
|
||||
bool _validate_no_foreign();
|
||||
bool _validate_no_instance();
|
||||
void _selection_changed();
|
||||
void _update_script_button();
|
||||
void _queue_update_script_button();
|
||||
|
||||
void _fill_path_renames(Vector<StringName> base_path, Vector<StringName> new_base_path, Node *p_node, HashMap<Node *, NodePath> *p_renames);
|
||||
bool _has_tracks_to_delete(Node *p_node, List<Node *> &p_to_delete) const;
|
||||
|
||||
void _normalize_drop(Node *&to_node, int &to_pos, int p_type);
|
||||
Array _get_selection_array();
|
||||
|
||||
void _nodes_dragged(const Array &p_nodes, NodePath p_to, int p_type);
|
||||
void _files_dropped(const Vector<String> &p_files, NodePath p_to, int p_type);
|
||||
void _script_dropped(const String &p_file, NodePath p_to);
|
||||
void _quick_open(const String &p_file_path);
|
||||
|
||||
void _tree_rmb(const Vector2 &p_menu_pos);
|
||||
void _update_tree_menu();
|
||||
|
||||
void _filter_changed(const String &p_filter);
|
||||
void _filter_gui_input(const Ref<InputEvent> &p_event);
|
||||
void _filter_option_selected(int option);
|
||||
void _append_filter_options_to(PopupMenu *p_menu, bool p_include_separator = true);
|
||||
|
||||
void _perform_instantiate_scenes(const Vector<String> &p_files, Node *p_parent, int p_pos);
|
||||
void _perform_create_audio_stream_players(const Vector<String> &p_files, Node *p_parent, int p_pos);
|
||||
void _replace_with_branch_scene(const String &p_file, Node *base);
|
||||
|
||||
void _remote_tree_selected();
|
||||
void _local_tree_selected();
|
||||
|
||||
void _update_create_root_dialog(bool p_initializing = false);
|
||||
void _favorite_root_selected(const String &p_class);
|
||||
|
||||
void _feature_profile_changed();
|
||||
|
||||
void _clear_clipboard();
|
||||
void _create_remap_for_node(Node *p_node, HashMap<Ref<Resource>, Ref<Resource>> &r_remap);
|
||||
void _create_remap_for_resource(Ref<Resource> p_resource, HashMap<Ref<Resource>, Ref<Resource>> &r_remap);
|
||||
|
||||
void _list_all_subresources(PopupMenu *p_menu);
|
||||
void _gather_resources(Node *p_node, List<Pair<Ref<Resource>, Node *>> &r_resources);
|
||||
void _edit_subresource(int p_idx, const PopupMenu *p_from_menu);
|
||||
|
||||
bool profile_allow_editing = true;
|
||||
bool profile_allow_script_editing = true;
|
||||
bool determine_path_automatically = true;
|
||||
|
||||
static void _update_configuration_warning();
|
||||
|
||||
bool _update_node_path(Node *p_root_node, NodePath &r_node_path, HashMap<Node *, NodePath> *p_renames) const;
|
||||
void _check_object_properties_recursive(Node *p_root_node, Object *p_obj, HashMap<Node *, NodePath> *p_renames, bool p_inside_resource = false) const;
|
||||
bool _check_node_path_recursive(Node *p_root_node, Variant &r_variant, HashMap<Node *, NodePath> *p_renames, bool p_inside_resource = false) const;
|
||||
bool _check_node_recursive(Variant &r_variant, Node *p_node, Node *p_by_node, const String type_hint, String &r_warn_message);
|
||||
void _replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties = true, bool p_remove_old = true);
|
||||
|
||||
private:
|
||||
static SceneTreeDock *singleton;
|
||||
|
||||
public:
|
||||
static SceneTreeDock *get_singleton() { return singleton; }
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
String get_filter();
|
||||
void set_filter(const String &p_filter);
|
||||
void save_branch_to_file(const String &p_directory);
|
||||
|
||||
void _focus_node();
|
||||
|
||||
void add_root_node(Node *p_node);
|
||||
void set_edited_scene(Node *p_scene);
|
||||
void instantiate(const String &p_file);
|
||||
void instantiate_scenes(const Vector<String> &p_files, Node *p_parent = nullptr);
|
||||
void clear_previous_node_selection();
|
||||
void set_selection(const Vector<Node *> &p_nodes);
|
||||
void set_selected(Node *p_node, bool p_emit_selected = false);
|
||||
void fill_path_renames(Node *p_node, Node *p_new_parent, HashMap<Node *, NodePath> *p_renames);
|
||||
void perform_node_renames(Node *p_base, HashMap<Node *, NodePath> *p_renames, HashMap<Ref<Animation>, HashSet<int>> *r_rem_anims = nullptr);
|
||||
void perform_node_replace(Node *p_base, Node *p_node, Node *p_by_node);
|
||||
SceneTreeEditor *get_tree_editor() { return scene_tree; }
|
||||
EditorData *get_editor_data() { return editor_data; }
|
||||
|
||||
void add_remote_tree_editor(Control *p_remote);
|
||||
void show_remote_tree();
|
||||
void hide_remote_tree();
|
||||
void show_tab_buttons();
|
||||
void hide_tab_buttons();
|
||||
|
||||
void replace_node(Node *p_node, Node *p_by_node);
|
||||
|
||||
void attach_script_to_selected(bool p_extend);
|
||||
void open_script_dialog(Node *p_for_node, bool p_extend);
|
||||
|
||||
void attach_shader_to_selected(int p_preferred_mode = -1);
|
||||
void open_shader_dialog(const Ref<ShaderMaterial> &p_for_material, int p_preferred_mode = -1);
|
||||
|
||||
void open_add_child_dialog();
|
||||
void open_instance_child_dialog();
|
||||
|
||||
List<Node *> paste_nodes(bool p_paste_as_sibling = false);
|
||||
List<Node *> get_node_clipboard() const;
|
||||
|
||||
ScriptCreateDialog *get_script_create_dialog() {
|
||||
return script_create_dialog;
|
||||
}
|
||||
|
||||
SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data);
|
||||
~SceneTreeDock();
|
||||
};
|
Reference in New Issue
Block a user