summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/classes/EditorPlugin.xml8
-rw-r--r--doc/classes/EditorSettings.xml3
-rw-r--r--doc/classes/Object.xml2
-rw-r--r--editor/editor_dock_manager.cpp79
-rw-r--r--editor/editor_dock_manager.h11
-rw-r--r--editor/editor_node.cpp31
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/editor_settings.cpp1
-rw-r--r--editor/plugins/editor_plugin.cpp6
-rw-r--r--editor/plugins/editor_plugin.h2
-rw-r--r--scene/gui/split_container.cpp4
11 files changed, 131 insertions, 17 deletions
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index e874b44cfc..89b9e7d6c2 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -727,6 +727,14 @@
Removes a callback previously added by [method add_undo_redo_inspector_hook_callback].
</description>
</method>
+ <method name="set_dock_tab_icon">
+ <return type="void" />
+ <param index="0" name="control" type="Control" />
+ <param index="1" name="icon" type="Texture2D" />
+ <description>
+ Sets the tab icon for the given control in a dock slot. Setting to [code]null[/code] removes the icon.
+ </description>
+ </method>
<method name="set_force_draw_over_forwarding_enabled">
<return type="void" />
<description>
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 6aa81803b7..f31f8135ff 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -632,6 +632,9 @@
If set to [b]Auto[/b], the editor scale is automatically determined based on the screen resolution and reported display DPI. This heuristic is not always ideal, which means you can get better results by setting the editor scale manually.
If set to [b]Custom[/b], the scaling value in [member interface/editor/custom_display_scale] will be used.
</member>
+ <member name="interface/editor/dock_tab_style" type="int" setter="" getter="">
+ Tab style of editor docks.
+ </member>
<member name="interface/editor/editor_language" type="String" setter="" getter="">
The language to use for the editor interface.
Translations are provided by the community. If you spot a mistake, [url=$DOCS_URL/contributing/documentation/editor_and_docs_localization.html]contribute to editor translations on Weblate![/url]
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index 68d2d6411c..b0dec2d00a 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -1034,6 +1034,7 @@
Translates a [param message], using the translation catalogs configured in the Project Settings. Further [param context] can be specified to help with the translation. Note that most [Control] nodes automatically translate their strings, so this method is mostly useful for formatted strings or custom drawn text.
If [method can_translate_messages] is [code]false[/code], or no translation is available, this method returns the [param message] without changes. See [method set_message_translation].
For detailed examples, see [url=$DOCS_URL/tutorials/i18n/internationalizing_games.html]Internationalizing games[/url].
+ [b]Note:[/b] This method can't be used without an [Object] instance, as it requires the [method can_translate_messages] method. To translate strings in a static context, use [method TranslationServer.translate].
</description>
</method>
<method name="tr_n" qualifiers="const">
@@ -1048,6 +1049,7 @@
The [param n] is the number, or amount, of the message's subject. It is used by the translation system to fetch the correct plural form for the current language.
For detailed examples, see [url=$DOCS_URL/tutorials/i18n/localization_using_gettext.html]Localization using gettext[/url].
[b]Note:[/b] Negative and [float] numbers may not properly apply to some countable subjects. It's recommended to handle these cases with [method tr].
+ [b]Note:[/b] This method can't be used without an [Object] instance, as it requires the [method can_translate_messages] method. To translate strings in a static context, use [method TranslationServer.translate_plural].
</description>
</method>
</methods>
diff --git a/editor/editor_dock_manager.cpp b/editor/editor_dock_manager.cpp
index 06dd33d8ab..4956fa867a 100644
--- a/editor/editor_dock_manager.cpp
+++ b/editor/editor_dock_manager.cpp
@@ -33,12 +33,10 @@
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
-#include "scene/gui/popup.h"
#include "scene/gui/split_container.h"
#include "scene/gui/tab_container.h"
#include "scene/main/window.h"
-#include "editor/editor_command_palette.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
@@ -47,6 +45,12 @@
#include "editor/themes/editor_scale.h"
#include "editor/window_wrapper.h"
+enum class TabStyle {
+ TEXT_ONLY,
+ ICON_ONLY,
+ TEXT_AND_ICON,
+};
+
EditorDockManager *EditorDockManager::singleton = nullptr;
void DockSplitContainer::_update_visibility() {
@@ -156,7 +160,7 @@ void EditorDockManager::_update_docks_menu() {
docks_menu->clear();
docks_menu->reset_size();
- const Ref<Texture2D> icon = docks_menu->get_editor_theme_icon(SNAME("Window"));
+ const Ref<Texture2D> default_icon = docks_menu->get_editor_theme_icon(SNAME("Window"));
const Color closed_icon_color_mod = Color(1, 1, 1, 0.5);
// Add docks.
@@ -169,7 +173,8 @@ void EditorDockManager::_update_docks_menu() {
} else {
docks_menu->add_item(dock.value.title, id);
}
- docks_menu->set_item_icon(id, icon);
+ const Ref<Texture2D> icon = dock.value.icon_name ? docks_menu->get_editor_theme_icon(dock.value.icon_name) : dock.value.icon;
+ docks_menu->set_item_icon(id, icon.is_valid() ? icon : default_icon);
if (!dock.value.open) {
docks_menu->set_item_icon_modulate(id, closed_icon_color_mod);
}
@@ -344,7 +349,9 @@ void EditorDockManager::_move_dock(Control *p_dock, Control *p_target, int p_tab
p_target->set_block_signals(false);
TabContainer *dock_tab_container = Object::cast_to<TabContainer>(p_target);
if (dock_tab_container) {
- dock_tab_container->set_tab_title(dock_tab_container->get_tab_idx_from_control(p_dock), all_docks[p_dock].title);
+ if (dock_tab_container->is_inside_tree()) {
+ _update_tab_style(p_dock);
+ }
if (p_tab_index >= 0) {
_move_dock_tab_index(p_dock, p_tab_index, p_set_current);
}
@@ -352,6 +359,42 @@ void EditorDockManager::_move_dock(Control *p_dock, Control *p_target, int p_tab
}
}
+void EditorDockManager::_update_tab_style(Control *p_dock) {
+ const DockInfo &dock_info = all_docks[p_dock];
+ if (!dock_info.enabled || !dock_info.open) {
+ return; // Disabled by feature profile or manually closed by user.
+ }
+ if (dock_info.dock_window || dock_info.at_bottom) {
+ return; // Floating or sent to bottom.
+ }
+
+ TabContainer *tab_container = get_dock_tab_container(p_dock);
+ ERR_FAIL_NULL(tab_container);
+ int index = tab_container->get_tab_idx_from_control(p_dock);
+ ERR_FAIL_COND(index == -1);
+
+ const TabStyle style = (TabStyle)EDITOR_GET("interface/editor/dock_tab_style").operator int();
+ switch (style) {
+ case TabStyle::TEXT_ONLY: {
+ tab_container->set_tab_title(index, dock_info.title);
+ tab_container->set_tab_icon(index, Ref<Texture2D>());
+ tab_container->set_tab_tooltip(index, String());
+ } break;
+ case TabStyle::ICON_ONLY: {
+ const Ref<Texture2D> icon = dock_info.icon_name ? tab_container->get_editor_theme_icon(dock_info.icon_name) : dock_info.icon;
+ tab_container->set_tab_title(index, icon.is_valid() ? String() : dock_info.title);
+ tab_container->set_tab_icon(index, icon);
+ tab_container->set_tab_tooltip(index, icon.is_valid() ? dock_info.title : String());
+ } break;
+ case TabStyle::TEXT_AND_ICON: {
+ const Ref<Texture2D> icon = dock_info.icon_name ? tab_container->get_editor_theme_icon(dock_info.icon_name) : dock_info.icon;
+ tab_container->set_tab_title(index, dock_info.title);
+ tab_container->set_tab_icon(index, icon);
+ tab_container->set_tab_tooltip(index, String());
+ } break;
+ }
+}
+
void EditorDockManager::save_docks_to_config(Ref<ConfigFile> p_layout, const String &p_section) const {
// Save docks by dock slot.
for (int i = 0; i < DOCK_SLOT_MAX; i++) {
@@ -665,14 +708,15 @@ void EditorDockManager::focus_dock(Control *p_dock) {
tab_container->set_current_tab(tab_index);
}
-void EditorDockManager::add_dock(Control *p_dock, const String &p_title, DockSlot p_slot, const Ref<Shortcut> &p_shortcut) {
+void EditorDockManager::add_dock(Control *p_dock, const String &p_title, DockSlot p_slot, const Ref<Shortcut> &p_shortcut, const StringName &p_icon_name) {
ERR_FAIL_NULL(p_dock);
ERR_FAIL_COND_MSG(all_docks.has(p_dock), vformat("Cannot add dock '%s', already added.", p_dock->get_name()));
DockInfo dock_info;
- dock_info.title = !p_title.is_empty() ? p_title : String(p_dock->get_name());
+ dock_info.title = p_title.is_empty() ? String(p_dock->get_name()) : p_title;
dock_info.dock_slot_index = p_slot;
dock_info.shortcut = p_shortcut;
+ dock_info.icon_name = p_icon_name;
all_docks[p_dock] = dock_info;
if (p_slot != DOCK_SLOT_NONE) {
@@ -695,6 +739,14 @@ void EditorDockManager::remove_dock(Control *p_dock) {
_update_layout();
}
+void EditorDockManager::set_dock_tab_icon(Control *p_dock, const Ref<Texture2D> &p_icon) {
+ ERR_FAIL_NULL(p_dock);
+ ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot set tab icon for unknown dock '%s'.", p_dock->get_name()));
+
+ all_docks[p_dock].icon = p_icon;
+ _update_tab_style(p_dock);
+}
+
void EditorDockManager::set_docks_visible(bool p_show) {
if (docks_visible == p_show) {
return;
@@ -710,6 +762,19 @@ bool EditorDockManager::are_docks_visible() const {
return docks_visible;
}
+void EditorDockManager::update_tab_styles() {
+ for (const KeyValue<Control *, DockInfo> &dock : all_docks) {
+ _update_tab_style(dock.key);
+ }
+}
+
+void EditorDockManager::set_tab_icon_max_width(int p_max_width) {
+ for (int i = 0; i < DOCK_SLOT_MAX; i++) {
+ TabContainer *tab_container = dock_slot[i];
+ tab_container->add_theme_constant_override(SNAME("icon_max_width"), p_max_width);
+ }
+}
+
void EditorDockManager::add_vsplit(DockSplitContainer *p_split) {
vsplits.push_back(p_split);
p_split->connect("dragged", callable_mp(this, &EditorDockManager::_dock_split_dragged));
diff --git a/editor/editor_dock_manager.h b/editor/editor_dock_manager.h
index cbb076c809..226222c55a 100644
--- a/editor/editor_dock_manager.h
+++ b/editor/editor_dock_manager.h
@@ -87,6 +87,8 @@ private:
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;
@@ -126,9 +128,14 @@ private:
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_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);
@@ -150,9 +157,11 @@ public:
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);
+ 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();
};
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 33b46b82bc..8ba85032f1 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -544,6 +544,9 @@ void EditorNode::_update_theme(bool p_skip_creation) {
}
}
}
+
+ editor_dock_manager->update_tab_styles();
+ editor_dock_manager->set_tab_icon_max_width(theme->get_constant(SNAME("class_icon_size"), EditorStringName(Editor)));
}
void EditorNode::update_preview_themes(int p_mode) {
@@ -752,7 +755,7 @@ void EditorNode::_notification(int p_what) {
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
// Save on focus loss before applying the FPS limit to avoid slowing down the saving process.
if (EDITOR_GET("interface/editor/save_on_focus_loss")) {
- _menu_option_confirm(FILE_SAVE_SCENE, false);
+ _menu_option_confirm(FILE_SAVE_SCENE_SILENTLY, false);
}
// Set a low FPS cap to decrease CPU/GPU usage while the editor is unfocused.
@@ -789,6 +792,10 @@ void EditorNode::_notification(int p_what) {
recent_scenes->reset_size();
}
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/dock_tab_style")) {
+ editor_dock_manager->update_tab_styles();
+ }
+
if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/scene_tabs")) {
scene_tabs->update_scene_tabs();
}
@@ -2666,6 +2673,16 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case FILE_CLOSE: {
_scene_tab_closed(editor_data.get_edited_scene());
} break;
+ case FILE_SAVE_SCENE_SILENTLY: {
+ // Save scene without displaying progress dialog. Used to work around
+ // errors about parent node being busy setting up children
+ // when Save on Focus Loss kicks in.
+ Node *scene = editor_data.get_edited_scene_root();
+ if (scene && !scene->get_scene_file_path().is_empty() && DirAccess::exists(scene->get_scene_file_path().get_base_dir())) {
+ _save_scene(scene->get_scene_file_path());
+ save_editor_layout_delayed();
+ }
+ } break;
case SCENE_TAB_CLOSE:
case FILE_SAVE_SCENE: {
int scene_idx = (p_option == FILE_SAVE_SCENE) ? -1 : tab_closing_idx;
@@ -7045,22 +7062,22 @@ EditorNode::EditorNode() {
history_dock = memnew(HistoryDock);
// Scene: Top left.
- editor_dock_manager->add_dock(SceneTreeDock::get_singleton(), TTR("Scene"), EditorDockManager::DOCK_SLOT_LEFT_UR);
+ editor_dock_manager->add_dock(SceneTreeDock::get_singleton(), TTR("Scene"), EditorDockManager::DOCK_SLOT_LEFT_UR, nullptr, "PackedScene");
// Import: Top left, behind Scene.
- editor_dock_manager->add_dock(ImportDock::get_singleton(), TTR("Import"), EditorDockManager::DOCK_SLOT_LEFT_UR);
+ editor_dock_manager->add_dock(ImportDock::get_singleton(), TTR("Import"), EditorDockManager::DOCK_SLOT_LEFT_UR, nullptr, "FileAccess");
// FileSystem: Bottom left.
- editor_dock_manager->add_dock(FileSystemDock::get_singleton(), TTR("FileSystem"), EditorDockManager::DOCK_SLOT_LEFT_BR, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_filesystem_bottom_panel", TTR("Toggle FileSystem Bottom Panel"), KeyModifierMask::ALT | Key::F));
+ editor_dock_manager->add_dock(FileSystemDock::get_singleton(), TTR("FileSystem"), EditorDockManager::DOCK_SLOT_LEFT_BR, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_filesystem_bottom_panel", TTR("Toggle FileSystem Bottom Panel"), KeyModifierMask::ALT | Key::F), "Folder");
// Inspector: Full height right.
- editor_dock_manager->add_dock(InspectorDock::get_singleton(), TTR("Inspector"), EditorDockManager::DOCK_SLOT_RIGHT_UL);
+ editor_dock_manager->add_dock(InspectorDock::get_singleton(), TTR("Inspector"), EditorDockManager::DOCK_SLOT_RIGHT_UL, nullptr, "AnimationTrackList");
// Node: Full height right, behind Inspector.
- editor_dock_manager->add_dock(NodeDock::get_singleton(), TTR("Node"), EditorDockManager::DOCK_SLOT_RIGHT_UL);
+ editor_dock_manager->add_dock(NodeDock::get_singleton(), TTR("Node"), EditorDockManager::DOCK_SLOT_RIGHT_UL, nullptr, "Object");
// History: Full height right, behind Node.
- editor_dock_manager->add_dock(history_dock, TTR("History"), EditorDockManager::DOCK_SLOT_RIGHT_UL);
+ editor_dock_manager->add_dock(history_dock, TTR("History"), EditorDockManager::DOCK_SLOT_RIGHT_UL, nullptr, "History");
// Add some offsets to left_r and main hsplits to make LEFT_R and RIGHT_L docks wider than minsize.
left_r_hsplit->set_split_offset(270 * EDSCALE);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 54c84bc6a6..6b3359eaee 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -159,6 +159,7 @@ private:
FILE_NEW_INHERITED_SCENE,
FILE_OPEN_SCENE,
FILE_SAVE_SCENE,
+ FILE_SAVE_SCENE_SILENTLY,
FILE_SAVE_AS_SCENE,
FILE_SAVE_ALL_SCENES,
FILE_SAVE_AND_RUN,
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index d1aa69294e..cf97221fa7 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -400,6 +400,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// Editor
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/localize_settings", true, "")
+ EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/dock_tab_style", 0, "Text Only,Icon Only,Text and Icon")
EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/ui_layout_direction", 0, "Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
// Display what the Auto display scale setting effectively corresponds to.
diff --git a/editor/plugins/editor_plugin.cpp b/editor/plugins/editor_plugin.cpp
index f42a1555a2..2b3c187352 100644
--- a/editor/plugins/editor_plugin.cpp
+++ b/editor/plugins/editor_plugin.cpp
@@ -100,6 +100,11 @@ void EditorPlugin::remove_control_from_bottom_panel(Control *p_control) {
EditorNode::get_bottom_panel()->remove_item(p_control);
}
+void EditorPlugin::set_dock_tab_icon(Control *p_control, const Ref<Texture2D> &p_icon) {
+ ERR_FAIL_NULL(p_control);
+ EditorDockManager::get_singleton()->set_dock_tab_icon(p_control, p_icon);
+}
+
void EditorPlugin::add_control_to_container(CustomControlContainer p_location, Control *p_control) {
ERR_FAIL_NULL(p_control);
@@ -565,6 +570,7 @@ void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_control_from_docks", "control"), &EditorPlugin::remove_control_from_docks);
ClassDB::bind_method(D_METHOD("remove_control_from_bottom_panel", "control"), &EditorPlugin::remove_control_from_bottom_panel);
ClassDB::bind_method(D_METHOD("remove_control_from_container", "container", "control"), &EditorPlugin::remove_control_from_container);
+ ClassDB::bind_method(D_METHOD("set_dock_tab_icon", "control", "icon"), &EditorPlugin::set_dock_tab_icon);
ClassDB::bind_method(D_METHOD("add_tool_menu_item", "name", "callable"), &EditorPlugin::add_tool_menu_item);
ClassDB::bind_method(D_METHOD("add_tool_submenu_item", "name", "submenu"), &EditorPlugin::add_tool_submenu_item);
ClassDB::bind_method(D_METHOD("remove_tool_menu_item", "name"), &EditorPlugin::remove_tool_menu_item);
diff --git a/editor/plugins/editor_plugin.h b/editor/plugins/editor_plugin.h
index f45a512b89..f6c4b35407 100644
--- a/editor/plugins/editor_plugin.h
+++ b/editor/plugins/editor_plugin.h
@@ -151,6 +151,8 @@ public:
void remove_control_from_docks(Control *p_control);
void remove_control_from_bottom_panel(Control *p_control);
+ void set_dock_tab_icon(Control *p_control, const Ref<Texture2D> &p_icon);
+
void add_tool_menu_item(const String &p_name, const Callable &p_callable);
void add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu);
void remove_tool_menu_item(const String &p_name);
diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp
index 4ea67eec49..98b1db4a96 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -126,8 +126,8 @@ Control *SplitContainer::get_containable_child(int p_idx) const {
int idx = 0;
for (int i = 0; i < get_child_count(false); i++) {
- Control *c = as_sortable_control(get_child(i, false));
- if (!c) {
+ Control *c = Object::cast_to<Control>(get_child(i, false));
+ if (!c || !c->is_visible() || c->is_set_as_top_level()) {
continue;
}