diff options
Diffstat (limited to 'editor')
37 files changed, 1036 insertions, 534 deletions
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp index a9c18138d8..b461ac4f35 100644 --- a/editor/debugger/editor_debugger_node.cpp +++ b/editor/debugger/editor_debugger_node.cpp @@ -34,6 +34,7 @@ #include "editor/debugger/script_editor_debugger.h" #include "editor/editor_log.h" #include "editor/editor_node.h" +#include "editor/plugins/editor_debugger_plugin.h" #include "editor/plugins/script_editor_plugin.h" #include "scene/gui/menu_button.h" #include "scene/gui/tab_container.h" @@ -114,6 +115,12 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() { tabs->add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox("DebuggerPanel", "EditorStyles")); } + if (!debugger_plugins.empty()) { + for (Set<Ref<Script>>::Element *i = debugger_plugins.front(); i; i = i->next()) { + node->add_debugger_plugin(i->get()); + } + } + return node; } @@ -618,3 +625,23 @@ void EditorDebuggerNode::live_debug_reparent_node(const NodePath &p_at, const No dbg->live_debug_reparent_node(p_at, p_new_place, p_new_name, p_at_pos); }); } + +void EditorDebuggerNode::add_debugger_plugin(const Ref<Script> &p_script) { + ERR_FAIL_COND_MSG(debugger_plugins.has(p_script), "Debugger plugin already exists."); + ERR_FAIL_COND_MSG(p_script.is_null(), "Debugger plugin script is null"); + ERR_FAIL_COND_MSG(String(p_script->get_instance_base_type()) == "", "Debugger plugin script has error."); + ERR_FAIL_COND_MSG(String(p_script->get_instance_base_type()) != "EditorDebuggerPlugin", "Base type of debugger plugin is not 'EditorDebuggerPlugin'."); + ERR_FAIL_COND_MSG(!p_script->is_tool(), "Debugger plugin script is not in tool mode."); + debugger_plugins.insert(p_script); + for (int i = 0; get_debugger(i); i++) { + get_debugger(i)->add_debugger_plugin(p_script); + } +} + +void EditorDebuggerNode::remove_debugger_plugin(const Ref<Script> &p_script) { + ERR_FAIL_COND_MSG(!debugger_plugins.has(p_script), "Debugger plugin doesn't exists."); + debugger_plugins.erase(p_script); + for (int i = 0; get_debugger(i); i++) { + get_debugger(i)->remove_debugger_plugin(p_script); + } +} diff --git a/editor/debugger/editor_debugger_node.h b/editor/debugger/editor_debugger_node.h index ff9601c026..8d70a7f961 100644 --- a/editor/debugger/editor_debugger_node.h +++ b/editor/debugger/editor_debugger_node.h @@ -103,6 +103,8 @@ private: CameraOverride camera_override = OVERRIDE_NONE; Map<Breakpoint, bool> breakpoints; + Set<Ref<Script>> debugger_plugins; + ScriptEditorDebugger *_add_debugger(); EditorDebuggerRemoteObject *get_inspected_remote_object(); @@ -186,5 +188,8 @@ public: Error start(const String &p_protocol = "tcp://"); void stop(); + + void add_debugger_plugin(const Ref<Script> &p_script); + void remove_debugger_plugin(const Ref<Script> &p_script); }; #endif // EDITOR_DEBUGGER_NODE_H diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 49bf068be7..1fca95b6da 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -44,6 +44,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/plugins/canvas_item_editor_plugin.h" +#include "editor/plugins/editor_debugger_plugin.h" #include "editor/plugins/node_3d_editor_plugin.h" #include "editor/property_editor.h" #include "main/performance.h" @@ -701,7 +702,28 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da performance_profiler->update_monitors(monitors); } else { - WARN_PRINT("unknown message " + p_msg); + int colon_index = p_msg.find_char(':'); + ERR_FAIL_COND_MSG(colon_index < 1, "Invalid message received"); + + bool parsed = false; + const String cap = p_msg.substr(0, colon_index); + Map<StringName, Callable>::Element *element = captures.find(cap); + if (element) { + Callable &c = element->value(); + ERR_FAIL_COND_MSG(c.is_null(), "Invalid callable registered: " + cap); + Variant cmd = p_msg.substr(colon_index + 1), data = p_data; + const Variant *args[2] = { &cmd, &data }; + Variant retval; + Callable::CallError err; + c.call(args, 2, retval, err); + ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'capture' to callable: " + Variant::get_callable_error_text(c, args, 2, err)); + ERR_FAIL_COND_MSG(retval.get_type() != Variant::BOOL, "Error calling 'capture' to callable: " + String(c) + ". Return type is not bool."); + parsed = retval; + } + + if (!parsed) { + WARN_PRINT("unknown message " + p_msg); + } } } @@ -847,6 +869,7 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) { tabs->set_current_tab(0); _set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS); _update_buttons_state(); + emit_signal("started"); } void ScriptEditorDebugger::_update_buttons_state() { @@ -1395,6 +1418,7 @@ void ScriptEditorDebugger::_bind_methods() { ClassDB::bind_method(D_METHOD("request_remote_object", "id"), &ScriptEditorDebugger::request_remote_object); ClassDB::bind_method(D_METHOD("update_remote_object", "id", "property", "value"), &ScriptEditorDebugger::update_remote_object); + ADD_SIGNAL(MethodInfo("started")); ADD_SIGNAL(MethodInfo("stopped")); ADD_SIGNAL(MethodInfo("stop_requested")); ADD_SIGNAL(MethodInfo("stack_frame_selected", PropertyInfo(Variant::INT, "frame"))); @@ -1408,6 +1432,43 @@ void ScriptEditorDebugger::_bind_methods() { ADD_SIGNAL(MethodInfo("remote_tree_updated")); } +void ScriptEditorDebugger::add_debugger_plugin(const Ref<Script> &p_script) { + if (!debugger_plugins.has(p_script)) { + EditorDebuggerPlugin *plugin = memnew(EditorDebuggerPlugin()); + plugin->attach_debugger(this); + plugin->set_script(p_script); + tabs->add_child(plugin); + debugger_plugins.insert(p_script, plugin); + } +} + +void ScriptEditorDebugger::remove_debugger_plugin(const Ref<Script> &p_script) { + if (debugger_plugins.has(p_script)) { + tabs->remove_child(debugger_plugins[p_script]); + debugger_plugins[p_script]->detach_debugger(false); + memdelete(debugger_plugins[p_script]); + debugger_plugins.erase(p_script); + } +} + +void ScriptEditorDebugger::send_message(const String &p_message, const Array &p_args) { + _put_msg(p_message, p_args); +} + +void ScriptEditorDebugger::register_message_capture(const StringName &p_name, const Callable &p_callable) { + ERR_FAIL_COND_MSG(has_capture(p_name), "Capture already registered: " + p_name); + captures.insert(p_name, p_callable); +} + +void ScriptEditorDebugger::unregister_message_capture(const StringName &p_name) { + ERR_FAIL_COND_MSG(!has_capture(p_name), "Capture not registered: " + p_name); + captures.erase(p_name); +} + +bool ScriptEditorDebugger::has_capture(const StringName &p_name) { + return captures.has(p_name); +} + ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { editor = p_editor; diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h index 6e5699e929..56b34e8e8c 100644 --- a/editor/debugger/script_editor_debugger.h +++ b/editor/debugger/script_editor_debugger.h @@ -54,6 +54,7 @@ class EditorVisualProfiler; class EditorNetworkProfiler; class EditorPerformanceProfiler; class SceneDebuggerTree; +class EditorDebuggerPlugin; class ScriptEditorDebugger : public MarginContainer { GDCLASS(ScriptEditorDebugger, MarginContainer); @@ -146,6 +147,10 @@ private: EditorDebuggerNode::CameraOverride camera_override; + Map<Ref<Script>, EditorDebuggerPlugin *> debugger_plugins; + + Map<StringName, Callable> captures; + void _stack_dump_frame_selected(); void _file_selected(const String &p_file); @@ -253,6 +258,16 @@ public: bool is_skip_breakpoints(); virtual Size2 get_minimum_size() const override; + + void add_debugger_plugin(const Ref<Script> &p_script); + void remove_debugger_plugin(const Ref<Script> &p_script); + + void send_message(const String &p_message, const Array &p_args); + + void register_message_capture(const StringName &p_name, const Callable &p_callable); + void unregister_message_capture(const StringName &p_name); + bool has_capture(const StringName &p_name); + ScriptEditorDebugger(EditorNode *p_editor = nullptr); ~ScriptEditorDebugger(); }; diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index 130c330f5a..5118ccacad 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -262,7 +262,9 @@ EditorHistory::EditorHistory() { } EditorPlugin *EditorData::get_editor(Object *p_object) { - for (int i = 0; i < editor_plugins.size(); i++) { + // We need to iterate backwards so that we can check user-created plugins first. + // Otherwise, it would not be possible for plugins to handle CanvasItem and Spatial nodes. + for (int i = editor_plugins.size() - 1; i > -1; i--) { if (editor_plugins[i]->has_main_screen() && editor_plugins[i]->handles(p_object)) { return editor_plugins[i]; } @@ -272,7 +274,7 @@ EditorPlugin *EditorData::get_editor(Object *p_object) { } EditorPlugin *EditorData::get_subeditor(Object *p_object) { - for (int i = 0; i < editor_plugins.size(); i++) { + for (int i = editor_plugins.size() - 1; i > -1; i--) { if (!editor_plugins[i]->has_main_screen() && editor_plugins[i]->handles(p_object)) { return editor_plugins[i]; } @@ -283,7 +285,7 @@ EditorPlugin *EditorData::get_subeditor(Object *p_object) { Vector<EditorPlugin *> EditorData::get_subeditors(Object *p_object) { Vector<EditorPlugin *> sub_plugins; - for (int i = 0; i < editor_plugins.size(); i++) { + for (int i = editor_plugins.size() - 1; i > -1; i--) { if (!editor_plugins[i]->has_main_screen() && editor_plugins[i]->handles(p_object)) { sub_plugins.push_back(editor_plugins[i]); } @@ -292,7 +294,7 @@ Vector<EditorPlugin *> EditorData::get_subeditors(Object *p_object) { } EditorPlugin *EditorData::get_editor(String p_name) { - for (int i = 0; i < editor_plugins.size(); i++) { + for (int i = editor_plugins.size() - 1; i > -1; i--) { if (editor_plugins[i]->get_name() == p_name) { return editor_plugins[i]; } diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index 951bec2c83..16e69734d3 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -512,10 +512,18 @@ void EditorExportPlugin::add_ios_framework(const String &p_path) { ios_frameworks.push_back(p_path); } +void EditorExportPlugin::add_ios_embedded_framework(const String &p_path) { + ios_embedded_frameworks.push_back(p_path); +} + Vector<String> EditorExportPlugin::get_ios_frameworks() const { return ios_frameworks; } +Vector<String> EditorExportPlugin::get_ios_embedded_frameworks() const { + return ios_embedded_frameworks; +} + void EditorExportPlugin::add_ios_plist_content(const String &p_plist_content) { ios_plist_content += p_plist_content + "\n"; } @@ -592,6 +600,7 @@ void EditorExportPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("add_ios_project_static_lib", "path"), &EditorExportPlugin::add_ios_project_static_lib); ClassDB::bind_method(D_METHOD("add_file", "path", "file", "remap"), &EditorExportPlugin::add_file); ClassDB::bind_method(D_METHOD("add_ios_framework", "path"), &EditorExportPlugin::add_ios_framework); + ClassDB::bind_method(D_METHOD("add_ios_embedded_framework", "path"), &EditorExportPlugin::add_ios_embedded_framework); ClassDB::bind_method(D_METHOD("add_ios_plist_content", "plist_content"), &EditorExportPlugin::add_ios_plist_content); ClassDB::bind_method(D_METHOD("add_ios_linker_flags", "flags"), &EditorExportPlugin::add_ios_linker_flags); ClassDB::bind_method(D_METHOD("add_ios_bundle_file", "path"), &EditorExportPlugin::add_ios_bundle_file); diff --git a/editor/editor_export.h b/editor/editor_export.h index e31b53ad67..bb701b94ec 100644 --- a/editor/editor_export.h +++ b/editor/editor_export.h @@ -290,6 +290,7 @@ class EditorExportPlugin : public Reference { bool skipped; Vector<String> ios_frameworks; + Vector<String> ios_embedded_frameworks; Vector<String> ios_project_static_libs; String ios_plist_content; String ios_linker_flags; @@ -304,6 +305,7 @@ class EditorExportPlugin : public Reference { _FORCE_INLINE_ void _export_end() { ios_frameworks.clear(); + ios_embedded_frameworks.clear(); ios_bundle_files.clear(); ios_plist_content = ""; ios_linker_flags = ""; @@ -322,6 +324,7 @@ protected: void add_shared_object(const String &p_path, const Vector<String> &tags); void add_ios_framework(const String &p_path); + void add_ios_embedded_framework(const String &p_path); void add_ios_project_static_lib(const String &p_path); void add_ios_plist_content(const String &p_plist_content); void add_ios_linker_flags(const String &p_flags); @@ -337,6 +340,7 @@ protected: public: Vector<String> get_ios_frameworks() const; + Vector<String> get_ios_embedded_frameworks() const; Vector<String> get_ios_project_static_libs() const; String get_ios_plist_content() const; String get_ios_linker_flags() const; diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp index f68cc3b323..418370a7c3 100644 --- a/editor/editor_feature_profile.cpp +++ b/editor/editor_feature_profile.cpp @@ -41,9 +41,9 @@ const char *EditorFeatureProfile::feature_names[FEATURE_MAX] = { TTRC("Script Editor"), TTRC("Asset Library"), TTRC("Scene Tree Editing"), - TTRC("Import Dock"), TTRC("Node Dock"), - TTRC("FileSystem and Import Docks") + TTRC("FileSystem Dock"), + TTRC("Import Dock"), }; const char *EditorFeatureProfile::feature_identifiers[FEATURE_MAX] = { @@ -51,9 +51,9 @@ const char *EditorFeatureProfile::feature_identifiers[FEATURE_MAX] = { "script", "asset_lib", "scene_tree", - "import_dock", "node_dock", - "filesystem_dock" + "filesystem_dock", + "import_dock", }; void EditorFeatureProfile::set_disable_class(const StringName &p_class, bool p_disabled) { @@ -678,9 +678,16 @@ void EditorFeatureProfileManager::_update_selected_profile() { TreeItem *root = class_list->create_item(); TreeItem *features = class_list->create_item(root); + TreeItem *last_feature; features->set_text(0, TTR("Enabled Features:")); for (int i = 0; i < EditorFeatureProfile::FEATURE_MAX; i++) { - TreeItem *feature = class_list->create_item(features); + TreeItem *feature; + if (i == EditorFeatureProfile::FEATURE_IMPORT_DOCK) { + feature = class_list->create_item(last_feature); + } else { + feature = class_list->create_item(features); + last_feature = feature; + } feature->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); feature->set_text(0, TTRGET(EditorFeatureProfile::get_feature_name(EditorFeatureProfile::Feature(i)))); feature->set_selectable(0, true); diff --git a/editor/editor_feature_profile.h b/editor/editor_feature_profile.h index 38413e35a2..d0d08c61f4 100644 --- a/editor/editor_feature_profile.h +++ b/editor/editor_feature_profile.h @@ -49,9 +49,9 @@ public: FEATURE_SCRIPT, FEATURE_ASSET_LIB, FEATURE_SCENE_TREE, - FEATURE_IMPORT_DOCK, FEATURE_NODE_DOCK, FEATURE_FILESYSTEM_DOCK, + FEATURE_IMPORT_DOCK, FEATURE_MAX }; diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index cf32ffb4e0..336e34298f 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1174,6 +1174,47 @@ void EditorInspectorSection::_notification(int p_what) { if (arrow.is_valid()) { draw_texture(arrow, Point2(Math::round(arrow_margin * EDSCALE), (h - arrow->get_height()) / 2).floor()); } + + if (dropping && !vbox->is_visible_in_tree()) { + Color accent_color = get_theme_color("accent_color", "Editor"); + draw_rect(Rect2(Point2(), get_size()), accent_color, false); + } + } + + if (p_what == NOTIFICATION_DRAG_BEGIN) { + Dictionary dd = get_viewport()->gui_get_drag_data(); + + // Only allow dropping if the section contains properties which can take the dragged data. + bool children_can_drop = false; + for (int child_idx = 0; child_idx < vbox->get_child_count(); child_idx++) { + Control *editor_property = Object::cast_to<Control>(vbox->get_child(child_idx)); + + // Test can_drop_data and can_drop_data_fw, since can_drop_data only works if set up with forwarding or if script attached. + if (editor_property && (editor_property->can_drop_data(Point2(), dd) || editor_property->call("can_drop_data_fw", Point2(), dd, this))) { + children_can_drop = true; + break; + } + } + + dropping = children_can_drop; + update(); + } + + if (p_what == NOTIFICATION_DRAG_END) { + dropping = false; + update(); + } + + if (p_what == NOTIFICATION_MOUSE_ENTER) { + if (dropping) { + dropping_unfold_timer->start(); + } + } + + if (p_what == NOTIFICATION_MOUSE_EXIT) { + if (dropping) { + dropping_unfold_timer->stop(); + } } } @@ -1236,14 +1277,11 @@ void EditorInspectorSection::_gui_input(const Ref<InputEvent> &p_event) { return; } - _test_unfold(); - - bool unfold = !object->editor_is_section_unfolded(section); - object->editor_set_section_unfold(section, unfold); - if (unfold) { - vbox->show(); + bool should_unfold = !object->editor_is_section_unfolded(section); + if (should_unfold) { + unfold(); } else { - vbox->hide(); + fold(); } } } @@ -1291,6 +1329,13 @@ EditorInspectorSection::EditorInspectorSection() { foldable = false; vbox = memnew(VBoxContainer); vbox_added = false; + + dropping = false; + dropping_unfold_timer = memnew(Timer); + dropping_unfold_timer->set_wait_time(0.6); + dropping_unfold_timer->set_one_shot(true); + add_child(dropping_unfold_timer); + dropping_unfold_timer->connect("timeout", callable_mp(this, &EditorInspectorSection::unfold)); } EditorInspectorSection::~EditorInspectorSection() { @@ -1924,7 +1969,7 @@ void EditorInspector::refresh() { if (refresh_countdown > 0 || changing) { return; } - refresh_countdown = EditorSettings::get_singleton()->get("docks/property_editor/auto_refresh_interval"); + refresh_countdown = refresh_interval_cache; } Object *EditorInspector::get_edited_object() { @@ -2287,6 +2332,8 @@ void EditorInspector::_node_removed(Node *p_node) { void EditorInspector::_notification(int p_what) { if (p_what == NOTIFICATION_READY) { EditorFeatureProfileManager::get_singleton()->connect("current_feature_profile_changed", callable_mp(this, &EditorInspector::_feature_profile_changed)); + refresh_interval_cache = EDITOR_GET("docks/property_editor/auto_refresh_interval"); + refresh_countdown = refresh_interval_cache; } if (p_what == NOTIFICATION_ENTER_TREE) { @@ -2322,6 +2369,9 @@ void EditorInspector::_notification(int p_what) { } } } + } else { + // Restart countdown if <= 0 + refresh_countdown = refresh_interval_cache; } changing++; @@ -2354,6 +2404,9 @@ void EditorInspector::_notification(int p_what) { add_theme_style_override("bg", get_theme_stylebox("bg", "Tree")); } + refresh_interval_cache = EDITOR_GET("docks/property_editor/auto_refresh_interval"); + refresh_countdown = refresh_interval_cache; + update_tree(); } } @@ -2517,6 +2570,7 @@ EditorInspector::EditorInspector() { update_all_pending = false; update_tree_pending = false; refresh_countdown = 0; + refresh_interval_cache = 0; read_only = false; search_box = nullptr; keying = false; diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 95072fd703..d1046315f4 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -231,6 +231,9 @@ class EditorInspectorSection : public Container { Color bg_color; bool foldable; + Timer *dropping_unfold_timer; + bool dropping; + void _test_unfold(); protected: @@ -291,6 +294,7 @@ class EditorInspector : public ScrollContainer { bool deletable_properties; float refresh_countdown; + float refresh_interval_cache; bool update_tree_pending; StringName _prop_edited; StringName property_selected; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index ed187aa6df..e90f30496c 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -123,6 +123,7 @@ #include "editor/plugins/cpu_particles_3d_editor_plugin.h" #include "editor/plugins/curve_editor_plugin.h" #include "editor/plugins/debugger_editor_plugin.h" +#include "editor/plugins/editor_debugger_plugin.h" #include "editor/plugins/editor_preview_plugins.h" #include "editor/plugins/gi_probe_editor_plugin.h" #include "editor/plugins/gpu_particles_2d_editor_plugin.h" @@ -456,8 +457,6 @@ void EditorNode::_notification(int p_what) { editor_selection->update(); - //scene_root->set_size_override(true, Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"))); - { //TODO should only happen on settings changed int current_filter = GLOBAL_GET("rendering/canvas_textures/default_texture_filter"); if (current_filter != scene_root->get_default_canvas_item_texture_filter()) { @@ -479,6 +478,8 @@ void EditorNode::_notification(int p_what) { RS::get_singleton()->screen_space_roughness_limiter_set_active(GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_enabled"), GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_amount"), GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_limit")); bool glow_bicubic = int(GLOBAL_GET("rendering/quality/glow/upscale_mode")) > 0; RS::get_singleton()->environment_glow_set_use_bicubic_upscale(glow_bicubic); + bool glow_high_quality = GLOBAL_GET("rendering/quality/glow/use_high_quality"); + RS::get_singleton()->environment_glow_set_use_high_quality(glow_high_quality); RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::EnvironmentSSRRoughnessQuality(int(GLOBAL_GET("rendering/quality/screen_space_reflection/roughness_quality"))); RS::get_singleton()->environment_set_ssr_roughness_quality(ssr_roughness_quality); RS::SubSurfaceScatteringQuality sss_quality = RS::SubSurfaceScatteringQuality(int(GLOBAL_GET("rendering/quality/subsurface_scattering/subsurface_scattering_quality"))); @@ -3623,6 +3624,7 @@ void EditorNode::register_editor_types() { // FIXME: Is this stuff obsolete, or should it be ported to new APIs? ClassDB::register_class<EditorScenePostImport>(); //ClassDB::register_type<EditorImportExport>(); + ClassDB::register_class<EditorDebuggerPlugin>(); } void EditorNode::unregister_editor_types() { @@ -5345,9 +5347,11 @@ void EditorNode::_feature_profile_changed() { TabContainer *node_tabs = cast_to<TabContainer>(node_dock->get_parent()); TabContainer *fs_tabs = cast_to<TabContainer>(filesystem_dock->get_parent()); if (profile.is_valid()) { - import_tabs->set_tab_hidden(import_dock->get_index(), profile->is_feature_disabled(EditorFeatureProfile::FEATURE_IMPORT_DOCK)); node_tabs->set_tab_hidden(node_dock->get_index(), profile->is_feature_disabled(EditorFeatureProfile::FEATURE_NODE_DOCK)); - fs_tabs->set_tab_hidden(filesystem_dock->get_index(), profile->is_feature_disabled(EditorFeatureProfile::FEATURE_FILESYSTEM_DOCK)); + // The Import dock is useless without the FileSystem dock. Ensure the configuration is valid. + bool fs_dock_disabled = profile->is_feature_disabled(EditorFeatureProfile::FEATURE_FILESYSTEM_DOCK); + fs_tabs->set_tab_hidden(filesystem_dock->get_index(), fs_dock_disabled); + import_tabs->set_tab_hidden(import_dock->get_index(), fs_dock_disabled || profile->is_feature_disabled(EditorFeatureProfile::FEATURE_IMPORT_DOCK)); main_editor_buttons[EDITOR_3D]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)); main_editor_buttons[EDITOR_SCRIPT]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT)); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index da0a0827d2..bce46b719a 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -811,6 +811,14 @@ ScriptCreateDialog *EditorPlugin::get_script_create_dialog() { return EditorNode::get_singleton()->get_script_create_dialog(); } +void EditorPlugin::add_debugger_plugin(const Ref<Script> &p_script) { + EditorDebuggerNode::get_singleton()->add_debugger_plugin(p_script); +} + +void EditorPlugin::remove_debugger_plugin(const Ref<Script> &p_script) { + EditorDebuggerNode::get_singleton()->remove_debugger_plugin(p_script); +} + void EditorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("add_control_to_container", "container", "control"), &EditorPlugin::add_control_to_container); ClassDB::bind_method(D_METHOD("add_control_to_bottom_panel", "control", "title"), &EditorPlugin::add_control_to_bottom_panel); @@ -851,6 +859,8 @@ void EditorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("get_editor_interface"), &EditorPlugin::get_editor_interface); ClassDB::bind_method(D_METHOD("get_script_create_dialog"), &EditorPlugin::get_script_create_dialog); + ClassDB::bind_method(D_METHOD("add_debugger_plugin", "script"), &EditorPlugin::add_debugger_plugin); + ClassDB::bind_method(D_METHOD("remove_debugger_plugin", "script"), &EditorPlugin::remove_debugger_plugin); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_canvas_gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_canvas_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 685f69bf3f..c7803f73c9 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -33,6 +33,7 @@ #include "core/io/config_file.h" #include "core/undo_redo.h" +#include "editor/debugger/editor_debugger_node.h" #include "editor/editor_inspector.h" #include "editor/editor_translation_parser.h" #include "editor/import/editor_import_plugin.h" @@ -249,6 +250,9 @@ public: void add_autoload_singleton(const String &p_name, const String &p_path); void remove_autoload_singleton(const String &p_name); + void add_debugger_plugin(const Ref<Script> &p_script); + void remove_debugger_plugin(const Ref<Script> &p_script); + void enable_plugin(); void disable_plugin(); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 34d553b5f9..4c8af615b4 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -36,6 +36,7 @@ #include "editor_properties_array_dict.h" #include "editor_scale.h" #include "scene/main/window.h" +#include "scene/resources/dynamic_font.h" ///////////////////// NULL ///////////////////////// @@ -2947,11 +2948,9 @@ void EditorPropertyResource::_notification(int p_what) { } if (p_what == NOTIFICATION_DRAG_BEGIN) { - if (is_visible_in_tree()) { - if (_is_drop_valid(get_viewport()->gui_get_drag_data())) { - dropping = true; - assign->update(); - } + if (_is_drop_valid(get_viewport()->gui_get_drag_data())) { + dropping = true; + assign->update(); } } @@ -3016,6 +3015,8 @@ bool EditorPropertyResource::_is_drop_valid(const Dictionary &p_drag_data) const allowed_types.append("Texture2D"); } else if (at == "ShaderMaterial") { allowed_types.append("Shader"); + } else if (at == "Font") { + allowed_types.append("DynamicFontData"); } } @@ -3113,6 +3114,13 @@ void EditorPropertyResource::drop_data_fw(const Point2 &p_point, const Variant & res = mat; break; } + + if (at == "Font" && ClassDB::is_parent_class(res->get_class(), "DynamicFontData")) { + Ref<DynamicFont> font = memnew(DynamicFont); + font->set_font_data(res); + res = font; + break; + } } } diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp index eabbf6b0d8..cf19b54cff 100644 --- a/editor/editor_sectioned_inspector.cpp +++ b/editor/editor_sectioned_inspector.cpp @@ -238,7 +238,7 @@ void SectionedInspector::update_category_list() { continue; } - if (!filter.empty() && !filter.is_subsequence_ofi(pi.name) && !filter.is_subsequence_ofi(pi.name.replace("/", " ").capitalize())) { + if (!filter.empty() && pi.name.findn(filter) == -1 && pi.name.replace("/", " ").capitalize().findn(filter) == -1) { continue; } diff --git a/editor/editor_translation_parser.cpp b/editor/editor_translation_parser.cpp index da191fbc92..7a90d20000 100644 --- a/editor/editor_translation_parser.cpp +++ b/editor/editor_translation_parser.cpp @@ -37,15 +37,30 @@ EditorTranslationParser *EditorTranslationParser::singleton = nullptr; -Error EditorTranslationParserPlugin::parse_file(const String &p_path, Vector<String> *r_extracted_strings) { +Error EditorTranslationParserPlugin::parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) { if (!get_script_instance()) return ERR_UNAVAILABLE; if (get_script_instance()->has_method("parse_file")) { - Array extracted_strings; - get_script_instance()->call("parse_file", p_path, extracted_strings); - for (int i = 0; i < extracted_strings.size(); i++) { - r_extracted_strings->append(extracted_strings[i]); + Array ids; + Array ids_ctx_plural; + get_script_instance()->call("parse_file", p_path, ids, ids_ctx_plural); + + // Add user's extracted translatable messages. + for (int i = 0; i < ids.size(); i++) { + r_ids->append(ids[i]); + } + + // Add user's collected translatable messages with context or plurals. + for (int i = 0; i < ids_ctx_plural.size(); i++) { + Array arr = ids_ctx_plural[i]; + ERR_FAIL_COND_V_MSG(arr.size() != 3, ERR_INVALID_DATA, "Array entries written into `msgids_context_plural` in `parse_file()` method should have the form [\"message\", \"context\", \"plural message\"]"); + + Vector<String> id_ctx_plural; + id_ctx_plural.push_back(arr[0]); + id_ctx_plural.push_back(arr[1]); + id_ctx_plural.push_back(arr[2]); + r_ids_ctx_plural->append(id_ctx_plural); } return OK; } else { @@ -69,7 +84,7 @@ void EditorTranslationParserPlugin::get_recognized_extensions(List<String> *r_ex } void EditorTranslationParserPlugin::_bind_methods() { - ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::NIL, "parse_file", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::ARRAY, "extracted_strings"))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::NIL, "parse_file", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::ARRAY, "msgids"), PropertyInfo(Variant::ARRAY, "msgids_context_plural"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::ARRAY, "get_recognized_extensions")); } diff --git a/editor/editor_translation_parser.h b/editor/editor_translation_parser.h index fb8aa6ec9b..18f49b3803 100644 --- a/editor/editor_translation_parser.h +++ b/editor/editor_translation_parser.h @@ -41,7 +41,7 @@ protected: static void _bind_methods(); public: - virtual Error parse_file(const String &p_path, Vector<String> *r_extracted_strings); + virtual Error parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural); virtual void get_recognized_extensions(List<String> *r_extensions) const; }; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index f3508cedbd..3f9f159d7f 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -2599,6 +2599,11 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { void CanvasItemEditor::_update_cursor() { CursorShape c = CURSOR_ARROW; + bool should_switch = false; + if (drag_selection.size() != 0) { + float angle = drag_selection[0]->_edit_get_rotation(); + should_switch = abs(Math::cos(angle)) < Math_SQRT12; + } switch (drag_type) { case DRAG_NONE: switch (tool) { @@ -2621,21 +2626,37 @@ void CanvasItemEditor::_update_cursor() { case DRAG_LEFT: case DRAG_RIGHT: case DRAG_V_GUIDE: - c = CURSOR_HSIZE; + if (should_switch) { + c = CURSOR_VSIZE; + } else { + c = CURSOR_HSIZE; + } break; case DRAG_TOP: case DRAG_BOTTOM: case DRAG_H_GUIDE: - c = CURSOR_VSIZE; + if (should_switch) { + c = CURSOR_HSIZE; + } else { + c = CURSOR_VSIZE; + } break; case DRAG_TOP_LEFT: case DRAG_BOTTOM_RIGHT: case DRAG_DOUBLE_GUIDE: - c = CURSOR_FDIAGSIZE; + if (should_switch) { + c = CURSOR_BDIAGSIZE; + } else { + c = CURSOR_FDIAGSIZE; + } break; case DRAG_TOP_RIGHT: case DRAG_BOTTOM_LEFT: - c = CURSOR_BDIAGSIZE; + if (should_switch) { + c = CURSOR_FDIAGSIZE; + } else { + c = CURSOR_BDIAGSIZE; + } break; case DRAG_MOVE: c = CURSOR_MOVE; diff --git a/editor/plugins/editor_debugger_plugin.cpp b/editor/plugins/editor_debugger_plugin.cpp new file mode 100644 index 0000000000..b775e871e2 --- /dev/null +++ b/editor/plugins/editor_debugger_plugin.cpp @@ -0,0 +1,124 @@ +/*************************************************************************/ +/* editor_debugger_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "editor_debugger_plugin.h" + +#include "editor/debugger/script_editor_debugger.h" + +void EditorDebuggerPlugin::_breaked(bool p_really_did, bool p_can_debug) { + if (p_really_did) { + emit_signal("breaked", p_can_debug); + } else { + emit_signal("continued"); + } +} + +void EditorDebuggerPlugin::_started() { + emit_signal("started"); +} + +void EditorDebuggerPlugin::_stopped() { + emit_signal("stopped"); +} + +void EditorDebuggerPlugin::_bind_methods() { + ClassDB::bind_method(D_METHOD("send_message", "message", "data"), &EditorDebuggerPlugin::send_message); + ClassDB::bind_method(D_METHOD("register_message_capture", "name", "callable"), &EditorDebuggerPlugin::register_message_capture); + ClassDB::bind_method(D_METHOD("unregister_message_capture", "name"), &EditorDebuggerPlugin::unregister_message_capture); + ClassDB::bind_method(D_METHOD("has_capture", "name"), &EditorDebuggerPlugin::has_capture); + ClassDB::bind_method(D_METHOD("is_breaked"), &EditorDebuggerPlugin::is_breaked); + ClassDB::bind_method(D_METHOD("is_debuggable"), &EditorDebuggerPlugin::is_debuggable); + ClassDB::bind_method(D_METHOD("is_session_active"), &EditorDebuggerPlugin::is_session_active); + + ADD_SIGNAL(MethodInfo("started")); + ADD_SIGNAL(MethodInfo("stopped")); + ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "can_debug"))); + ADD_SIGNAL(MethodInfo("continued")); +} + +void EditorDebuggerPlugin::attach_debugger(ScriptEditorDebugger *p_debugger) { + debugger = p_debugger; + if (debugger) { + debugger->connect("started", callable_mp(this, &EditorDebuggerPlugin::_started)); + debugger->connect("stopped", callable_mp(this, &EditorDebuggerPlugin::_stopped)); + debugger->connect("breaked", callable_mp(this, &EditorDebuggerPlugin::_breaked)); + } +} + +void EditorDebuggerPlugin::detach_debugger(bool p_call_debugger) { + if (debugger) { + debugger->disconnect("started", callable_mp(this, &EditorDebuggerPlugin::_started)); + debugger->disconnect("stopped", callable_mp(this, &EditorDebuggerPlugin::_stopped)); + debugger->disconnect("breaked", callable_mp(this, &EditorDebuggerPlugin::_breaked)); + if (p_call_debugger && get_script_instance()) { + debugger->remove_debugger_plugin(get_script_instance()->get_script()); + } + debugger = nullptr; + } +} + +void EditorDebuggerPlugin::send_message(const String &p_message, const Array &p_args) { + ERR_FAIL_COND_MSG(!debugger, "Plugin is not attached to debugger"); + debugger->send_message(p_message, p_args); +} + +void EditorDebuggerPlugin::register_message_capture(const StringName &p_name, const Callable &p_callable) { + ERR_FAIL_COND_MSG(!debugger, "Plugin is not attached to debugger"); + debugger->register_message_capture(p_name, p_callable); +} + +void EditorDebuggerPlugin::unregister_message_capture(const StringName &p_name) { + ERR_FAIL_COND_MSG(!debugger, "Plugin is not attached to debugger"); + debugger->unregister_message_capture(p_name); +} + +bool EditorDebuggerPlugin::has_capture(const StringName &p_name) { + ERR_FAIL_COND_V_MSG(!debugger, false, "Plugin is not attached to debugger"); + return debugger->has_capture(p_name); +} + +bool EditorDebuggerPlugin::is_breaked() { + ERR_FAIL_COND_V_MSG(!debugger, false, "Plugin is not attached to debugger"); + return debugger->is_breaked(); +} + +bool EditorDebuggerPlugin::is_debuggable() { + ERR_FAIL_COND_V_MSG(!debugger, false, "Plugin is not attached to debugger"); + return debugger->is_debuggable(); +} + +bool EditorDebuggerPlugin::is_session_active() { + ERR_FAIL_COND_V_MSG(!debugger, false, "Plugin is not attached to debugger"); + return debugger->is_session_active(); +} + +EditorDebuggerPlugin::~EditorDebuggerPlugin() { + detach_debugger(true); +} diff --git a/editor/plugins/editor_debugger_plugin.h b/editor/plugins/editor_debugger_plugin.h new file mode 100644 index 0000000000..10fd1151de --- /dev/null +++ b/editor/plugins/editor_debugger_plugin.h @@ -0,0 +1,64 @@ +/*************************************************************************/ +/* editor_debugger_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef EDITOR_DEBUGGER_PLUGIN_H +#define EDITOR_DEBUGGER_PLUGIN_H + +#include "scene/gui/control.h" + +class ScriptEditorDebugger; + +class EditorDebuggerPlugin : public Control { + GDCLASS(EditorDebuggerPlugin, Control); + +private: + ScriptEditorDebugger *debugger = nullptr; + + void _breaked(bool p_really_did, bool p_can_debug); + void _started(); + void _stopped(); + +protected: + static void _bind_methods(); + +public: + void attach_debugger(ScriptEditorDebugger *p_debugger); + void detach_debugger(bool p_call_debugger); + void send_message(const String &p_message, const Array &p_args); + void register_message_capture(const StringName &p_name, const Callable &p_callable); + void unregister_message_capture(const StringName &p_name); + bool has_capture(const StringName &p_name); + bool is_breaked(); + bool is_debuggable(); + bool is_session_active(); + ~EditorDebuggerPlugin(); +}; + +#endif // EDITOR_DEBUGGER_PLUGIN_H diff --git a/editor/plugins/packed_scene_translation_parser_plugin.cpp b/editor/plugins/packed_scene_translation_parser_plugin.cpp index 52af0008b7..608b5c3104 100644 --- a/editor/plugins/packed_scene_translation_parser_plugin.cpp +++ b/editor/plugins/packed_scene_translation_parser_plugin.cpp @@ -37,7 +37,7 @@ void PackedSceneEditorTranslationParserPlugin::get_recognized_extensions(List<St ResourceLoader::get_recognized_extensions_for_type("PackedScene", r_extensions); } -Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path, Vector<String> *r_extracted_strings) { +Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) { // Parse specific scene Node's properties (see in constructor) that are auto-translated by the engine when set. E.g Label's text property. // These properties are translated with the tr() function in the C++ code when being set or updated. @@ -71,8 +71,10 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path, String extension = s->get_language()->get_extension(); if (EditorTranslationParser::get_singleton()->can_parse(extension)) { Vector<String> temp; - EditorTranslationParser::get_singleton()->get_parser(extension)->parse_file(s->get_path(), &temp); + Vector<Vector<String>> ids_context_plural; + EditorTranslationParser::get_singleton()->get_parser(extension)->parse_file(s->get_path(), &temp, &ids_context_plural); parsed_strings.append_array(temp); + r_ids_ctx_plural->append_array(ids_context_plural); } } else if (property_name == "filters") { // Extract FileDialog's filters property with values in format "*.png ; PNG Images","*.gd ; GDScript Files". @@ -93,7 +95,7 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path, } } - r_extracted_strings->append_array(parsed_strings); + r_ids->append_array(parsed_strings); return OK; } diff --git a/editor/plugins/packed_scene_translation_parser_plugin.h b/editor/plugins/packed_scene_translation_parser_plugin.h index 2bd4dae995..a0ffdf692c 100644 --- a/editor/plugins/packed_scene_translation_parser_plugin.h +++ b/editor/plugins/packed_scene_translation_parser_plugin.h @@ -40,7 +40,7 @@ class PackedSceneEditorTranslationParserPlugin : public EditorTranslationParserP Set<String> lookup_properties; public: - virtual Error parse_file(const String &p_path, Vector<String> *r_extracted_strings) override; + virtual Error parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) override; virtual void get_recognized_extensions(List<String> *r_extensions) const override; PackedSceneEditorTranslationParserPlugin(); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 762f42abeb..6e722607f7 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -875,7 +875,7 @@ void TextureRegionEditor::_changed_callback(Object *p_changed, const char *p_pro if (!is_visible()) { return; } - if (p_prop == StringName("atlas") || p_prop == StringName("texture")) { + if (p_prop == StringName("atlas") || p_prop == StringName("texture") || p_prop == StringName("region")) { _edit_region(); } } diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 18a107ff75..932ded6938 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -206,8 +206,8 @@ void ThemeEditor::_save_template_cbk(String fname) { file->store_line("; [value] examples:"); file->store_line("; "); file->store_line("; Type.item = 6 ; numeric constant. "); - file->store_line("; Type.item = #FF00FF ; HTML color "); - file->store_line("; Type.item = #55FF00FF ; HTML color with alpha 55."); + file->store_line("; Type.item = #FF00FF ; HTML color (magenta)."); + file->store_line("; Type.item = #FF00FF55 ; HTML color (magenta with alpha 0x55)."); file->store_line("; Type.item = icon(image.png) ; icon in a png file (relative to theme file)."); file->store_line("; Type.item = font(font.xres) ; font in a resource (relative to theme file)."); file->store_line("; Type.item = sbox(stylebox.xres) ; stylebox in a resource (relative to theme file)."); @@ -629,7 +629,7 @@ ThemeEditor::ThemeEditor() { ScrollContainer *scroll = memnew(ScrollContainer); add_child(scroll); scroll->set_enable_v_scroll(true); - scroll->set_enable_h_scroll(false); + scroll->set_enable_h_scroll(true); scroll->set_v_size_flags(SIZE_EXPAND_FILL); MarginContainer *root_container = memnew(MarginContainer); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 53bd1150ec..217294464c 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -177,6 +177,9 @@ bool VisualShaderEditor::_is_available(int p_mode) { case VisualShader::TYPE_LIGHT: current_mode = 4; break; + case VisualShader::TYPE_COMPUTE: + current_mode = 8; + break; default: break; } @@ -191,6 +194,10 @@ bool VisualShaderEditor::_is_available(int p_mode) { temp_mode |= 4; } + if (p_mode == VisualShader::TYPE_COMPUTE) { + temp_mode = 8; + } + if (temp_mode == 0) { temp_mode |= 1; } @@ -2432,6 +2439,7 @@ VisualShaderEditor::VisualShaderEditor() { edit_type->add_item(TTR("Vertex")); edit_type->add_item(TTR("Fragment")); edit_type->add_item(TTR("Light")); + edit_type->add_item(TTR("Compute")); edit_type->select(1); edit_type->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected)); graph->get_zoom_hbox()->add_child(edit_type); @@ -2644,6 +2652,7 @@ VisualShaderEditor::VisualShaderEditor() { const String input_param_for_fragment_shader_mode = TTR("'%s' input parameter for fragment shader mode."); const String input_param_for_light_shader_mode = TTR("'%s' input parameter for light shader mode."); const String input_param_for_vertex_shader_mode = TTR("'%s' input parameter for vertex shader mode."); + const String input_param_for_compute_shader_mode = TTR("'%s' input parameter for compute shader mode."); const String input_param_for_vertex_and_fragment_shader_mode = TTR("'%s' input parameter for vertex and fragment shader mode."); add_options.push_back(AddOption("Alpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT, Shader::MODE_SPATIAL)); @@ -2717,19 +2726,19 @@ VisualShaderEditor::VisualShaderEditor() { // PARTICLES INPUTS - add_options.push_back(AddOption("Active", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "active"), "active", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Alpha", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Color", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "color"), "color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Custom", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom"), "custom", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("CustomAlpha", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "custom_alpha"), "custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Delta", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "delta"), "delta", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("EmissionTransform", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "emission_transform"), "emission_transform", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Index", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "index"), "index", VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("LifeTime", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "lifetime"), "lifetime", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Restart", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "restart"), "restart", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Time", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Transform", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "transform"), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("Velocity", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "velocity"), "velocity", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Active", "Input", "Compute", "VisualShaderNodeInput", vformat(input_param_for_compute_shader_mode, "active"), "active", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_COMPUTE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Alpha", "Input", "Compute", "VisualShaderNodeInput", vformat(input_param_for_compute_shader_mode, "alpha"), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_COMPUTE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Color", "Input", "Compute", "VisualShaderNodeInput", vformat(input_param_for_compute_shader_mode, "color"), "color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_COMPUTE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Custom", "Input", "Compute", "VisualShaderNodeInput", vformat(input_param_for_compute_shader_mode, "custom"), "custom", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_COMPUTE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("CustomAlpha", "Input", "Compute", "VisualShaderNodeInput", vformat(input_param_for_compute_shader_mode, "custom_alpha"), "custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_COMPUTE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Delta", "Input", "Compute", "VisualShaderNodeInput", vformat(input_param_for_compute_shader_mode, "delta"), "delta", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_COMPUTE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("EmissionTransform", "Input", "Compute", "VisualShaderNodeInput", vformat(input_param_for_compute_shader_mode, "emission_transform"), "emission_transform", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_COMPUTE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Index", "Input", "Compute", "VisualShaderNodeInput", vformat(input_param_for_compute_shader_mode, "index"), "index", VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShader::TYPE_COMPUTE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("LifeTime", "Input", "Compute", "VisualShaderNodeInput", vformat(input_param_for_compute_shader_mode, "lifetime"), "lifetime", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_COMPUTE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Restart", "Input", "Compute", "VisualShaderNodeInput", vformat(input_param_for_compute_shader_mode, "restart"), "restart", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_COMPUTE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Time", "Input", "Compute", "VisualShaderNodeInput", vformat(input_param_for_compute_shader_mode, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_COMPUTE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Transform", "Input", "Compute", "VisualShaderNodeInput", vformat(input_param_for_compute_shader_mode, "transform"), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_COMPUTE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Velocity", "Input", "Compute", "VisualShaderNodeInput", vformat(input_param_for_compute_shader_mode, "velocity"), "velocity", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_COMPUTE, Shader::MODE_PARTICLES)); // SKY INPUTS diff --git a/editor/pot_generator.cpp b/editor/pot_generator.cpp index f9b8722aad..f09750efdc 100644 --- a/editor/pot_generator.cpp +++ b/editor/pot_generator.cpp @@ -31,23 +31,25 @@ #include "pot_generator.h" #include "core/error_macros.h" -#include "core/os/file_access.h" #include "core/project_settings.h" #include "editor_translation_parser.h" #include "plugins/packed_scene_translation_parser_plugin.h" POTGenerator *POTGenerator::singleton = nullptr; -//#define DEBUG_POT - #ifdef DEBUG_POT -void _print_all_translation_strings(const OrderedHashMap<String, Set<String>> &p_all_translation_strings) { - for (auto E_pair = p_all_translation_strings.front(); E_pair; E_pair = E_pair.next()) { - String msg = static_cast<String>(E_pair.key()) + " : "; - for (Set<String>::Element *E = E_pair.value().front(); E; E = E->next()) { - msg += E->get() + " "; +void POTGenerator::_print_all_translation_strings() { + for (auto E = all_translation_strings.front(); E; E = E.next()) { + Vector<MsgidData> v_md = all_translation_strings[E.key()]; + for (int i = 0; i < v_md.size(); i++) { + print_line("++++++"); + print_line("msgid: " + E.key()); + print_line("context: " + v_md[i].ctx); + print_line("msgid_plural: " + v_md[i].plural); + for (Set<String>::Element *E = v_md[i].locations.front(); E; E = E->next()) { + print_line("location: " + E->get()); + } } - print_line(msg); } } #endif @@ -65,27 +67,27 @@ void POTGenerator::generate_pot(const String &p_file) { // Collect all translatable strings according to files order in "POT Generation" setting. for (int i = 0; i < files.size(); i++) { - Vector<String> translation_strings; + Vector<String> msgids; + Vector<Vector<String>> msgids_context_plural; String file_path = files[i]; String file_extension = file_path.get_extension(); if (EditorTranslationParser::get_singleton()->can_parse(file_extension)) { - EditorTranslationParser::get_singleton()->get_parser(file_extension)->parse_file(file_path, &translation_strings); + EditorTranslationParser::get_singleton()->get_parser(file_extension)->parse_file(file_path, &msgids, &msgids_context_plural); } else { ERR_PRINT("Unrecognized file extension " + file_extension + " in generate_pot()"); return; } - // Store translation strings parsed in this iteration along with their corresponding source file - to write into POT later on. - for (int j = 0; j < translation_strings.size(); j++) { - all_translation_strings[translation_strings[j]].insert(file_path); + for (int j = 0; j < msgids_context_plural.size(); j++) { + Vector<String> entry = msgids_context_plural[j]; + _add_new_msgid(entry[0], entry[1], entry[2], file_path); + } + for (int j = 0; j < msgids.size(); j++) { + _add_new_msgid(msgids[j], "", "", file_path); } } -#ifdef DEBUG_POT - _print_all_translation_strings(all_translation_strings); -#endif - _write_to_pot(p_file); } @@ -119,35 +121,86 @@ void POTGenerator::_write_to_pot(const String &p_file) { file->store_string(header); - for (OrderedHashMap<String, Set<String>>::Element E_pair = all_translation_strings.front(); E_pair; E_pair = E_pair.next()) { - String msg = E_pair.key(); + for (OrderedHashMap<String, Vector<MsgidData>>::Element E_pair = all_translation_strings.front(); E_pair; E_pair = E_pair.next()) { + String msgid = E_pair.key(); + Vector<MsgidData> v_msgid_data = E_pair.value(); + for (int i = 0; i < v_msgid_data.size(); i++) { + String context = v_msgid_data[i].ctx; + String plural = v_msgid_data[i].plural; + const Set<String> &locations = v_msgid_data[i].locations; + + // Write file locations. + for (Set<String>::Element *E = locations.front(); E; E = E->next()) { + file->store_line("#: " + E->get().trim_prefix("res://")); + } - // Write file locations. - for (Set<String>::Element *E = E_pair.value().front(); E; E = E->next()) { - file->store_line("#: " + E->get().trim_prefix("res://")); - } + // Write context. + if (!context.empty()) { + file->store_line("msgctxt \"" + context + "\""); + } - // Split \\n and \n. - Vector<String> temp = msg.split("\\n"); - Vector<String> msg_lines; - for (int i = 0; i < temp.size(); i++) { - msg_lines.append_array(temp[i].split("\n")); - if (i < temp.size() - 1) { - // Add \n. - msg_lines.set(msg_lines.size() - 1, msg_lines[msg_lines.size() - 1] + "\\n"); + // Write msgid. + _write_msgid(file, msgid, false); + + // Write msgid_plural + if (!plural.empty()) { + _write_msgid(file, plural, true); + file->store_line("msgstr[0] \"\""); + file->store_line("msgstr[1] \"\"\n"); + } else { + file->store_line("msgstr \"\"\n"); } } + } - // Write msgid. - file->store_string("msgid "); - for (int i = 0; i < msg_lines.size(); i++) { - file->store_line("\"" + msg_lines[i] + "\""); + file->close(); +} + +void POTGenerator::_write_msgid(FileAccess *r_file, const String &p_id, bool p_plural) { + // Split \\n and \n. + Vector<String> temp = p_id.split("\\n"); + Vector<String> msg_lines; + for (int i = 0; i < temp.size(); i++) { + msg_lines.append_array(temp[i].split("\n")); + if (i < temp.size() - 1) { + // Add \n. + msg_lines.set(msg_lines.size() - 1, msg_lines[msg_lines.size() - 1] + "\\n"); } + } - file->store_line("msgstr \"\"\n"); + if (p_plural) { + r_file->store_string("msgid_plural "); + } else { + r_file->store_string("msgid "); } - file->close(); + for (int i = 0; i < msg_lines.size(); i++) { + r_file->store_line("\"" + msg_lines[i] + "\""); + } +} + +void POTGenerator::_add_new_msgid(const String &p_msgid, const String &p_context, const String &p_plural, const String &p_location) { + // Insert new location if msgid under same context exists already. + if (all_translation_strings.has(p_msgid)) { + Vector<MsgidData> &v_mdata = all_translation_strings[p_msgid]; + for (int i = 0; i < v_mdata.size(); i++) { + if (v_mdata[i].ctx == p_context) { + if (!v_mdata[i].plural.empty() && !p_plural.empty() && v_mdata[i].plural != p_plural) { + WARN_PRINT("Redefinition of plural message (msgid_plural), under the same message (msgid) and context (msgctxt)"); + } + v_mdata.write[i].locations.insert(p_location); + return; + } + } + } + + // Add a new entry of msgid, context, plural and location - context and plural might be empty if the inserted msgid doesn't associated + // context or plurals. + MsgidData mdata; + mdata.ctx = p_context; + mdata.plural = p_plural; + mdata.locations.insert(p_location); + all_translation_strings[p_msgid].push_back(mdata); } POTGenerator *POTGenerator::get_singleton() { diff --git a/editor/pot_generator.h b/editor/pot_generator.h index abe1a21d41..8853b784ed 100644 --- a/editor/pot_generator.h +++ b/editor/pot_generator.h @@ -32,14 +32,29 @@ #define POT_GENERATOR_H #include "core/ordered_hash_map.h" +#include "core/os/file_access.h" #include "core/set.h" +//#define DEBUG_POT + class POTGenerator { static POTGenerator *singleton; - // Stores all translatable strings and the source files containing them. - OrderedHashMap<String, Set<String>> all_translation_strings; + + struct MsgidData { + String ctx; + String plural; + Set<String> locations; + }; + // Store msgid as key and the additional data around the msgid - if it's under a context, has plurals and its file locations. + OrderedHashMap<String, Vector<MsgidData>> all_translation_strings; void _write_to_pot(const String &p_file); + void _write_msgid(FileAccess *r_file, const String &p_id, bool p_plural); + void _add_new_msgid(const String &p_msgid, const String &p_context, const String &p_plural, const String &p_location); + +#ifdef DEBUG_POT + void _print_all_translation_strings(); +#endif public: static POTGenerator *get_singleton(); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 35311b32eb..ea55029de0 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -330,6 +330,7 @@ private: return; } } + String sp = p.simplify_path(); project_path->set_text(sp); _path_text_changed(sp); @@ -411,7 +412,7 @@ private: _test_path(); if (p_text == "") { - set_message(TTR("It would be a good idea to name your project."), MESSAGE_WARNING); + set_message(TTR("It would be a good idea to name your project."), MESSAGE_ERROR); } } @@ -1012,7 +1013,7 @@ public: void update_dock_menu(); void load_projects(); void set_search_term(String p_search_term); - void set_order_option(ProjectListFilter::FilterOption p_option); + void set_order_option(int p_option); void sort_projects(); int get_project_count() const; void select_project(int p_index); @@ -1045,7 +1046,7 @@ private: static void load_project_data(const String &p_property_key, Item &p_item, bool p_favorite); String _search_term; - ProjectListFilter::FilterOption _order_option; + FilterOption _order_option; Set<String> _selected_project_keys; String _last_clicked; // Project key VBoxContainer *_scroll_children; @@ -1055,7 +1056,7 @@ private: }; struct ProjectListComparator { - ProjectListFilter::FilterOption order_option; + FilterOption order_option; // operator< _FORCE_INLINE_ bool operator()(const ProjectList::Item &a, const ProjectList::Item &b) const { @@ -1066,9 +1067,9 @@ struct ProjectListComparator { return false; } switch (order_option) { - case ProjectListFilter::FILTER_PATH: + case PATH: return a.project_key < b.project_key; - case ProjectListFilter::FILTER_EDIT_DATE: + case EDIT_DATE: return a.last_edited > b.last_edited; default: return a.project_name < b.project_name; @@ -1077,8 +1078,7 @@ struct ProjectListComparator { }; ProjectList::ProjectList() { - _order_option = ProjectListFilter::FILTER_EDIT_DATE; - + _order_option = FilterOption::NAME; _scroll_children = memnew(VBoxContainer); _scroll_children->set_h_size_flags(Control::SIZE_EXPAND_FILL); add_child(_scroll_children); @@ -1238,8 +1238,6 @@ void ProjectList::load_projects() { create_project_item_control(i); } - sort_projects(); - set_v_scroll(0); update_icons_async(); @@ -1391,12 +1389,13 @@ void ProjectList::set_search_term(String p_search_term) { _search_term = p_search_term; } -void ProjectList::set_order_option(ProjectListFilter::FilterOption p_option) { - if (_order_option != p_option) { - _order_option = p_option; - EditorSettings::get_singleton()->set("project_manager/sorting_order", (int)_order_option); - EditorSettings::get_singleton()->save(); - } +void ProjectList::set_order_option(int p_option) { + FilterOption selected = (FilterOption)p_option; + EditorSettings::get_singleton()->set("project_manager/sorting_order", p_option); + EditorSettings::get_singleton()->save(); + _order_option = selected; + + sort_projects(); } void ProjectList::sort_projects() { @@ -1798,6 +1797,9 @@ void ProjectList::_bind_methods() { void ProjectManager::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { + search_box->set_right_icon(get_theme_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); + Engine::get_singleton()->set_editor_hint(false); } break; case NOTIFICATION_RESIZED: { @@ -1806,6 +1808,10 @@ void ProjectManager::_notification(int p_what) { } } break; case NOTIFICATION_READY: { + int default_sorting = (int)EditorSettings::get_singleton()->get("project_manager/sorting_order"); + filter_option->select(default_sorting); + _project_list->set_order_option(default_sorting); + if (_project_list->get_project_count() == 0 && StreamPeerSSL::is_available()) { open_templates->popup_centered(); } @@ -1813,7 +1819,7 @@ void ProjectManager::_notification(int p_what) { 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 - project_filter->search_box->grab_focus(); + search_box->grab_focus(); } } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -1833,7 +1839,7 @@ void ProjectManager::_dim_window() { // No transition is applied, as the effect needs to be visible immediately float c = 0.5f; Color dim_color = Color(c, c, c); - gui_base->set_modulate(dim_color); + set_modulate(dim_color); } void ProjectManager::_update_project_buttons() { @@ -1853,7 +1859,7 @@ void ProjectManager::_update_project_buttons() { rename_btn->set_disabled(empty_selection || is_missing_project_selected); run_btn->set_disabled(empty_selection || is_missing_project_selected); - erase_missing_btn->set_visible(_project_list->is_any_project_missing()); + erase_missing_btn->set_disabled(!_project_list->is_any_project_missing()); } void ProjectManager::_unhandled_input(const Ref<InputEvent> &p_ev) { @@ -1930,7 +1936,7 @@ void ProjectManager::_unhandled_input(const Ref<InputEvent> &p_ev) { } break; case KEY_F: { if (k->get_command()) { - this->project_filter->search_box->grab_focus(); + this->search_box->grab_focus(); } else { keycode_handled = false; } @@ -1947,8 +1953,7 @@ void ProjectManager::_unhandled_input(const Ref<InputEvent> &p_ev) { } void ProjectManager::_load_recent_projects() { - _project_list->set_order_option(project_order_filter->get_filter_option()); - _project_list->set_search_term(project_filter->get_search_term()); + _project_list->set_search_term(search_box->get_text().strip_edges()); _project_list->load_projects(); _update_project_buttons(); @@ -1970,7 +1975,7 @@ void ProjectManager::_on_projects_updated() { } void ProjectManager::_on_project_created(const String &dir) { - project_filter->clear(); + search_box->clear(); int i = _project_list->refresh_project(dir); _project_list->select_project(i); _project_list->ensure_project_visible(i); @@ -2113,7 +2118,6 @@ void ProjectManager::_run_project_confirm() { } } -// When you press the "Run" button void ProjectManager::_run_project() { const Set<String> &selected_list = _project_list->get_selected_project_keys(); @@ -2226,8 +2230,6 @@ void ProjectManager::_erase_missing_projects() { void ProjectManager::_language_selected(int p_id) { String lang = language_btn->get_item_metadata(p_id); EditorSettings::get_singleton()->set("interface/editor/editor_language", lang); - language_btn->set_text(lang); - language_btn->set_icon(get_theme_icon("Environment", "EditorIcons")); language_restart_ask->set_text(TTR("Language changed.\nThe interface will update after restarting the editor or project manager.")); language_restart_ask->popup_centered(); @@ -2304,13 +2306,14 @@ void ProjectManager::_scan_multiple_folders(PackedStringArray p_files) { } } -void ProjectManager::_on_order_option_changed() { - _project_list->set_order_option(project_order_filter->get_filter_option()); - _project_list->sort_projects(); +void ProjectManager::_on_order_option_changed(int p_idx) { + if (is_inside_tree()) { + _project_list->set_order_option(p_idx); + } } -void ProjectManager::_on_filter_option_changed() { - _project_list->set_search_term(project_filter->get_search_term()); +void ProjectManager::_on_search_term_changed(const String &p_term) { + _project_list->set_search_term(p_term); _project_list->sort_projects(); // Select the first visible project in the list. @@ -2341,7 +2344,6 @@ ProjectManager::ProjectManager() { { int display_scale = EditorSettings::get_singleton()->get("interface/editor/display_scale"); - float custom_display_scale = EditorSettings::get_singleton()->get("interface/editor/custom_display_scale"); switch (display_scale) { case 0: { @@ -2372,9 +2374,8 @@ ProjectManager::ProjectManager() { case 6: editor_set_scale(2.0); break; - default: { - editor_set_scale(custom_display_scale); + editor_set_scale(EditorSettings::get_singleton()->get("interface/editor/custom_display_scale")); } break; } @@ -2385,28 +2386,26 @@ ProjectManager::ProjectManager() { DisplayServer::get_singleton()->window_set_size(DisplayServer::get_singleton()->window_get_size() * MAX(1, EDSCALE)); } + String cp; + cp += 0xA9; + DisplayServer::get_singleton()->window_set_title(VERSION_NAME + String(" - ") + TTR("Project Manager") + " - " + cp + " 2007-2020 Juan Linietsky, Ariel Manzur & Godot Contributors"); + FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files")); set_anchors_and_margins_preset(Control::PRESET_WIDE); set_theme(create_custom_theme()); - gui_base = memnew(Control); - add_child(gui_base); - gui_base->set_anchors_and_margins_preset(Control::PRESET_WIDE); + set_anchors_and_margins_preset(Control::PRESET_WIDE); Panel *panel = memnew(Panel); - gui_base->add_child(panel); + add_child(panel); panel->set_anchors_and_margins_preset(Control::PRESET_WIDE); - panel->add_theme_style_override("panel", gui_base->get_theme_stylebox("Background", "EditorStyles")); + panel->add_theme_style_override("panel", get_theme_stylebox("Background", "EditorStyles")); VBoxContainer *vb = memnew(VBoxContainer); panel->add_child(vb); vb->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 8 * EDSCALE); - String cp; - cp += 0xA9; - DisplayServer::get_singleton()->window_set_title(VERSION_NAME + String(" - ") + TTR("Project Manager") + " - " + cp + " 2007-2020 Juan Linietsky, Ariel Manzur & Godot Contributors"); - Control *center_box = memnew(Control); center_box->set_v_size_flags(Control::SIZE_EXPAND_FILL); vb->add_child(center_box); @@ -2416,218 +2415,231 @@ ProjectManager::ProjectManager() { tabs->set_anchors_and_margins_preset(Control::PRESET_WIDE); tabs->set_tab_align(TabContainer::ALIGN_LEFT); - HBoxContainer *tree_hb = memnew(HBoxContainer); - projects_hb = tree_hb; - + HBoxContainer *projects_hb = memnew(HBoxContainer); projects_hb->set_name(TTR("Projects")); + tabs->add_child(projects_hb); - tabs->add_child(tree_hb); - - VBoxContainer *search_tree_vb = memnew(VBoxContainer); - tree_hb->add_child(search_tree_vb); - search_tree_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL); - - HBoxContainer *sort_filters = memnew(HBoxContainer); - Label *sort_label = memnew(Label); - sort_label->set_text(TTR("Sort:")); - sort_filters->add_child(sort_label); - Vector<String> sort_filter_titles; - sort_filter_titles.push_back(TTR("Name")); - sort_filter_titles.push_back(TTR("Path")); - sort_filter_titles.push_back(TTR("Last Edited")); - project_order_filter = memnew(ProjectListFilter); - project_order_filter->add_filter_option(); - project_order_filter->_setup_filters(sort_filter_titles); - project_order_filter->set_filter_size(150); - sort_filters->add_child(project_order_filter); - project_order_filter->connect("filter_changed", callable_mp(this, &ProjectManager::_on_order_option_changed)); - project_order_filter->set_custom_minimum_size(Size2(180, 10) * EDSCALE); - - int projects_sorting_order = (int)EditorSettings::get_singleton()->get("project_manager/sorting_order"); - project_order_filter->set_filter_option((ProjectListFilter::FilterOption)projects_sorting_order); - - sort_filters->add_spacer(true); - - project_filter = memnew(ProjectListFilter); - project_filter->add_search_box(); - project_filter->connect("filter_changed", callable_mp(this, &ProjectManager::_on_filter_option_changed)); - project_filter->set_custom_minimum_size(Size2(280, 10) * EDSCALE); - sort_filters->add_child(project_filter); - - search_tree_vb->add_child(sort_filters); - - PanelContainer *pc = memnew(PanelContainer); - pc->add_theme_style_override("panel", gui_base->get_theme_stylebox("bg", "Tree")); - search_tree_vb->add_child(pc); - pc->set_v_size_flags(Control::SIZE_EXPAND_FILL); - - _project_list = memnew(ProjectList); - _project_list->connect(ProjectList::SIGNAL_SELECTION_CHANGED, callable_mp(this, &ProjectManager::_update_project_buttons)); - _project_list->connect(ProjectList::SIGNAL_PROJECT_ASK_OPEN, callable_mp(this, &ProjectManager::_open_selected_projects_ask)); - pc->add_child(_project_list); - _project_list->set_enable_h_scroll(false); - - VBoxContainer *tree_vb = memnew(VBoxContainer); - tree_hb->add_child(tree_vb); - - Button *open = memnew(Button); - open->set_text(TTR("Edit")); - tree_vb->add_child(open); - open->connect("pressed", callable_mp(this, &ProjectManager::_open_selected_projects_ask)); - open_btn = open; - - Button *run = memnew(Button); - run->set_text(TTR("Run")); - tree_vb->add_child(run); - run->connect("pressed", callable_mp(this, &ProjectManager::_run_project)); - run_btn = run; - - tree_vb->add_child(memnew(HSeparator)); - - Button *scan = memnew(Button); - scan->set_text(TTR("Scan")); - tree_vb->add_child(scan); - scan->connect("pressed", callable_mp(this, &ProjectManager::_scan_projects)); - - tree_vb->add_child(memnew(HSeparator)); - - scan_dir = memnew(FileDialog); - scan_dir->set_access(FileDialog::ACCESS_FILESYSTEM); - scan_dir->set_file_mode(FileDialog::FILE_MODE_OPEN_DIR); - scan_dir->set_title(TTR("Select a Folder to Scan")); // must be after mode or it's overridden - scan_dir->set_current_dir(EditorSettings::get_singleton()->get("filesystem/directories/default_project_path")); - gui_base->add_child(scan_dir); - scan_dir->connect("dir_selected", callable_mp(this, &ProjectManager::_scan_begin)); - - Button *create = memnew(Button); - create->set_text(TTR("New Project")); - tree_vb->add_child(create); - create->connect("pressed", callable_mp(this, &ProjectManager::_new_project)); - - Button *import = memnew(Button); - import->set_text(TTR("Import")); - tree_vb->add_child(import); - import->connect("pressed", callable_mp(this, &ProjectManager::_import_project)); - - Button *rename = memnew(Button); - rename->set_text(TTR("Rename")); - tree_vb->add_child(rename); - rename->connect("pressed", callable_mp(this, &ProjectManager::_rename_project)); - rename_btn = rename; - - Button *erase = memnew(Button); - erase->set_text(TTR("Remove")); - tree_vb->add_child(erase); - erase->connect("pressed", callable_mp(this, &ProjectManager::_erase_project)); - erase_btn = erase; - - Button *erase_missing = memnew(Button); - erase_missing->set_text(TTR("Remove Missing")); - tree_vb->add_child(erase_missing); - erase_missing->connect("pressed", callable_mp(this, &ProjectManager::_erase_missing_projects)); - erase_missing_btn = erase_missing; - - tree_vb->add_spacer(); + { + // Projects + search bar + VBoxContainer *search_tree_vb = memnew(VBoxContainer); + projects_hb->add_child(search_tree_vb); + search_tree_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL); - if (StreamPeerSSL::is_available()) { - asset_library = memnew(EditorAssetLibrary(true)); - asset_library->set_name(TTR("Templates")); - tabs->add_child(asset_library); - asset_library->connect("install_asset", callable_mp(this, &ProjectManager::_install_project)); - } else { - WARN_PRINT("Asset Library not available, as it requires SSL to work."); - } + HBoxContainer *hb = memnew(HBoxContainer); + hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + search_tree_vb->add_child(hb); - HBoxContainer *settings_hb = memnew(HBoxContainer); - settings_hb->set_alignment(BoxContainer::ALIGN_END); - settings_hb->set_h_grow_direction(Control::GROW_DIRECTION_BEGIN); + search_box = memnew(LineEdit); + search_box->set_placeholder(TTR("Search")); + search_box->set_tooltip(TTR("The search box filters projects by name and last path component.\nTo filter projects by name and full path, the query must contain at least one `/` character.")); + search_box->connect("text_changed", callable_mp(this, &ProjectManager::_on_search_term_changed)); + search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hb->add_child(search_box); - Label *version_label = memnew(Label); - String hash = String(VERSION_HASH); - if (hash.length() != 0) { - hash = "." + hash.left(9); - } - version_label->set_text("v" VERSION_FULL_BUILD "" + hash); - // Fade out the version label to be less prominent, but still readable - version_label->set_self_modulate(Color(1, 1, 1, 0.6)); - version_label->set_align(Label::ALIGN_CENTER); - settings_hb->add_child(version_label); + hb->add_spacer(); - language_btn = memnew(OptionButton); - language_btn->set_flat(true); - language_btn->set_focus_mode(Control::FOCUS_NONE); + Label *sort_label = memnew(Label); + sort_label->set_text(TTR("Sort:")); + hb->add_child(sort_label); - Vector<String> editor_languages; - List<PropertyInfo> editor_settings_properties; - EditorSettings::get_singleton()->get_property_list(&editor_settings_properties); - for (List<PropertyInfo>::Element *E = editor_settings_properties.front(); E; E = E->next()) { - PropertyInfo &pi = E->get(); - if (pi.name == "interface/editor/editor_language") { - editor_languages = pi.hint_string.split(","); - } - } - String current_lang = EditorSettings::get_singleton()->get("interface/editor/editor_language"); - for (int i = 0; i < editor_languages.size(); i++) { - String lang = editor_languages[i]; - String lang_name = TranslationServer::get_singleton()->get_locale_name(lang); - language_btn->add_item(lang_name + " [" + lang + "]", i); - language_btn->set_item_metadata(i, lang); - if (current_lang == lang) { - language_btn->select(i); - language_btn->set_text(lang); - } - } - language_btn->set_icon(get_theme_icon("Environment", "EditorIcons")); + filter_option = memnew(OptionButton); + filter_option->set_clip_text(true); + filter_option->set_custom_minimum_size(Size2(150 * EDSCALE, 10 * EDSCALE)); + filter_option->connect("item_selected", callable_mp(this, &ProjectManager::_on_order_option_changed)); + hb->add_child(filter_option); - settings_hb->add_child(language_btn); - language_btn->connect("item_selected", callable_mp(this, &ProjectManager::_language_selected)); + Vector<String> sort_filter_titles; + sort_filter_titles.push_back(TTR("Name")); + sort_filter_titles.push_back(TTR("Path")); + sort_filter_titles.push_back(TTR("Last Edited")); - center_box->add_child(settings_hb); - settings_hb->set_anchors_and_margins_preset(Control::PRESET_TOP_RIGHT); + for (int i = 0; i < sort_filter_titles.size(); i++) { + filter_option->add_item(sort_filter_titles[i]); + } - ////////////////////////////////////////////////////////////// + PanelContainer *pc = memnew(PanelContainer); + pc->add_theme_style_override("panel", get_theme_stylebox("bg", "Tree")); + pc->set_v_size_flags(Control::SIZE_EXPAND_FILL); + search_tree_vb->add_child(pc); - language_restart_ask = memnew(ConfirmationDialog); - language_restart_ask->get_ok()->set_text(TTR("Restart Now")); - language_restart_ask->get_ok()->connect("pressed", callable_mp(this, &ProjectManager::_restart_confirm)); - language_restart_ask->get_cancel()->set_text(TTR("Continue")); - gui_base->add_child(language_restart_ask); + _project_list = memnew(ProjectList); + _project_list->connect(ProjectList::SIGNAL_SELECTION_CHANGED, callable_mp(this, &ProjectManager::_update_project_buttons)); + _project_list->connect(ProjectList::SIGNAL_PROJECT_ASK_OPEN, callable_mp(this, &ProjectManager::_open_selected_projects_ask)); + _project_list->set_enable_h_scroll(false); + pc->add_child(_project_list); + } - erase_missing_ask = memnew(ConfirmationDialog); - erase_missing_ask->get_ok()->set_text(TTR("Remove All")); - erase_missing_ask->get_ok()->connect("pressed", callable_mp(this, &ProjectManager::_erase_missing_projects_confirm)); - gui_base->add_child(erase_missing_ask); + { + // Project tab side bar + VBoxContainer *tree_vb = memnew(VBoxContainer); + tree_vb->set_custom_minimum_size(Size2(120, 120)); + projects_hb->add_child(tree_vb); + + Button *create = memnew(Button); + create->set_text(TTR("New Project")); + create->connect("pressed", callable_mp(this, &ProjectManager::_new_project)); + tree_vb->add_child(create); + + Button *import = memnew(Button); + import->set_text(TTR("Import")); + import->connect("pressed", callable_mp(this, &ProjectManager::_import_project)); + tree_vb->add_child(import); + + Button *scan = memnew(Button); + scan->set_text(TTR("Scan")); + scan->connect("pressed", callable_mp(this, &ProjectManager::_scan_projects)); + tree_vb->add_child(scan); + + tree_vb->add_child(memnew(HSeparator)); + + open_btn = memnew(Button); + open_btn->set_text(TTR("Edit")); + open_btn->connect("pressed", callable_mp(this, &ProjectManager::_open_selected_projects_ask)); + tree_vb->add_child(open_btn); + + run_btn = memnew(Button); + run_btn->set_text(TTR("Run")); + run_btn->connect("pressed", callable_mp(this, &ProjectManager::_run_project)); + tree_vb->add_child(run_btn); + + rename_btn = memnew(Button); + rename_btn->set_text(TTR("Rename")); + rename_btn->connect("pressed", callable_mp(this, &ProjectManager::_rename_project)); + tree_vb->add_child(rename_btn); + + erase_btn = memnew(Button); + erase_btn->set_text(TTR("Remove")); + erase_btn->connect("pressed", callable_mp(this, &ProjectManager::_erase_project)); + tree_vb->add_child(erase_btn); + + erase_missing_btn = memnew(Button); + erase_missing_btn->set_text(TTR("Remove Missing")); + erase_missing_btn->connect("pressed", callable_mp(this, &ProjectManager::_erase_missing_projects)); + tree_vb->add_child(erase_missing_btn); + } - erase_ask = memnew(ConfirmationDialog); - erase_ask->get_ok()->set_text(TTR("Remove")); - erase_ask->get_ok()->connect("pressed", callable_mp(this, &ProjectManager::_erase_project_confirm)); - gui_base->add_child(erase_ask); + { + // Version info and language options + HBoxContainer *settings_hb = memnew(HBoxContainer); + settings_hb->set_alignment(BoxContainer::ALIGN_END); + settings_hb->set_h_grow_direction(Control::GROW_DIRECTION_BEGIN); + + Label *version_label = memnew(Label); + String hash = String(VERSION_HASH); + if (hash.length() != 0) { + hash = "." + hash.left(9); + } + version_label->set_text("v" VERSION_FULL_BUILD "" + hash); + version_label->set_self_modulate(Color(1, 1, 1, 0.6)); + version_label->set_align(Label::ALIGN_CENTER); + settings_hb->add_child(version_label); + + language_btn = memnew(OptionButton); + language_btn->set_flat(true); + language_btn->set_icon(get_theme_icon("Environment", "EditorIcons")); + language_btn->set_focus_mode(Control::FOCUS_NONE); + language_btn->connect("item_selected", callable_mp(this, &ProjectManager::_language_selected)); + + Vector<String> editor_languages; + List<PropertyInfo> editor_settings_properties; + EditorSettings::get_singleton()->get_property_list(&editor_settings_properties); + for (List<PropertyInfo>::Element *E = editor_settings_properties.front(); E; E = E->next()) { + PropertyInfo &pi = E->get(); + if (pi.name == "interface/editor/editor_language") { + editor_languages = pi.hint_string.split(","); + break; + } + } - multi_open_ask = memnew(ConfirmationDialog); - multi_open_ask->get_ok()->set_text(TTR("Edit")); - multi_open_ask->get_ok()->connect("pressed", callable_mp(this, &ProjectManager::_open_selected_projects)); - gui_base->add_child(multi_open_ask); + String current_lang = EditorSettings::get_singleton()->get("interface/editor/editor_language"); + language_btn->set_text(current_lang); - multi_run_ask = memnew(ConfirmationDialog); - multi_run_ask->get_ok()->set_text(TTR("Run")); - multi_run_ask->get_ok()->connect("pressed", callable_mp(this, &ProjectManager::_run_project_confirm)); - gui_base->add_child(multi_run_ask); + for (int i = 0; i < editor_languages.size(); i++) { + String lang = editor_languages[i]; + String lang_name = TranslationServer::get_singleton()->get_locale_name(lang); + language_btn->add_item(lang_name + " [" + lang + "]", i); + language_btn->set_item_metadata(i, lang); + if (current_lang == lang) { + language_btn->select(i); + } + } - multi_scan_ask = memnew(ConfirmationDialog); - multi_scan_ask->get_ok()->set_text(TTR("Scan")); - gui_base->add_child(multi_scan_ask); + settings_hb->add_child(language_btn); + center_box->add_child(settings_hb); + settings_hb->set_anchors_and_margins_preset(Control::PRESET_TOP_RIGHT); + } - ask_update_settings = memnew(ConfirmationDialog); - ask_update_settings->get_ok()->connect("pressed", callable_mp(this, &ProjectManager::_confirm_update_settings)); - gui_base->add_child(ask_update_settings); + if (StreamPeerSSL::is_available()) { + asset_library = memnew(EditorAssetLibrary(true)); + asset_library->set_name(TTR("Templates")); + tabs->add_child(asset_library); + asset_library->connect("install_asset", callable_mp(this, &ProjectManager::_install_project)); + } else { + WARN_PRINT("Asset Library not available, as it requires SSL to work."); + } - OS::get_singleton()->set_low_processor_usage_mode(true); + { + // Dialogs + language_restart_ask = memnew(ConfirmationDialog); + language_restart_ask->get_ok()->set_text(TTR("Restart Now")); + language_restart_ask->get_ok()->connect("pressed", callable_mp(this, &ProjectManager::_restart_confirm)); + language_restart_ask->get_cancel()->set_text(TTR("Continue")); + add_child(language_restart_ask); + + scan_dir = memnew(FileDialog); + scan_dir->set_access(FileDialog::ACCESS_FILESYSTEM); + scan_dir->set_file_mode(FileDialog::FILE_MODE_OPEN_DIR); + scan_dir->set_title(TTR("Select a Folder to Scan")); // must be after mode or it's overridden + scan_dir->set_current_dir(EditorSettings::get_singleton()->get("filesystem/directories/default_project_path")); + add_child(scan_dir); + scan_dir->connect("dir_selected", callable_mp(this, &ProjectManager::_scan_begin)); + + erase_missing_ask = memnew(ConfirmationDialog); + erase_missing_ask->get_ok()->set_text(TTR("Remove All")); + erase_missing_ask->get_ok()->connect("pressed", callable_mp(this, &ProjectManager::_erase_missing_projects_confirm)); + add_child(erase_missing_ask); + + erase_ask = memnew(ConfirmationDialog); + erase_ask->get_ok()->set_text(TTR("Remove")); + erase_ask->get_ok()->connect("pressed", callable_mp(this, &ProjectManager::_erase_project_confirm)); + add_child(erase_ask); + + multi_open_ask = memnew(ConfirmationDialog); + multi_open_ask->get_ok()->set_text(TTR("Edit")); + multi_open_ask->get_ok()->connect("pressed", callable_mp(this, &ProjectManager::_open_selected_projects)); + add_child(multi_open_ask); + + multi_run_ask = memnew(ConfirmationDialog); + multi_run_ask->get_ok()->set_text(TTR("Run")); + multi_run_ask->get_ok()->connect("pressed", callable_mp(this, &ProjectManager::_run_project_confirm)); + add_child(multi_run_ask); + + multi_scan_ask = memnew(ConfirmationDialog); + multi_scan_ask->get_ok()->set_text(TTR("Scan")); + add_child(multi_scan_ask); + + ask_update_settings = memnew(ConfirmationDialog); + ask_update_settings->get_ok()->connect("pressed", callable_mp(this, &ProjectManager::_confirm_update_settings)); + add_child(ask_update_settings); + + npdialog = memnew(ProjectDialog); + npdialog->connect("projects_updated", callable_mp(this, &ProjectManager::_on_projects_updated)); + npdialog->connect("project_created", callable_mp(this, &ProjectManager::_on_project_created)); + add_child(npdialog); + + run_error_diag = memnew(AcceptDialog); + run_error_diag->set_title(TTR("Can't run project")); + add_child(run_error_diag); - npdialog = memnew(ProjectDialog); - gui_base->add_child(npdialog); + dialog_error = memnew(AcceptDialog); + add_child(dialog_error); - npdialog->connect("projects_updated", callable_mp(this, &ProjectManager::_on_projects_updated)); - npdialog->connect("project_created", callable_mp(this, &ProjectManager::_on_project_created)); + open_templates = memnew(ConfirmationDialog); + open_templates->set_text(TTR("You currently don't have any projects.\nWould you like to explore official example projects in the Asset Library?")); + open_templates->get_ok()->set_text(TTR("Open Asset Library")); + open_templates->connect("confirmed", callable_mp(this, &ProjectManager::_open_asset_library)); + add_child(open_templates); + } _load_recent_projects(); @@ -2637,18 +2649,7 @@ ProjectManager::ProjectManager() { SceneTree::get_singleton()->get_root()->connect("files_dropped", callable_mp(this, &ProjectManager::_files_dropped)); - run_error_diag = memnew(AcceptDialog); - gui_base->add_child(run_error_diag); - run_error_diag->set_title(TTR("Can't run project")); - - dialog_error = memnew(AcceptDialog); - gui_base->add_child(dialog_error); - - open_templates = memnew(ConfirmationDialog); - open_templates->set_text(TTR("You currently don't have any projects.\nWould you like to explore official example projects in the Asset Library?")); - open_templates->get_ok()->set_text(TTR("Open Asset Library")); - open_templates->connect("confirmed", callable_mp(this, &ProjectManager::_open_asset_library)); - add_child(open_templates); + OS::get_singleton()->set_low_processor_usage_mode(true); } ProjectManager::~ProjectManager() { @@ -2656,82 +2657,3 @@ ProjectManager::~ProjectManager() { EditorSettings::destroy(); } } - -void ProjectListFilter::_setup_filters(Vector<String> options) { - filter_option->clear(); - for (int i = 0; i < options.size(); i++) { - filter_option->add_item(options[i]); - } -} - -void ProjectListFilter::_search_text_changed(const String &p_newtext) { - emit_signal("filter_changed"); -} - -String ProjectListFilter::get_search_term() { - return search_box->get_text().strip_edges(); -} - -ProjectListFilter::FilterOption ProjectListFilter::get_filter_option() { - return _current_filter; -} - -void ProjectListFilter::set_filter_option(FilterOption option) { - filter_option->select((int)option); - _filter_option_selected(0); -} - -void ProjectListFilter::_filter_option_selected(int p_idx) { - FilterOption selected = (FilterOption)(filter_option->get_selected()); - if (_current_filter != selected) { - _current_filter = selected; - if (is_inside_tree()) { - emit_signal("filter_changed"); - } - } -} - -void ProjectListFilter::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE && has_search_box) { - search_box->set_right_icon(get_theme_icon("Search", "EditorIcons")); - search_box->set_clear_button_enabled(true); - } -} - -void ProjectListFilter::_bind_methods() { - ADD_SIGNAL(MethodInfo("filter_changed")); -} - -void ProjectListFilter::add_filter_option() { - filter_option = memnew(OptionButton); - filter_option->set_clip_text(true); - filter_option->connect("item_selected", callable_mp(this, &ProjectListFilter::_filter_option_selected)); - add_child(filter_option); -} - -void ProjectListFilter::add_search_box() { - search_box = memnew(LineEdit); - search_box->set_placeholder(TTR("Search")); - search_box->set_tooltip( - TTR("The search box filters projects by name and last path component.\nTo filter projects by name and full path, the query must contain at least one `/` character.")); - search_box->connect("text_changed", callable_mp(this, &ProjectListFilter::_search_text_changed)); - search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); - add_child(search_box); - - has_search_box = true; -} - -void ProjectListFilter::set_filter_size(int h_size) { - filter_option->set_custom_minimum_size(Size2(h_size * EDSCALE, 10 * EDSCALE)); -} - -ProjectListFilter::ProjectListFilter() { - _current_filter = FILTER_NAME; - has_search_box = false; -} - -void ProjectListFilter::clear() { - if (has_search_box) { - search_box->clear(); - } -} diff --git a/editor/project_manager.h b/editor/project_manager.h index 66b38d0746..407dba0c94 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -39,22 +39,31 @@ class ProjectDialog; class ProjectList; -class ProjectListFilter; + +enum FilterOption { + NAME, + PATH, + EDIT_DATE, +}; class ProjectManager : public Control { GDCLASS(ProjectManager, Control); - Button *erase_btn; - Button *erase_missing_btn; + TabContainer *tabs; + + ProjectList *_project_list; + + LineEdit *search_box; + OptionButton *filter_option; + + Button *run_btn; Button *open_btn; Button *rename_btn; - Button *run_btn; + Button *erase_btn; + Button *erase_missing_btn; EditorAssetLibrary *asset_library; - ProjectListFilter *project_filter; - ProjectListFilter *project_order_filter; - FileDialog *scan_dir; ConfirmationDialog *language_restart_ask; ConfirmationDialog *erase_ask; @@ -64,18 +73,12 @@ class ProjectManager : public Control { ConfirmationDialog *multi_scan_ask; ConfirmationDialog *ask_update_settings; ConfirmationDialog *open_templates; + AcceptDialog *run_error_diag; AcceptDialog *dialog_error; ProjectDialog *npdialog; - HBoxContainer *projects_hb; - TabContainer *tabs; - ProjectList *_project_list; - OptionButton *language_btn; - Control *gui_base; - - bool importing; void _open_asset_library(); void _scan_projects(); @@ -94,14 +97,13 @@ class ProjectManager : public Control { void _language_selected(int p_id); void _restart_confirm(); void _exit_dialog(); - void _scan_begin(const String &p_base); - void _confirm_update_settings(); void _load_recent_projects(); void _on_project_created(const String &dir); void _on_projects_updated(); - void _update_scroll_position(const String &dir); + void _scan_multiple_folders(PackedStringArray p_files); + void _scan_begin(const String &p_base); void _scan_dir(const String &path, List<String> *r_projects); void _install_project(const String &p_zip_path, const String &p_title); @@ -109,10 +111,9 @@ class ProjectManager : public Control { void _dim_window(); void _unhandled_input(const Ref<InputEvent> &p_ev); void _files_dropped(PackedStringArray p_files, int p_screen); - void _scan_multiple_folders(PackedStringArray p_files); - void _on_order_option_changed(); - void _on_filter_option_changed(); + void _on_order_option_changed(int p_idx); + void _on_search_term_changed(const String &p_term); protected: void _notification(int p_what); @@ -123,41 +124,4 @@ public: ~ProjectManager(); }; -class ProjectListFilter : public HBoxContainer { - GDCLASS(ProjectListFilter, HBoxContainer); - -public: - enum FilterOption { - FILTER_NAME, - FILTER_PATH, - FILTER_EDIT_DATE, - }; - -private: - friend class ProjectManager; - - OptionButton *filter_option; - LineEdit *search_box; - bool has_search_box; - FilterOption _current_filter; - - void _search_text_changed(const String &p_newtext); - void _filter_option_selected(int p_idx); - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - void _setup_filters(Vector<String> options); - void add_filter_option(); - void add_search_box(); - void set_filter_size(int h_size); - String get_search_term(); - FilterOption get_filter_option(); - void set_filter_option(FilterOption); - ProjectListFilter(); - void clear(); -}; - #endif // PROJECT_MANAGER_H diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 89feb436d8..b6621d0d1e 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -113,11 +113,17 @@ void ProjectSettingsEditor::_add_setting() { inspector->set_current_section(setting.get_slice("/", 1)); } -void ProjectSettingsEditor::_delete_setting() { +void ProjectSettingsEditor::_delete_setting(bool p_confirmed) { String setting = _get_setting_name(); Variant value = ps->get(setting); int order = ps->get_order(setting); + if (!p_confirmed) { + del_confirmation->set_text(vformat(TTR("Are you sure you want to delete '%s'?"), setting)); + del_confirmation->popup_centered(); + return; + } + undo_redo->create_action(TTR("Delete Item")); undo_redo->add_do_method(ps, "clear", setting); @@ -327,6 +333,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { header->add_child(search_bar); search_box = memnew(LineEdit); + search_box->set_placeholder(TTR("Search")); search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); search_bar->add_child(search_box); @@ -394,7 +401,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { del_button = memnew(Button); del_button->set_flat(true); - del_button->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_delete_setting)); + del_button->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_delete_setting), varray(false)); hbc->add_child(del_button); error_label = memnew(Label); @@ -466,6 +473,10 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { timer->set_one_shot(true); add_child(timer); + del_confirmation = memnew(ConfirmationDialog); + del_confirmation->connect("confirmed", callable_mp(this, &ProjectSettingsEditor::_delete_setting), varray(true)); + add_child(del_confirmation); + get_ok()->set_text(TTR("Close")); set_hide_on_ok(true); } diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index 0d7e19b242..4ecd28e514 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -77,6 +77,8 @@ class ProjectSettingsEditor : public AcceptDialog { OptionButton *feature_override; Label *error_label; + ConfirmationDialog *del_confirmation; + Label *restart_label; TextureRect *restart_icon; PanelContainer *restart_container; @@ -94,7 +96,7 @@ class ProjectSettingsEditor : public AcceptDialog { void _setting_edited(const String &p_name); void _setting_selected(const String &p_path); void _add_setting(); - void _delete_setting(); + void _delete_setting(bool p_confirmed); void _editor_restart_request(); void _editor_restart(); diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp index c6c93fae83..27b11e4fb5 100644 --- a/editor/property_selector.cpp +++ b/editor/property_selector.cpp @@ -84,6 +84,9 @@ void PropertySelector::_update_search() { TreeItem *root = search_options->create_item(); + // Allow using spaces in place of underscores in the search string (makes the search more fault-tolerant). + const String search_text = search_box->get_text().replace(" ", "_"); + if (properties) { List<PropertyInfo> props; @@ -167,7 +170,7 @@ void PropertySelector::_update_search() { continue; } - if (search_box->get_text() != String() && E->get().name.find(search_box->get_text()) == -1) { + if (search_box->get_text() != String() && E->get().name.findn(search_text) == -1) { continue; } @@ -180,7 +183,7 @@ void PropertySelector::_update_search() { item->set_metadata(0, E->get().name); item->set_icon(0, type_icons[E->get().type]); - if (!found && search_box->get_text() != String() && E->get().name.find(search_box->get_text()) != -1) { + if (!found && search_box->get_text() != String() && E->get().name.findn(search_text) != -1) { item->select(0); found = true; } @@ -255,7 +258,7 @@ void PropertySelector::_update_search() { continue; } - if (search_box->get_text() != String() && name.find(search_box->get_text()) == -1) { + if (search_box->get_text() != String() && name.findn(search_text) == -1) { continue; } @@ -270,29 +273,29 @@ void PropertySelector::_update_search() { } else if (mi.return_val.type != Variant::NIL) { desc = Variant::get_type_name(mi.return_val.type); } else { - desc = "void "; + desc = "void"; } - desc += " " + mi.name + " ( "; + desc += vformat(" %s(", mi.name); for (int i = 0; i < mi.arguments.size(); i++) { if (i > 0) { desc += ", "; } + desc += mi.arguments[i].name; + if (mi.arguments[i].type == Variant::NIL) { - desc += "var "; + desc += ": Variant"; } else if (mi.arguments[i].name.find(":") != -1) { - desc += mi.arguments[i].name.get_slice(":", 1) + " "; + desc += vformat(": %s", mi.arguments[i].name.get_slice(":", 1)); mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0); } else { - desc += Variant::get_type_name(mi.arguments[i].type) + " "; + desc += vformat(": %s", Variant::get_type_name(mi.arguments[i].type)); } - - desc += mi.arguments[i].name; } - desc += " )"; + desc += ")"; if (E->get().flags & METHOD_FLAG_CONST) { desc += " const"; @@ -306,7 +309,7 @@ void PropertySelector::_update_search() { item->set_metadata(0, name); item->set_selectable(0, true); - if (!found && search_box->get_text() != String() && name.find(search_box->get_text()) != -1) { + if (!found && search_box->get_text() != String() && name.findn(search_text) != -1) { item->select(0); found = true; } diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 7404c9779b..a62448169d 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -258,27 +258,35 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { int num_connections = p_node->get_persistent_signal_connection_count(); int num_groups = p_node->get_persistent_group_count(); + String msg_temp; + if (num_connections >= 1) { + Array arr; + arr.push_back(num_connections); + msg_temp += TTRN("Node has one connection.", "Node has {num} connections.", num_connections).format(arr, "{num}"); + msg_temp += " "; + } + if (num_groups >= 1) { + Array arr; + arr.push_back(num_groups); + msg_temp += TTRN("Node is in one group.", "Node is in {num} groups.", num_groups).format(arr, "{num}"); + } + if (num_connections >= 1 || num_groups >= 1) { + msg_temp += "\n" + TTR("Click to show signals dock."); + } + + Ref<Texture2D> icon_temp; + auto signal_temp = BUTTON_SIGNALS; if (num_connections >= 1 && num_groups >= 1) { - item->add_button( - 0, - get_theme_icon("SignalsAndGroups", "EditorIcons"), - BUTTON_SIGNALS, - false, - vformat(TTR("Node has %s connection(s) and %s group(s).\nClick to show signals dock."), num_connections, num_groups)); + icon_temp = get_theme_icon("SignalsAndGroups", "EditorIcons"); } else if (num_connections >= 1) { - item->add_button( - 0, - get_theme_icon("Signals", "EditorIcons"), - BUTTON_SIGNALS, - false, - vformat(TTR("Node has %s connection(s).\nClick to show signals dock."), num_connections)); + icon_temp = get_theme_icon("Signals", "EditorIcons"); } else if (num_groups >= 1) { - item->add_button( - 0, - get_theme_icon("Groups", "EditorIcons"), - BUTTON_GROUPS, - false, - vformat(TTR("Node is in %s group(s).\nClick to show groups dock."), num_groups)); + icon_temp = get_theme_icon("Groups", "EditorIcons"); + signal_temp = BUTTON_GROUPS; + } + + if (num_connections >= 1 || num_groups >= 1) { + item->add_button(0, icon_temp, signal_temp, false, msg_temp); } } diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 628475bbc0..90efb11b7d 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -722,6 +722,15 @@ void ScriptCreateDialog::_update_dialog() { } get_ok()->set_disabled(!script_ok); + + Callable entered_call = callable_mp(this, &ScriptCreateDialog::_path_entered); + if (script_ok) { + if (!file_path->is_connected("text_entered", entered_call)) { + file_path->connect("text_entered", entered_call); + } + } else if (file_path->is_connected("text_entered", entered_call)) { + file_path->disconnect("text_entered", entered_call); + } } void ScriptCreateDialog::_bind_methods() { @@ -849,7 +858,6 @@ ScriptCreateDialog::ScriptCreateDialog() { hb->connect("sort_children", callable_mp(this, &ScriptCreateDialog::_path_hbox_sorted)); file_path = memnew(LineEdit); file_path->connect("text_changed", callable_mp(this, &ScriptCreateDialog::_path_changed)); - file_path->connect("text_entered", callable_mp(this, &ScriptCreateDialog::_path_entered)); file_path->set_h_size_flags(Control::SIZE_EXPAND_FILL); hb->add_child(file_path); path_button = memnew(Button); diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index 9f286bd8f6..35610ef71b 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -405,6 +405,7 @@ EditorSettingsDialog::EditorSettingsDialog() { tab_general->add_child(hbc); search_box = memnew(LineEdit); + search_box->set_placeholder(TTR("Search")); search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); hbc->add_child(search_box); @@ -449,6 +450,7 @@ EditorSettingsDialog::EditorSettingsDialog() { tab_shortcuts->add_child(hbc); shortcut_search_box = memnew(LineEdit); + shortcut_search_box->set_placeholder(TTR("Search")); shortcut_search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); hbc->add_child(shortcut_search_box); shortcut_search_box->connect("text_changed", callable_mp(this, &EditorSettingsDialog::_filter_shortcuts)); diff --git a/editor/translations/extract.py b/editor/translations/extract.py index 749bad5fff..02ed65131f 100755 --- a/editor/translations/extract.py +++ b/editor/translations/extract.py @@ -33,6 +33,7 @@ matches.sort() unique_str = [] unique_loc = {} +ctx_group = {} # Store msgctx, msg, and locations. main_po = """ # LANGUAGE translation of the Godot Engine editor. # Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. @@ -52,6 +53,34 @@ msgstr "" """ +def _write_message(msgctx, msg, msg_plural, location): + global main_po + main_po += "\n#: " + location + "\n" + if msgctx != "": + main_po += 'msgctxt "' + msgctx + '"\n' + main_po += 'msgid "' + msg + '"\n' + if msg_plural != "": + main_po += 'msgid_plural "' + msg_plural + '"\n' + main_po += 'msgstr[0] ""\n' + main_po += 'msgstr[1] ""\n' + else: + main_po += 'msgstr ""\n' + + +def _add_additional_location(msgctx, msg, location): + global main_po + # Add additional location to previous occurrence + msg_pos = -1 + if msgctx != "": + msg_pos = main_po.find('\nmsgctxt "' + msgctx + '"\nmsgid "' + msg + '"') + else: + msg_pos = main_po.find('\nmsgid "' + msg + '"') + + if msg_pos == -1: + print("Someone apparently thought writing Python was as easy as GDScript. Ping Akien.") + main_po = main_po[:msg_pos] + " " + location + main_po[msg_pos:] + + def process_file(f, fname): global main_po, unique_str, unique_loc @@ -60,10 +89,11 @@ def process_file(f, fname): lc = 1 while l: - patterns = ['RTR("', 'TTR("', 'TTRC("'] + patterns = ['RTR("', 'TTR("', 'TTRC("', 'TTRN("', 'RTRN("'] idx = 0 pos = 0 while pos >= 0: + # Loop until a pattern is found. If not, next line. pos = l.find(patterns[idx], pos) if pos == -1: if idx < len(patterns) - 1: @@ -72,29 +102,64 @@ def process_file(f, fname): continue pos += len(patterns[idx]) + # Read msg until " msg = "" while pos < len(l) and (l[pos] != '"' or l[pos - 1] == "\\"): msg += l[pos] pos += 1 + # Read plural. + msg_plural = "" + if patterns[idx] in ['TTRN("', 'RTRN("']: + pos = l.find('"', pos + 1) + pos += 1 + while pos < len(l) and (l[pos] != '"' or l[pos - 1] == "\\"): + msg_plural += l[pos] + pos += 1 + + # Read context. + msgctx = "" + pos += 1 + read_ctx = False + while pos < len(l): + if l[pos] == ")": + break + elif l[pos] == '"': + read_ctx = True + break + pos += 1 + + pos += 1 + if read_ctx: + while pos < len(l) and (l[pos] != '"' or l[pos - 1] == "\\"): + msgctx += l[pos] + pos += 1 + + # File location. location = os.path.relpath(fname).replace("\\", "/") if line_nb: location += ":" + str(lc) - if not msg in unique_str: - main_po += "\n#: " + location + "\n" - main_po += 'msgid "' + msg + '"\n' - main_po += 'msgstr ""\n' - unique_str.append(msg) - unique_loc[msg] = [location] - elif not location in unique_loc[msg]: - # Add additional location to previous occurrence too - msg_pos = main_po.find('\nmsgid "' + msg + '"') - if msg_pos == -1: - print("Someone apparently thought writing Python was as easy as GDScript. Ping Akien.") - main_po = main_po[:msg_pos] + " " + location + main_po[msg_pos:] - unique_loc[msg].append(location) - + if msgctx != "": + # If it's a new context or a new message within an existing context, then write new msgid. + # Else add location to existing msgid. + if not msgctx in ctx_group: + _write_message(msgctx, msg, msg_plural, location) + ctx_group[msgctx] = {msg: [location]} + elif not msg in ctx_group[msgctx]: + _write_message(msgctx, msg, msg_plural, location) + ctx_group[msgctx][msg] = [location] + elif not location in ctx_group[msgctx][msg]: + _add_additional_location(msgctx, msg, location) + ctx_group[msgctx][msg].append(location) + else: + if not msg in unique_str: + _write_message(msgctx, msg, msg_plural, location) + unique_str.append(msg) + unique_loc[msg] = [location] + elif not location in unique_loc[msg]: + _add_additional_location(msgctx, msg, location) + unique_loc[msg].append(location) l = f.readline() lc += 1 @@ -102,7 +167,7 @@ def process_file(f, fname): print("Updating the editor.pot template...") for fname in matches: - with open(fname, "r") as f: + with open(fname, "r", encoding="utf8") as f: process_file(f, fname) with open("editor.pot", "w") as f: |