summaryrefslogtreecommitdiff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/animation_track_editor.cpp22
-rw-r--r--editor/editor_help_search.cpp64
-rw-r--r--editor/editor_help_search.h9
-rw-r--r--editor/editor_inspector.cpp14
-rw-r--r--editor/editor_node.cpp13
-rw-r--r--editor/editor_path.cpp26
-rw-r--r--editor/editor_themes.cpp24
-rw-r--r--editor/icons/MemberAnnotation.svg1
-rw-r--r--editor/inspector_dock.cpp14
-rw-r--r--editor/multi_node_edit.cpp148
-rw-r--r--editor/multi_node_edit.h10
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp4
-rw-r--r--editor/plugins/animation_state_machine_editor.h1
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp2
-rw-r--r--editor/plugins/script_text_editor.cpp2
-rw-r--r--editor/project_manager.cpp4
16 files changed, 255 insertions, 103 deletions
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 4991b2cfaf..e64fc6e9e3 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -52,13 +52,9 @@ public:
bool setting = false;
bool animation_read_only = false;
- bool _hide_script_from_inspector() {
- return true;
- }
-
- bool _dont_undo_redo() {
- return true;
- }
+ bool _hide_script_from_inspector() { return true; }
+ bool _hide_metadata_from_inspector() { return true; }
+ bool _dont_undo_redo() { return true; }
bool _is_read_only() {
return animation_read_only;
@@ -68,6 +64,7 @@ public:
ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationTrackKeyEdit::_update_obj);
ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationTrackKeyEdit::_key_ofs_changed);
ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationTrackKeyEdit::_hide_script_from_inspector);
+ ClassDB::bind_method(D_METHOD("_hide_metadata_from_inspector"), &AnimationTrackKeyEdit::_hide_metadata_from_inspector);
ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationTrackKeyEdit::get_root_path);
ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationTrackKeyEdit::_dont_undo_redo);
ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationTrackKeyEdit::_is_read_only);
@@ -719,13 +716,9 @@ public:
bool setting = false;
bool animation_read_only = false;
- bool _hide_script_from_inspector() {
- return true;
- }
-
- bool _dont_undo_redo() {
- return true;
- }
+ bool _hide_script_from_inspector() { return true; }
+ bool _hide_metadata_from_inspector() { return true; }
+ bool _dont_undo_redo() { return true; }
bool _is_read_only() {
return animation_read_only;
@@ -735,6 +728,7 @@ public:
ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationMultiTrackKeyEdit::_update_obj);
ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationMultiTrackKeyEdit::_key_ofs_changed);
ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationMultiTrackKeyEdit::_hide_script_from_inspector);
+ ClassDB::bind_method(D_METHOD("_hide_metadata_from_inspector"), &AnimationMultiTrackKeyEdit::_hide_metadata_from_inspector);
ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationMultiTrackKeyEdit::get_root_path);
ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationMultiTrackKeyEdit::_dont_undo_redo);
ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationMultiTrackKeyEdit::_is_read_only);
diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp
index 2e35f21e47..af0cff9ad6 100644
--- a/editor/editor_help_search.cpp
+++ b/editor/editor_help_search.cpp
@@ -231,6 +231,7 @@ EditorHelpSearch::EditorHelpSearch() {
filter_combo->add_item(TTR("Methods Only"), SEARCH_METHODS);
filter_combo->add_item(TTR("Operators Only"), SEARCH_OPERATORS);
filter_combo->add_item(TTR("Signals Only"), SEARCH_SIGNALS);
+ filter_combo->add_item(TTR("Annotations Only"), SEARCH_ANNOTATIONS);
filter_combo->add_item(TTR("Constants Only"), SEARCH_CONSTANTS);
filter_combo->add_item(TTR("Properties Only"), SEARCH_PROPERTIES);
filter_combo->add_item(TTR("Theme Properties Only"), SEARCH_THEME_ITEMS);
@@ -339,8 +340,9 @@ bool EditorHelpSearch::Runner::_phase_match_classes() {
match.name = (term.is_empty() && (!class_doc.is_script_doc || class_doc.name[0] != '\"')) || _match_string(term, class_doc.name);
}
- // Match members if the term is long enough.
- if (term.length() > 1) {
+ // Match members only if the term is long enough, to avoid slow performance from building a large tree.
+ // Make an exception for annotations, since there are not that many of them.
+ if (term.length() > 1 || term == "@") {
if (search_flags & SEARCH_CONSTRUCTORS) {
for (int i = 0; i < class_doc.constructors.size(); i++) {
String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? class_doc.constructors[i].name : class_doc.constructors[i].name.to_lower();
@@ -402,6 +404,13 @@ bool EditorHelpSearch::Runner::_phase_match_classes() {
}
}
}
+ if (search_flags & SEARCH_ANNOTATIONS) {
+ for (int i = 0; i < class_doc.annotations.size(); i++) {
+ if (_match_string(term, class_doc.annotations[i].name)) {
+ match.annotations.push_back(const_cast<DocData::MethodDoc *>(&class_doc.annotations[i]));
+ }
+ }
+ }
matches[class_doc.name] = match;
}
matches[class_doc.name] = match;
@@ -485,6 +494,10 @@ bool EditorHelpSearch::Runner::_phase_member_items() {
for (int i = 0; i < match.theme_properties.size(); i++) {
_create_theme_property_item(parent, match.doc, match.theme_properties[i]);
}
+ for (int i = 0; i < match.annotations.size(); i++) {
+ // Hide the redundant leading @ symbol.
+ _create_annotation_item(parent, match.doc, match.annotations[i]->name.substr(1), match.annotations[i]);
+ }
++iterator_match;
return !iterator_match;
@@ -523,6 +536,22 @@ void EditorHelpSearch::Runner::_match_item(TreeItem *p_item, const String &p_tex
}
}
+String EditorHelpSearch::Runner::_build_method_tooltip(const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) const {
+ String tooltip = p_doc->return_type + " " + p_class_doc->name + "." + p_doc->name + "(";
+ for (int i = 0; i < p_doc->arguments.size(); i++) {
+ const DocData::ArgumentDoc &arg = p_doc->arguments[i];
+ tooltip += arg.type + " " + arg.name;
+ if (!arg.default_value.is_empty()) {
+ tooltip += " = " + arg.default_value;
+ }
+ if (i < p_doc->arguments.size() - 1) {
+ tooltip += ", ";
+ }
+ }
+ tooltip += ")";
+ return tooltip;
+}
+
TreeItem *EditorHelpSearch::Runner::_create_class_hierarchy(const ClassMatch &p_match) {
if (p_match.doc->name.is_empty()) {
return nullptr;
@@ -576,37 +605,20 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const
}
TreeItem *EditorHelpSearch::Runner::_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc) {
- String tooltip = p_doc->return_type + " " + p_class_doc->name + "." + p_doc->name + "(";
- for (int i = 0; i < p_doc->arguments.size(); i++) {
- const DocData::ArgumentDoc &arg = p_doc->arguments[i];
- tooltip += arg.type + " " + arg.name;
- if (!arg.default_value.is_empty()) {
- tooltip += " = " + arg.default_value;
- }
- if (i < p_doc->arguments.size() - 1) {
- tooltip += ", ";
- }
- }
- tooltip += ")";
+ String tooltip = _build_method_tooltip(p_class_doc, p_doc);
return _create_member_item(p_parent, p_class_doc->name, "MemberMethod", p_doc->name, p_text, TTRC("Method"), "method", tooltip);
}
TreeItem *EditorHelpSearch::Runner::_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) {
- String tooltip = p_doc->return_type + " " + p_class_doc->name + "." + p_doc->name + "(";
- for (int i = 0; i < p_doc->arguments.size(); i++) {
- const DocData::ArgumentDoc &arg = p_doc->arguments[i];
- tooltip += arg.type + " " + arg.name;
- if (!arg.default_value.is_empty()) {
- tooltip += " = " + arg.default_value;
- }
- if (i < p_doc->arguments.size() - 1) {
- tooltip += ", ";
- }
- }
- tooltip += ")";
+ String tooltip = _build_method_tooltip(p_class_doc, p_doc);
return _create_member_item(p_parent, p_class_doc->name, "MemberSignal", p_doc->name, p_doc->name, TTRC("Signal"), "signal", tooltip);
}
+TreeItem *EditorHelpSearch::Runner::_create_annotation_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc) {
+ String tooltip = _build_method_tooltip(p_class_doc, p_doc);
+ return _create_member_item(p_parent, p_class_doc->name, "MemberAnnotation", p_doc->name, p_text, TTRC("Annotation"), "annotation", tooltip);
+}
+
TreeItem *EditorHelpSearch::Runner::_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc) {
String tooltip = p_class_doc->name + "." + p_doc->name;
return _create_member_item(p_parent, p_class_doc->name, "MemberConstant", p_doc->name, p_doc->name, TTRC("Constant"), "constant", tooltip);
diff --git a/editor/editor_help_search.h b/editor/editor_help_search.h
index 3f17c992ac..26abaec6e6 100644
--- a/editor/editor_help_search.h
+++ b/editor/editor_help_search.h
@@ -50,7 +50,8 @@ class EditorHelpSearch : public ConfirmationDialog {
SEARCH_CONSTANTS = 1 << 5,
SEARCH_PROPERTIES = 1 << 6,
SEARCH_THEME_ITEMS = 1 << 7,
- SEARCH_ALL = SEARCH_CLASSES | SEARCH_CONSTRUCTORS | SEARCH_METHODS | SEARCH_OPERATORS | SEARCH_SIGNALS | SEARCH_CONSTANTS | SEARCH_PROPERTIES | SEARCH_THEME_ITEMS,
+ SEARCH_ANNOTATIONS = 1 << 8,
+ SEARCH_ALL = SEARCH_CLASSES | SEARCH_CONSTRUCTORS | SEARCH_METHODS | SEARCH_OPERATORS | SEARCH_SIGNALS | SEARCH_CONSTANTS | SEARCH_PROPERTIES | SEARCH_THEME_ITEMS | SEARCH_ANNOTATIONS,
SEARCH_CASE_SENSITIVE = 1 << 29,
SEARCH_SHOW_HIERARCHY = 1 << 30
};
@@ -108,9 +109,10 @@ class EditorHelpSearch::Runner : public RefCounted {
Vector<DocData::ConstantDoc *> constants;
Vector<DocData::PropertyDoc *> properties;
Vector<DocData::ThemeItemDoc *> theme_properties;
+ Vector<DocData::MethodDoc *> annotations;
bool required() {
- return name || methods.size() || signals.size() || constants.size() || properties.size() || theme_properties.size();
+ return name || methods.size() || signals.size() || constants.size() || properties.size() || theme_properties.size() || annotations.size();
}
};
@@ -141,12 +143,15 @@ class EditorHelpSearch::Runner : public RefCounted {
bool _phase_member_items();
bool _phase_select_match();
+ String _build_method_tooltip(const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) const;
+
bool _match_string(const String &p_term, const String &p_string) const;
void _match_item(TreeItem *p_item, const String &p_text);
TreeItem *_create_class_hierarchy(const ClassMatch &p_match);
TreeItem *_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray);
TreeItem *_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc);
TreeItem *_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc);
+ TreeItem *_create_annotation_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc);
TreeItem *_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc);
TreeItem *_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc);
TreeItem *_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ThemeItemDoc *p_doc);
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index bdf1c314f8..77110044ec 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -2711,6 +2711,11 @@ void EditorInspector::update_tree() {
continue;
}
+ // Hide the "MultiNodeEdit" category for MultiNodeEdit.
+ if (Object::cast_to<MultiNodeEdit>(object) && p.name == "MultiNodeEdit") {
+ continue;
+ }
+
// Iterate over remaining properties. If no properties in category, skip the category.
List<PropertyInfo>::Element *N = E_property->next();
bool valid = true;
@@ -2819,6 +2824,11 @@ void EditorInspector::update_tree() {
continue;
}
+ if (p.name.begins_with("metadata/") && bool(object->call("_hide_metadata_from_inspector"))) {
+ // Hide metadata from inspector if required.
+ continue;
+ }
+
// Get the path for property.
String path = p.name;
@@ -3089,6 +3099,8 @@ void EditorInspector::update_tree() {
StringName classname = doc_name == "" ? object->get_class_name() : doc_name;
if (!object_class.is_empty()) {
classname = object_class;
+ } else if (Object::cast_to<MultiNodeEdit>(object)) {
+ classname = Object::cast_to<MultiNodeEdit>(object)->get_edited_class_name();
}
StringName propname = property_prefix + p.name;
@@ -3242,7 +3254,7 @@ void EditorInspector::update_tree() {
}
}
- if (!hide_metadata) {
+ if (!hide_metadata && !object->call("_hide_metadata_from_inspector")) {
// Add 4px of spacing between the "Add Metadata" button and the content above it.
Control *spacer = memnew(Control);
spacer->set_custom_minimum_size(Size2(0, 4) * EDSCALE);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 58377eef0f..7a7576b241 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -762,11 +762,8 @@ void EditorNode::_notification(int p_what) {
gui_base->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("Background"), SNAME("EditorStyles")));
scene_root_parent->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("Content"), SNAME("EditorStyles")));
- bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("panel"), SNAME("TabContainer")));
-
+ bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("BottomPanel"), SNAME("EditorStyles")));
tabbar_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("tabbar_background"), SNAME("TabContainer")));
- scene_tabs->add_theme_style_override("tab_selected", gui_base->get_theme_stylebox(SNAME("SceneTabFG"), SNAME("EditorStyles")));
- scene_tabs->add_theme_style_override("tab_unselected", gui_base->get_theme_stylebox(SNAME("SceneTabBG"), SNAME("EditorStyles")));
main_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles")));
}
@@ -5475,7 +5472,7 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) {
// This is the debug panel which uses tabs, so the top section should be smaller.
bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("BottomPanelDebuggerOverride"), SNAME("EditorStyles")));
} else {
- bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("panel"), SNAME("TabContainer")));
+ bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("BottomPanel"), SNAME("EditorStyles")));
}
center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE);
center_split->set_collapsed(false);
@@ -5485,7 +5482,7 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) {
bottom_panel_raise->show();
} else {
- bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("panel"), SNAME("TabContainer")));
+ bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("BottomPanel"), SNAME("EditorStyles")));
bottom_panel_items[p_idx].button->set_pressed(false);
bottom_panel_items[p_idx].control->set_visible(false);
center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
@@ -6528,8 +6525,6 @@ EditorNode::EditorNode() {
tabbar_panel->add_child(tabbar_container);
scene_tabs = memnew(TabBar);
- scene_tabs->add_theme_style_override("tab_selected", gui_base->get_theme_stylebox(SNAME("SceneTabFG"), SNAME("EditorStyles")));
- scene_tabs->add_theme_style_override("tab_unselected", gui_base->get_theme_stylebox(SNAME("SceneTabBG"), SNAME("EditorStyles")));
scene_tabs->set_select_with_rmb(true);
scene_tabs->add_tab("unsaved");
scene_tabs->set_tab_close_display_policy((TabBar::CloseButtonDisplayPolicy)EDITOR_GET("interface/scene_tabs/display_close_button").operator int());
@@ -7093,7 +7088,7 @@ EditorNode::EditorNode() {
// Bottom panels.
bottom_panel = memnew(PanelContainer);
- bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("panel"), SNAME("TabContainer")));
+ bottom_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("BottomPanel"), SNAME("EditorStyles")));
center_split->add_child(bottom_panel);
center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
diff --git a/editor/editor_path.cpp b/editor/editor_path.cpp
index a8f8f7ab97..d1f41dad84 100644
--- a/editor/editor_path.cpp
+++ b/editor/editor_path.cpp
@@ -33,6 +33,7 @@
#include "editor/editor_data.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
+#include "editor/multi_node_edit.h"
void EditorPath::_add_children_to_popup(Object *p_obj, int p_depth) {
if (p_depth > 8) {
@@ -121,14 +122,22 @@ void EditorPath::update_path() {
continue;
}
- Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(obj);
+ Ref<Texture2D> icon;
+ if (Object::cast_to<MultiNodeEdit>(obj)) {
+ icon = EditorNode::get_singleton()->get_class_icon(Object::cast_to<MultiNodeEdit>(obj)->get_edited_class_name());
+ } else {
+ icon = EditorNode::get_singleton()->get_object_icon(obj);
+ }
+
if (icon.is_valid()) {
current_object_icon->set_texture(icon);
}
if (i == history->get_path_size() - 1) {
String name;
- if (Object::cast_to<Resource>(obj)) {
+ if (obj->has_method("_get_editor_name")) {
+ name = 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()) {
name = r->get_path().get_file();
@@ -149,7 +158,7 @@ void EditorPath::update_path() {
name = obj->get_class();
}
- current_object_label->set_text(" " + name); // An extra space so the text is not too close of the icon.
+ current_object_label->set_text(name);
set_tooltip_text(obj->get_class());
}
}
@@ -161,12 +170,12 @@ void EditorPath::clear_path() {
current_object_label->set_text("");
current_object_icon->set_texture(nullptr);
- sub_objects_icon->set_visible(false);
+ sub_objects_icon->hide();
}
void EditorPath::enable_path() {
set_disabled(false);
- sub_objects_icon->set_visible(true);
+ sub_objects_icon->show();
}
void EditorPath::_id_pressed(int p_idx) {
@@ -186,7 +195,7 @@ void EditorPath::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
update_path();
- sub_objects_icon->set_texture(get_theme_icon(SNAME("select_arrow"), SNAME("Tree")));
+ sub_objects_icon->set_texture(get_theme_icon(SNAME("arrow"), SNAME("OptionButton")));
current_object_label->add_theme_font_override("font", get_theme_font(SNAME("main"), SNAME("EditorFonts")));
} break;
@@ -216,13 +225,12 @@ EditorPath::EditorPath(EditorSelectionHistory *p_history) {
main_hb->add_child(current_object_icon);
current_object_label = memnew(Label);
- current_object_label->set_clip_text(true);
- current_object_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT);
+ current_object_label->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
current_object_label->set_h_size_flags(SIZE_EXPAND_FILL);
main_hb->add_child(current_object_label);
sub_objects_icon = memnew(TextureRect);
- sub_objects_icon->set_visible(false);
+ sub_objects_icon->hide();
sub_objects_icon->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
main_hb->add_child(sub_objects_icon);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 883d31d3f9..6934c2d13c 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -666,9 +666,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
style_tab_base->set_corner_radius(CORNER_BOTTOM_LEFT, 0);
style_tab_base->set_corner_radius(CORNER_BOTTOM_RIGHT, 0);
- // Prevent visible artifacts and cover the top-left rounded corner of the panel below the tab if selected
- // We can't prevent them with both rounded corners and non-zero border width, though
- style_tab_base->set_expand_margin_size(SIDE_BOTTOM, corner_width > 0 ? corner_width : border_width);
// When using a border width greater than 0, visually line up the left of the selected tab with the underlying panel.
style_tab_base->set_expand_margin_size(SIDE_LEFT, -border_width);
@@ -1215,11 +1212,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("line_separation", "ItemList", 3 * EDSCALE);
// TabBar & TabContainer
- Ref<StyleBoxFlat> style_tabbar_background = make_flat_stylebox(dark_color_1, 0, 0, 0, 0);
- style_tabbar_background->set_expand_margin_size(SIDE_BOTTOM, corner_width > 0 ? corner_width : border_width);
- style_tabbar_background->set_corner_detail(corner_width);
- style_tabbar_background->set_corner_radius(CORNER_TOP_LEFT, corner_radius * EDSCALE);
- style_tabbar_background->set_corner_radius(CORNER_TOP_RIGHT, corner_radius * EDSCALE);
+ Ref<StyleBoxFlat> style_tabbar_background = make_flat_stylebox(dark_color_1, 0, 0, 0, 0, corner_radius * EDSCALE);
+ style_tabbar_background->set_corner_radius(CORNER_BOTTOM_LEFT, 0);
+ style_tabbar_background->set_corner_radius(CORNER_BOTTOM_RIGHT, 0);
theme->set_stylebox("tabbar_background", "TabContainer", style_tabbar_background);
theme->set_stylebox("tab_selected", "TabContainer", style_tab_selected);
@@ -1230,8 +1225,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("tab_disabled", "TabBar", style_tab_disabled);
theme->set_stylebox("button_pressed", "TabBar", style_menu);
theme->set_stylebox("button_highlight", "TabBar", style_menu);
- theme->set_stylebox("SceneTabFG", "EditorStyles", style_tab_selected);
- theme->set_stylebox("SceneTabBG", "EditorStyles", style_tab_unselected);
theme->set_color("font_selected_color", "TabContainer", font_color);
theme->set_color("font_unselected_color", "TabContainer", font_disabled_color);
theme->set_color("font_selected_color", "TabBar", font_color);
@@ -1251,22 +1244,28 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_icon("decrement_highlight", "TabContainer", theme->get_icon(SNAME("GuiScrollArrowLeftHl"), SNAME("EditorIcons")));
theme->set_icon("drop_mark", "TabContainer", theme->get_icon(SNAME("GuiTabDropMark"), SNAME("EditorIcons")));
theme->set_icon("drop_mark", "TabBar", theme->get_icon(SNAME("GuiTabDropMark"), SNAME("EditorIcons")));
+ theme->set_constant("side_margin", "TabContainer", 0);
theme->set_constant("h_separation", "TabBar", 4 * EDSCALE);
- // Content of each tab
+ // Content of each tab.
Ref<StyleBoxFlat> style_content_panel = style_default->duplicate();
style_content_panel->set_border_color(dark_color_3);
style_content_panel->set_border_width_all(border_width);
style_content_panel->set_border_width(Side::SIDE_TOP, 0);
style_content_panel->set_corner_radius(CORNER_TOP_LEFT, 0);
style_content_panel->set_corner_radius(CORNER_TOP_RIGHT, 0);
- // compensate the border
+ // Compensate for the border.
style_content_panel->set_default_margin(SIDE_TOP, (2 + margin_size_extra) * EDSCALE);
style_content_panel->set_default_margin(SIDE_RIGHT, margin_size_extra * EDSCALE);
style_content_panel->set_default_margin(SIDE_BOTTOM, margin_size_extra * EDSCALE);
style_content_panel->set_default_margin(SIDE_LEFT, margin_size_extra * EDSCALE);
theme->set_stylebox("panel", "TabContainer", style_content_panel);
+ // Bottom panel.
+ Ref<StyleBoxFlat> style_bottom_panel = style_content_panel->duplicate();
+ style_bottom_panel->set_corner_radius_all(corner_radius * EDSCALE);
+ theme->set_stylebox("BottomPanel", "EditorStyles", style_bottom_panel);
+
// TabContainerOdd can be used on tabs against the base color background (e.g. nested tabs).
theme->set_type_variation("TabContainerOdd", "TabContainer");
@@ -1345,7 +1344,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("normal", "TextEdit", style_line_edit);
theme->set_stylebox("focus", "TextEdit", style_widget_focus);
theme->set_stylebox("read_only", "TextEdit", style_line_edit_disabled);
- theme->set_constant("side_margin", "TabContainer", 0);
theme->set_icon("tab", "TextEdit", theme->get_icon(SNAME("GuiTab"), SNAME("EditorIcons")));
theme->set_icon("space", "TextEdit", theme->get_icon(SNAME("GuiSpace"), SNAME("EditorIcons")));
theme->set_color("font_color", "TextEdit", font_color);
diff --git a/editor/icons/MemberAnnotation.svg b/editor/icons/MemberAnnotation.svg
new file mode 100644
index 0000000000..c73ebf7b9b
--- /dev/null
+++ b/editor/icons/MemberAnnotation.svg
@@ -0,0 +1 @@
+<svg width="16" height="16" version="1.0" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><script id="custom-useragent-string-page-script"/><path d="m13.821 12.756c-5.0033 3.9148-12.551 2.248-12.49-4.538 0.67424-11.471 17.312-7.4502 12.446 2.1173-1.0549 1.1955-2.0737 1.4617-3.1983 0.4329-0.21023-0.19282-0.44783-1.1594-0.3819-1.5089 0.35827-1.8946 1.0885-4.0778-0.72151-4.7234-2.4171-0.86457-4.5592 1.6495-4.9697 4.0193-0.47396 2.7343 2.284 3.3749 4.1487 1.9879 0.4553-0.36324 1.6433-1.3796 1.6806-1.9742" fill="none" stroke="#e0e0e0" stroke-linejoin="round" stroke-width="1.4928"/></svg>
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index 7af04e17c1..74fdbdebd7 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -311,7 +311,6 @@ void InspectorDock::_prepare_history() {
history_menu->get_popup()->clear();
- Ref<Texture2D> base_icon = get_theme_icon(SNAME("Object"), SNAME("EditorIcons"));
HashSet<ObjectID> already;
for (int i = editor_history->get_history_len() - 1; i >= history_to; i--) {
ObjectID id = editor_history->get_history_obj(i);
@@ -325,13 +324,12 @@ void InspectorDock::_prepare_history() {
already.insert(id);
- Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(obj, "");
- if (icon.is_null()) {
- icon = base_icon;
- }
+ Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(obj, "Object");
String text;
- if (Object::cast_to<Resource>(obj)) {
+ 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();
@@ -349,14 +347,14 @@ void InspectorDock::_prepare_history() {
}
if (i == editor_history->get_history_pos() && current) {
- text = "[" + text + "]";
+ 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
+ // 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) {
diff --git a/editor/multi_node_edit.cpp b/editor/multi_node_edit.cpp
index 70cc54668d..a386fba84d 100644
--- a/editor/multi_node_edit.cpp
+++ b/editor/multi_node_edit.cpp
@@ -46,7 +46,7 @@ bool MultiNodeEdit::_set_impl(const StringName &p_name, const Variant &p_value,
String name = p_name;
- if (name == "scripts") { // script set is intercepted at object level (check Variant Object::get() ) ,so use a different name
+ if (name == "scripts") { // Script set is intercepted at object level (check Variant Object::get()), so use a different name.
name = "script";
}
@@ -57,13 +57,9 @@ bool MultiNodeEdit::_set_impl(const StringName &p_name, const Variant &p_value,
Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
- ur->create_action(TTR("MultiNode Set") + " " + String(name), UndoRedo::MERGE_ENDS);
+ ur->create_action(vformat(TTR("Set %s on %d nodes"), name, get_node_count()), UndoRedo::MERGE_ENDS);
for (const NodePath &E : nodes) {
- if (!es->has_node(E)) {
- continue;
- }
-
- Node *n = es->get_node(E);
+ Node *n = es->get_node_or_null(E);
if (!n) {
continue;
}
@@ -100,16 +96,12 @@ bool MultiNodeEdit::_get(const StringName &p_name, Variant &r_ret) const {
}
String name = p_name;
- if (name == "scripts") { // script set is intercepted at object level (check Variant Object::get() ) ,so use a different name
+ if (name == "scripts") { // Script set is intercepted at object level (check Variant Object::get()), so use a different name.
name = "script";
}
for (const NodePath &E : nodes) {
- if (!es->has_node(E)) {
- continue;
- }
-
- const Node *n = es->get_node(E);
+ const Node *n = es->get_node_or_null(E);
if (!n) {
continue;
}
@@ -137,11 +129,7 @@ void MultiNodeEdit::_get_property_list(List<PropertyInfo> *p_list) const {
List<PLData *> data_list;
for (const NodePath &E : nodes) {
- if (!es->has_node(E)) {
- continue;
- }
-
- Node *n = es->get_node(E);
+ Node *n = es->get_node_or_null(E);
if (!n) {
continue;
}
@@ -151,7 +139,7 @@ void MultiNodeEdit::_get_property_list(List<PropertyInfo> *p_list) const {
for (const PropertyInfo &F : plist) {
if (F.name == "script") {
- continue; //added later manually, since this is intercepted before being set (check Variant Object::get() )
+ continue; // Added later manually, since this is intercepted before being set (check Variant Object::get()).
}
if (!usage.has(F.name)) {
PLData pld;
@@ -161,7 +149,7 @@ void MultiNodeEdit::_get_property_list(List<PropertyInfo> *p_list) const {
data_list.push_back(usage.getptr(F.name));
}
- // Make sure only properties with the same exact PropertyInfo data will appear
+ // Make sure only properties with the same exact PropertyInfo data will appear.
if (usage[F.name].info == F) {
usage[F.name].uses++;
}
@@ -179,6 +167,66 @@ void MultiNodeEdit::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::OBJECT, "scripts", PROPERTY_HINT_RESOURCE_TYPE, "Script"));
}
+String MultiNodeEdit::_get_editor_name() const {
+ return vformat(TTR("%s (%d Selected)"), get_edited_class_name(), get_node_count());
+}
+
+bool MultiNodeEdit::_property_can_revert(const StringName &p_name) const {
+ Node *es = EditorNode::get_singleton()->get_edited_scene();
+ if (!es) {
+ return false;
+ }
+
+ if (ClassDB::has_property(get_edited_class_name(), p_name)) {
+ StringName class_name;
+ for (const NodePath &E : nodes) {
+ Node *node = es->get_node_or_null(E);
+ if (!node) {
+ continue;
+ }
+
+ class_name = node->get_class_name();
+ }
+
+ Variant default_value = ClassDB::class_get_default_property_value(class_name, p_name);
+ for (const NodePath &E : nodes) {
+ Node *node = es->get_node_or_null(E);
+ if (!node) {
+ continue;
+ }
+
+ if (node->get(p_name) != default_value) {
+ // A node that doesn't have the default value has been found, so show the revert button.
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Don't show the revert button if the edited class doesn't have the property.
+ return false;
+}
+
+bool MultiNodeEdit::_property_get_revert(const StringName &p_name, Variant &r_property) const {
+ Node *es = EditorNode::get_singleton()->get_edited_scene();
+ if (!es) {
+ return false;
+ }
+
+ for (const NodePath &E : nodes) {
+ Node *node = es->get_node_or_null(E);
+ if (!node) {
+ continue;
+ }
+
+ r_property = ClassDB::class_get_default_property_value(node->get_class_name(), p_name);
+ return true;
+ }
+
+ return false;
+}
+
void MultiNodeEdit::add_node(const NodePath &p_node) {
nodes.push_back(p_node);
}
@@ -192,9 +240,69 @@ NodePath MultiNodeEdit::get_node(int p_index) const {
return nodes[p_index];
}
+StringName MultiNodeEdit::get_edited_class_name() const {
+ Node *es = EditorNode::get_singleton()->get_edited_scene();
+ if (!es) {
+ return SNAME("Node");
+ }
+
+ // Get the class name of the first node.
+ StringName class_name;
+ for (const NodePath &E : nodes) {
+ Node *node = es->get_node_or_null(E);
+ if (!node) {
+ continue;
+ }
+
+ class_name = node->get_class_name();
+ break;
+ }
+
+ if (class_name == StringName()) {
+ return SNAME("Node");
+ }
+
+ bool check_again = true;
+ while (check_again) {
+ check_again = false;
+
+ if (class_name == SNAME("Node") || class_name == StringName()) {
+ // All nodes inherit from Node, so no need to continue checking.
+ return SNAME("Node");
+ }
+
+ // Check that all nodes inherit from class_name.
+ for (const NodePath &E : nodes) {
+ Node *node = es->get_node_or_null(E);
+ if (!node) {
+ continue;
+ }
+
+ const StringName node_class_name = node->get_class_name();
+ if (class_name == node_class_name || ClassDB::is_parent_class(node_class_name, class_name)) {
+ // class_name is the same or a parent of the node's class.
+ continue;
+ }
+
+ // class_name is not a parent of the node's class, so check again with the parent class.
+ class_name = ClassDB::get_parent_class(class_name);
+ check_again = true;
+ break;
+ }
+ }
+
+ return class_name;
+}
+
void MultiNodeEdit::set_property_field(const StringName &p_property, const Variant &p_value, const String &p_field) {
_set_impl(p_property, p_value, p_field);
}
+void MultiNodeEdit::_bind_methods() {
+ ClassDB::bind_method("_hide_script_from_inspector", &MultiNodeEdit::_hide_script_from_inspector);
+ ClassDB::bind_method("_hide_metadata_from_inspector", &MultiNodeEdit::_hide_metadata_from_inspector);
+ ClassDB::bind_method("_get_editor_name", &MultiNodeEdit::_get_editor_name);
+}
+
MultiNodeEdit::MultiNodeEdit() {
}
diff --git a/editor/multi_node_edit.h b/editor/multi_node_edit.h
index 31678d7b01..9c0ec85e20 100644
--- a/editor/multi_node_edit.h
+++ b/editor/multi_node_edit.h
@@ -45,15 +45,25 @@ class MultiNodeEdit : public RefCounted {
bool _set_impl(const StringName &p_name, const Variant &p_value, const String &p_field);
protected:
+ static void _bind_methods();
+
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
public:
+ bool _hide_script_from_inspector() { return true; }
+ bool _hide_metadata_from_inspector() { return true; }
+
+ bool _property_can_revert(const StringName &p_name) const;
+ bool _property_get_revert(const StringName &p_name, Variant &r_property) const;
+ String _get_editor_name() const;
+
void add_node(const NodePath &p_node);
int get_node_count() const;
NodePath get_node(int p_index) const;
+ StringName get_edited_class_name() const;
void set_property_field(const StringName &p_property, const Variant &p_value, const String &p_field);
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
index 1a6106b4fb..6baeaf6273 100644
--- a/editor/plugins/animation_state_machine_editor.cpp
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -267,6 +267,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order
if (node_rects[i].node.has_point(mb->get_position())) { //select node since nothing else was selected
connecting = true;
+ connection_follows_cursor = true;
connecting_from = node_rects[i].node_name;
connecting_to = mb->get_position();
connecting_to_node = StringName();
@@ -296,6 +297,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
_open_menu(mb->get_position());
}
connecting_to_node = StringName();
+ connection_follows_cursor = false;
state_machine_draw->queue_redraw();
}
@@ -332,7 +334,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
}
// Move mouse while connecting
- if (mm.is_valid() && connecting && !read_only) {
+ if (mm.is_valid() && connecting && connection_follows_cursor && !read_only) {
connecting_to = mm->get_position();
connecting_to_node = StringName();
state_machine_draw->queue_redraw();
diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h
index 3a59e94a5f..18b5187491 100644
--- a/editor/plugins/animation_state_machine_editor.h
+++ b/editor/plugins/animation_state_machine_editor.h
@@ -117,6 +117,7 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin {
StringName snap_y;
bool connecting = false;
+ bool connection_follows_cursor = false;
StringName connecting_from;
Vector2 connecting_to;
StringName connecting_to_node;
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 41383edafe..6bc443039f 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -591,10 +591,12 @@ void EditorAssetLibrary::_notification(int p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible()) {
+#ifndef ANDROID_ENABLED
// Focus the search box automatically when switching to the Templates tab (in the Project Manager)
// or switching to the AssetLib tab (in the editor).
// The Project Manager's project filter box is automatically focused in the project manager code.
filter->grab_focus();
+#endif
if (initial_loading) {
_repository_changed(0); // Update when shown for the first time.
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index fff956a05e..cc955eae76 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -340,7 +340,9 @@ void ScriptTextEditor::set_edit_state(const Variant &p_state) {
}
if (editor_enabled) {
+#ifndef ANDROID_ENABLED
ensure_focus();
+#endif
}
}
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index ed8c7b14c8..a5f6d3f142 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -1901,11 +1901,13 @@ void ProjectManager::_notification(int p_what) {
filter_option->select(default_sorting);
_project_list->set_order_option(default_sorting);
+#ifndef ANDROID_ENABLED
if (_project_list->get_project_count() >= 1) {
// Focus on the search box immediately to allow the user
// to search without having to reach for their mouse
search_box->grab_focus();
}
+#endif
if (asset_library) {
// Removes extra border margins.
@@ -2443,6 +2445,7 @@ void ProjectManager::_on_order_option_changed(int p_idx) {
}
void ProjectManager::_on_tab_changed(int p_tab) {
+#ifndef ANDROID_ENABLED
if (p_tab == 0) { // Projects
// Automatically grab focus when the user moves from the Templates tab
// back to the Projects tab.
@@ -2451,6 +2454,7 @@ void ProjectManager::_on_tab_changed(int p_tab) {
// The Templates tab's search field is focused on display in the asset
// library editor plugin code.
+#endif
}
void ProjectManager::_on_search_term_changed(const String &p_term) {