summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/string/ustring.h2
-rw-r--r--core/variant/variant_call.cpp1
-rw-r--r--doc/classes/EditorPlugin.xml8
-rw-r--r--doc/classes/EditorSettings.xml3
-rw-r--r--doc/classes/Object.xml2
-rw-r--r--doc/classes/String.xml12
-rw-r--r--doc/classes/StringName.xml10
-rw-r--r--drivers/gles3/storage/config.cpp2
-rw-r--r--editor/animation_track_editor.cpp2
-rw-r--r--editor/connections_dialog.cpp2
-rw-r--r--editor/debugger/debug_adapter/debug_adapter_parser.h2
-rw-r--r--editor/editor_dock_manager.cpp79
-rw-r--r--editor/editor_dock_manager.h11
-rw-r--r--editor/editor_help_search.cpp6
-rw-r--r--editor/editor_inspector.cpp2
-rw-r--r--editor/editor_log.cpp2
-rw-r--r--editor/editor_node.cpp31
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/editor_sectioned_inspector.cpp2
-rw-r--r--editor/editor_settings.cpp1
-rw-r--r--editor/import/3d/resource_importer_scene.cpp4
-rw-r--r--editor/input_event_configuration_dialog.cpp8
-rw-r--r--editor/plugins/editor_plugin.cpp6
-rw-r--r--editor/plugins/editor_plugin.h2
-rw-r--r--editor/plugins/script_editor_plugin.cpp2
-rw-r--r--editor/plugins/theme_editor_plugin.cpp4
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp2
-rw-r--r--editor/project_manager/project_list.cpp2
-rw-r--r--editor/property_selector.cpp8
-rw-r--r--modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp2
-rw-r--r--modules/multiplayer/editor/replication_editor.cpp2
-rw-r--r--scene/gui/split_container.cpp4
-rw-r--r--scene/main/http_request.cpp2
-rw-r--r--tests/core/string/test_string.h15
34 files changed, 195 insertions, 49 deletions
diff --git a/core/string/ustring.h b/core/string/ustring.h
index a020c7d372..9df2d56e80 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -429,6 +429,8 @@ public:
_FORCE_INLINE_ bool is_empty() const { return length() == 0; }
_FORCE_INLINE_ bool contains(const char *p_str) const { return find(p_str) != -1; }
_FORCE_INLINE_ bool contains(const String &p_str) const { return find(p_str) != -1; }
+ _FORCE_INLINE_ bool containsn(const char *p_str) const { return findn(p_str) != -1; }
+ _FORCE_INLINE_ bool containsn(const String &p_str) const { return findn(p_str) != -1; }
// path functions
bool is_absolute_path() const;
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index c5861f3fbb..9b7777f480 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1707,6 +1707,7 @@ static void _register_variant_builtin_methods() {
bind_string_method(sha256_buffer, sarray(), varray());
bind_string_method(is_empty, sarray(), varray());
bind_string_methodv(contains, static_cast<bool (String::*)(const String &) const>(&String::contains), sarray("what"), varray());
+ bind_string_methodv(containsn, static_cast<bool (String::*)(const String &) const>(&String::containsn), sarray("what"), varray());
bind_string_method(is_absolute_path, sarray(), varray());
bind_string_method(is_relative_path, sarray(), varray());
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/doc/classes/String.xml b/doc/classes/String.xml
index 13bf994e83..450e483f69 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -126,7 +126,7 @@
[/codeblock]
</description>
</method>
- <method name="contains" qualifiers="const" keywords="includes, has">
+ <method name="contains" qualifiers="const">
<return type="bool" />
<param index="0" name="what" type="String" />
<description>
@@ -142,7 +142,15 @@
GD.Print("team".Contains("I")); // Prints false
[/csharp]
[/codeblocks]
- If you need to know where [param what] is within the string, use [method find].
+ If you need to know where [param what] is within the string, use [method find]. See also [method containsn].
+ </description>
+ </method>
+ <method name="containsn" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="what" type="String" />
+ <description>
+ Returns [code]true[/code] if the string contains [param what], [b]ignoring case[/b].
+ If you need to know where [param what] is within the string, use [method findn]. See also [method contains].
</description>
</method>
<method name="count" qualifiers="const">
diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml
index fbb73fd483..76586b7968 100644
--- a/doc/classes/StringName.xml
+++ b/doc/classes/StringName.xml
@@ -126,7 +126,15 @@
GD.Print("team".Contains("I")); // Prints false
[/csharp]
[/codeblocks]
- If you need to know where [param what] is within the string, use [method find].
+ If you need to know where [param what] is within the string, use [method find]. See also [method containsn].
+ </description>
+ </method>
+ <method name="containsn" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="what" type="String" />
+ <description>
+ Returns [code]true[/code] if the string contains [param what], [b]ignoring case[/b].
+ If you need to know where [param what] is within the string, use [method findn]. See also [method contains].
</description>
</method>
<method name="count" qualifiers="const">
diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp
index 1a41b60836..6b5e227782 100644
--- a/drivers/gles3/storage/config.cpp
+++ b/drivers/gles3/storage/config.cpp
@@ -157,7 +157,7 @@ Config::Config() {
continue;
}
- if (renderer.findn(v) != -1) {
+ if (renderer.containsn(v)) {
use_depth_prepass = false;
}
}
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 9c7f4c33d6..f21a0816ca 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -7122,7 +7122,7 @@ void AnimationTrackEditor::_pick_track_select_recursive(TreeItem *p_item, const
NodePath np = p_item->get_metadata(0);
Node *node = get_node_or_null(np);
- if (node && !p_filter.is_empty() && ((String)node->get_name()).findn(p_filter) != -1) {
+ if (node && !p_filter.is_empty() && ((String)node->get_name()).containsn(p_filter)) {
p_select_candidates.push_back(node);
}
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 787cae22cb..478067629e 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -282,7 +282,7 @@ List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_me
List<MethodInfo> ret;
for (const MethodInfo &mi : p_methods) {
- if (!p_search_string.is_empty() && mi.name.findn(p_search_string) == -1) {
+ if (!p_search_string.is_empty() && !mi.name.containsn(p_search_string)) {
continue;
}
diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.h b/editor/debugger/debug_adapter/debug_adapter_parser.h
index 9860e96498..8fd2c2b3e2 100644
--- a/editor/debugger/debug_adapter/debug_adapter_parser.h
+++ b/editor/debugger/debug_adapter/debug_adapter_parser.h
@@ -50,7 +50,7 @@ private:
if (p_path.contains("\\")) {
String project_path = ProjectSettings::get_singleton()->get_resource_path();
String path = p_path.replace("\\", "/");
- return path.findn(project_path) != -1;
+ return path.containsn(project_path);
}
return p_path.begins_with(ProjectSettings::get_singleton()->get_resource_path());
}
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_help_search.cpp b/editor/editor_help_search.cpp
index 02b5146052..1e0c0e9fd8 100644
--- a/editor/editor_help_search.cpp
+++ b/editor/editor_help_search.cpp
@@ -40,7 +40,7 @@
bool EditorHelpSearch::_all_terms_in_name(const Vector<String> &p_terms, const String &p_name) const {
for (int i = 0; i < p_terms.size(); i++) {
- if (p_name.findn(p_terms[i]) < 0) {
+ if (!p_name.containsn(p_terms[i])) {
return false;
}
}
@@ -109,7 +109,7 @@ Dictionary EditorHelpSearch::_native_search_cb(const String &p_search_string, in
if (class_doc.name.is_empty()) {
continue;
}
- if (class_doc.name.findn(term) > -1) {
+ if (class_doc.name.containsn(term)) {
ret[vformat("class_name:%s", class_doc.name)] = class_doc.name;
}
if (term.length() > 1 || term == "@") {
@@ -746,7 +746,7 @@ bool EditorHelpSearch::Runner::_match_string(const String &p_term, const String
if (search_flags & SEARCH_CASE_SENSITIVE) {
return p_string.contains(p_term);
} else {
- return p_string.findn(p_term) > -1;
+ return p_string.containsn(p_term);
}
}
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 7c69ac589a..9599479c83 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -52,7 +52,7 @@
#include "scene/resources/style_box_flat.h"
bool EditorInspector::_property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) {
- if (p_property_path.findn(p_filter) != -1) {
+ if (p_property_path.containsn(p_filter)) {
return true;
}
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index b8da550d8a..ab4950f01b 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -322,7 +322,7 @@ void EditorLog::_rebuild_log() {
bool EditorLog::_check_display_message(LogMessage &p_message) {
bool filter_active = type_filter_map[p_message.type]->is_active();
String search_text = search_box->get_text();
- bool search_match = search_text.is_empty() || p_message.text.findn(search_text) > -1;
+ bool search_match = search_text.is_empty() || p_message.text.containsn(search_text);
return filter_active && search_match;
}
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index b501835cd1..d7f197b569 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_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp
index f13af8e4ca..22120bfc01 100644
--- a/editor/editor_sectioned_inspector.cpp
+++ b/editor/editor_sectioned_inspector.cpp
@@ -36,7 +36,7 @@
#include "editor/themes/editor_scale.h"
static bool _property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) {
- if (p_property_path.findn(p_filter) != -1) {
+ if (p_property_path.containsn(p_filter)) {
return true;
}
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index e3dfbc45e8..c019d46034 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/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp
index 077625b3f1..36cbf253cc 100644
--- a/editor/import/3d/resource_importer_scene.cpp
+++ b/editor/import/3d/resource_importer_scene.cpp
@@ -387,7 +387,7 @@ static bool _teststr(const String &p_what, const String &p_str) {
what = what.substr(0, what.length() - 1);
}
- if (what.findn("$" + p_str) != -1) { //blender and other stuff
+ if (what.containsn("$" + p_str)) { // Blender and other stuff.
return true;
}
if (what.to_lower().ends_with("-" + p_str)) { //collada only supports "_" and "-" besides letters
@@ -410,7 +410,7 @@ static String _fixstr(const String &p_what, const String &p_str) {
String end = p_what.substr(what.length(), p_what.length() - what.length());
- if (what.findn("$" + p_str) != -1) { //blender and other stuff
+ if (what.containsn("$" + p_str)) { // Blender and other stuff.
return what.replace("$" + p_str, "") + end;
}
if (what.to_lower().ends_with("-" + p_str)) { //collada only supports "_" and "-" besides letters
diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp
index a667f94a62..143de82765 100644
--- a/editor/input_event_configuration_dialog.cpp
+++ b/editor/input_event_configuration_dialog.cpp
@@ -285,7 +285,7 @@ void InputEventConfigurationDialog::_update_input_list() {
for (int i = 0; i < keycode_get_count(); i++) {
String name = keycode_get_name_by_index(i);
- if (!search_term.is_empty() && name.findn(search_term) == -1) {
+ if (!search_term.is_empty() && !name.containsn(search_term)) {
continue;
}
@@ -309,7 +309,7 @@ void InputEventConfigurationDialog::_update_input_list() {
mb->set_button_index(mouse_buttons[i]);
String desc = EventListenerLineEdit::get_event_text(mb, false);
- if (!search_term.is_empty() && desc.findn(search_term) == -1) {
+ if (!search_term.is_empty() && !desc.containsn(search_term)) {
continue;
}
@@ -332,7 +332,7 @@ void InputEventConfigurationDialog::_update_input_list() {
joyb->set_button_index((JoyButton)i);
String desc = EventListenerLineEdit::get_event_text(joyb, false);
- if (!search_term.is_empty() && desc.findn(search_term) == -1) {
+ if (!search_term.is_empty() && !desc.containsn(search_term)) {
continue;
}
@@ -358,7 +358,7 @@ void InputEventConfigurationDialog::_update_input_list() {
joym->set_axis_value(direction);
String desc = EventListenerLineEdit::get_event_text(joym, false);
- if (!search_term.is_empty() && desc.findn(search_term) == -1) {
+ if (!search_term.is_empty() && !desc.containsn(search_term)) {
continue;
}
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/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index e1de10284a..723dbf5d6c 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -374,7 +374,7 @@ void ScriptEditorQuickOpen::_update_search() {
for (int i = 0; i < functions.size(); i++) {
String file = functions[i];
- if ((search_box->get_text().is_empty() || file.findn(search_box->get_text()) != -1)) {
+ if ((search_box->get_text().is_empty() || file.containsn(search_box->get_text()))) {
TreeItem *ti = search_options->create_item(root);
ti->set_text(0, file);
if (root->get_first_child() == ti) {
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 166ed05748..4cc88aab34 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -106,7 +106,7 @@ void ThemeItemImportTree::_update_items_tree() {
type_node->set_checked(IMPORT_ITEM_DATA, false);
type_node->set_editable(IMPORT_ITEM_DATA, true);
- bool is_matching_filter = (filter_text.is_empty() || type_name.findn(filter_text) > -1);
+ bool is_matching_filter = (filter_text.is_empty() || type_name.containsn(filter_text));
bool has_filtered_items = false;
for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) {
@@ -120,7 +120,7 @@ void ThemeItemImportTree::_update_items_tree() {
for (const StringName &F : names) {
String item_name = (String)F;
- bool is_item_matching_filter = (item_name.findn(filter_text) > -1);
+ bool is_item_matching_filter = item_name.containsn(filter_text);
if (!filter_text.is_empty() && !is_matching_filter && !is_item_matching_filter) {
continue;
}
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index e3f7aa5e6d..35db52a42e 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -1957,7 +1957,7 @@ void VisualShaderEditor::_update_options_menu() {
}
for (int i = 0; i < add_options.size(); i++) {
- if (!use_filter || add_options[i].name.findn(filter) != -1) {
+ if (!use_filter || add_options[i].name.containsn(filter)) {
// port type filtering
if (members_output_port_type != VisualShaderNode::PORT_TYPE_MAX || members_input_port_type != VisualShaderNode::PORT_TYPE_MAX) {
Ref<VisualShaderNode> vsn;
diff --git a/editor/project_manager/project_list.cpp b/editor/project_manager/project_list.cpp
index d125754dd7..b7520abc99 100644
--- a/editor/project_manager/project_list.cpp
+++ b/editor/project_manager/project_list.cpp
@@ -545,7 +545,7 @@ void ProjectList::sort_projects() {
}
// When searching, display projects whose name or path contain the search term and whose tags match the searched tags.
- item_visible = !missing_tags && (search_term.is_empty() || item.project_name.findn(search_term) != -1 || search_path.findn(search_term) != -1);
+ item_visible = !missing_tags && (search_term.is_empty() || item.project_name.containsn(search_term) || search_path.containsn(search_term));
}
item.control->set_visible(item_visible);
diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp
index eef68d918a..77ab629ba6 100644
--- a/editor/property_selector.cpp
+++ b/editor/property_selector.cpp
@@ -190,7 +190,7 @@ void PropertySelector::_update_search() {
continue;
}
- if (!search_box->get_text().is_empty() && E.name.findn(search_text) == -1) {
+ if (!search_box->get_text().is_empty() && !E.name.containsn(search_text)) {
continue;
}
@@ -203,7 +203,7 @@ void PropertySelector::_update_search() {
item->set_metadata(0, E.name);
item->set_icon(0, type_icons[E.type]);
- if (!found && !search_box->get_text().is_empty() && E.name.findn(search_text) != -1) {
+ if (!found && !search_box->get_text().is_empty() && E.name.containsn(search_text)) {
item->select(0);
found = true;
}
@@ -281,7 +281,7 @@ void PropertySelector::_update_search() {
continue;
}
- if (!search_box->get_text().is_empty() && name.findn(search_text) == -1) {
+ if (!search_box->get_text().is_empty() && !name.containsn(search_text)) {
continue;
}
@@ -330,7 +330,7 @@ void PropertySelector::_update_search() {
item->set_metadata(0, name);
item->set_selectable(0, true);
- if (!found && !search_box->get_text().is_empty() && name.findn(search_text) != -1) {
+ if (!found && !search_box->get_text().is_empty() && name.containsn(search_text)) {
item->select(0);
found = true;
}
diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp b/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp
index d11300343a..fc184c9342 100644
--- a/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp
@@ -77,7 +77,7 @@ void EditorSceneExporterGLTFSettings::_get_property_list(List<PropertyInfo> *p_l
for (PropertyInfo prop : _property_list) {
if (prop.name == "lossy_quality") {
String image_format = get("image_format");
- bool is_image_format_lossy = image_format == "JPEG" || image_format.findn("Lossy") != -1;
+ bool is_image_format_lossy = image_format == "JPEG" || image_format.containsn("Lossy");
prop.usage = is_image_format_lossy ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_STORAGE;
}
p_list->push_back(prop);
diff --git a/modules/multiplayer/editor/replication_editor.cpp b/modules/multiplayer/editor/replication_editor.cpp
index 73e53a6a07..08e7ba360a 100644
--- a/modules/multiplayer/editor/replication_editor.cpp
+++ b/modules/multiplayer/editor/replication_editor.cpp
@@ -81,7 +81,7 @@ void ReplicationEditor::_pick_node_select_recursive(TreeItem *p_item, const Stri
NodePath np = p_item->get_metadata(0);
Node *node = get_node(np);
- if (!p_filter.is_empty() && ((String)node->get_name()).findn(p_filter) != -1) {
+ if (!p_filter.is_empty() && ((String)node->get_name()).containsn(p_filter)) {
p_select_candidates.push_back(node);
}
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;
}
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 0f89784369..3469b806a6 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -239,7 +239,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
String new_request;
for (const String &E : rheaders) {
- if (E.findn("Location: ") != -1) {
+ if (E.containsn("Location: ")) {
new_request = E.substr(9, E.length()).strip_edges();
}
}
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index 18828c3b70..cf57183a02 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -297,6 +297,19 @@ TEST_CASE("[String] Contains") {
CHECK(!s.contains(String("\\char_test.tscn")));
}
+TEST_CASE("[String] Contains case insensitive") {
+ String s = "C:\\Godot\\project\\string_test.tscn";
+ CHECK(s.containsn("Godot"));
+ CHECK(s.containsn("godot"));
+ CHECK(s.containsn(String("Project\\string_test")));
+ CHECK(s.containsn(String("\\string_Test.tscn")));
+
+ CHECK(!s.containsn("Godoh"));
+ CHECK(!s.containsn("godoh"));
+ CHECK(!s.containsn(String("project\\string test")));
+ CHECK(!s.containsn(String("\\char_test.tscn")));
+}
+
TEST_CASE("[String] Test chr") {
CHECK(String::chr('H') == "H");
CHECK(String::chr(0x3012)[0] == 0x3012);
@@ -376,7 +389,7 @@ TEST_CASE("[String] Find") {
MULTICHECK_STRING_INT_EQ(s, rfind, "", 15, -1);
}
-TEST_CASE("[String] Find no case") {
+TEST_CASE("[String] Find case insensitive") {
String s = "Pretty Whale Whale";
MULTICHECK_STRING_EQ(s, findn, "WHA", 7);
MULTICHECK_STRING_INT_EQ(s, findn, "WHA", 9, 13);