diff options
26 files changed, 821 insertions, 739 deletions
@@ -1,25 +1,29 @@ -[![Godot Engine logo](/logo.png)](https://godotengine.org) +# Godot Engine -## Godot Engine +<p align="center"> + <a href="https://godotengine.org"> + <img src="logo.svg" width="400" alt="Godot Engine logo"> + </a> +</p> -Homepage: https://godotengine.org +## 2D and 3D cross-platform game engine -#### 2D and 3D cross-platform game engine +**[Godot Engine](https://godotengine.org) is a feature-packed, cross-platform +game engine to create 2D and 3D games from a unified interface.** It provides a +comprehensive set of common tools, so that users can focus on making games +without having to reinvent the wheel. Games can be exported in one click to a +number of platforms, including the major desktop platforms (Linux, macOS, +Windows), mobile platforms (Android, iOS), as well as Web-based platforms +(HTML5) and +[consoles](https://docs.godotengine.org/en/latest/tutorials/platform/consoles.html). -Godot Engine is a feature-packed, cross-platform game engine to create 2D and -3D games from a unified interface. It provides a comprehensive set of common -tools, so that users can focus on making games without having to reinvent the -wheel. Games can be exported in one click to a number of platforms, including -the major desktop platforms (Linux, Mac OSX, Windows) as well as mobile -(Android, iOS) and web-based (HTML5) platforms. - -#### Free, open source and community-driven +## Free, open source and community-driven Godot is completely free and open source under the very permissive MIT license. No strings attached, no royalties, nothing. The users' games are theirs, down to the last line of engine code. Godot's development is fully independent and community-driven, empowering users to help shape their engine to match their -expectations. It is supported by the Software Freedom Conservancy +expectations. It is supported by the [Software Freedom Conservancy](https://sfconservancy.org/) not-for-profit. Before being open sourced in February 2014, Godot had been developed by Juan @@ -28,43 +32,45 @@ years as an in-house engine, used to publish several work-for-hire titles. ![Screenshot of a 3D scene in Godot Engine](https://raw.githubusercontent.com/godotengine/godot-design/master/screenshots/editor_tps_demo_1920x1080.jpg) -### Getting the engine +## Getting the engine -#### Binary downloads +### Binary downloads Official binaries for the Godot editor and the export templates can be found [on the homepage](https://godotengine.org/download). -#### Compiling from source +### Compiling from source [See the official docs](https://docs.godotengine.org/en/latest/development/compiling/) for compilation instructions for every supported platform. -### Community and contributing +## Community and contributing Godot is not only an engine but an ever-growing community of users and engine developers. The main community channels are listed [on the homepage](https://godotengine.org/community). -To get in touch with the developers, the best way is to join the -[#godotengine IRC channel](https://webchat.freenode.net/?channels=godotengine) +To get in touch with the engine developers, the best way is to join the +[#godotengine-devel IRC channel](https://webchat.freenode.net/?channels=godotengine-devel) on Freenode. To get started contributing to the project, see the [contributing guide](CONTRIBUTING.md). -### Documentation and demos +## Documentation and demos The official documentation is hosted on [ReadTheDocs](https://docs.godotengine.org). It is maintained by the Godot community in its own [GitHub repository](https://github.com/godotengine/godot-docs). The [class reference](https://docs.godotengine.org/en/latest/classes/) -is also accessible from within the engine. +is also accessible from the Godot editor. The official demos are maintained in their own [GitHub repository](https://github.com/godotengine/godot-demo-projects) as well. -There are also a number of other learning resources provided by the community, -such as text and video tutorials, demos, etc. Consult the [community channels](https://godotengine.org/community) -for more info. +There are also a number of other +[learning resources](https://docs.godotengine.org/en/latest/community/tutorials.html) +provided by the community, such as text and video tutorials, demos, etc. +Consult the [community channels](https://godotengine.org/community) +for more information. [![Actions Build Status](https://github.com/godotengine/godot/workflows/Godot/badge.svg?branch=master)](https://github.com/godotengine/godot/actions) [![Code Triagers Badge](https://www.codetriage.com/godotengine/godot/badges/users.svg)](https://www.codetriage.com/godotengine/godot) diff --git a/core/project_settings.cpp b/core/project_settings.cpp index e08a44d0c1..960a0ec1b0 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -659,6 +659,12 @@ void ProjectSettings::set_builtin_order(const String &p_name) { } } +bool ProjectSettings::is_builtin_setting(const String &p_name) const { + // Return true because a false negative is worse than a false positive. + ERR_FAIL_COND_V_MSG(!props.has(p_name), true, "Request for nonexistent project setting: " + p_name + "."); + return props[p_name].order < NO_BUILTIN_ORDER_BASE; +} + void ProjectSettings::clear(const String &p_name) { ERR_FAIL_COND_MSG(!props.has(p_name), "Request for nonexistent project setting: " + p_name + "."); props.erase(p_name); diff --git a/core/project_settings.h b/core/project_settings.h index 659ee402b3..686f6f3873 100644 --- a/core/project_settings.h +++ b/core/project_settings.h @@ -142,6 +142,7 @@ public: int get_order(const String &p_name) const; void set_order(const String &p_name, int p_order); void set_builtin_order(const String &p_name); + bool is_builtin_setting(const String &p_name) const; Error setup(const String &p_path, const String &p_main_pack, bool p_upwards = false); diff --git a/core/variant_call.cpp b/core/variant_call.cpp index b96fd0c103..91af127d32 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -2350,9 +2350,9 @@ void register_variant_methods() { _VariantCall::add_variant_constant(Variant::VECTOR3, "FORWARD", Vector3(0, 0, -1)); _VariantCall::add_variant_constant(Variant::VECTOR3, "BACK", Vector3(0, 0, 1)); - _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_X", Vector3::AXIS_X); - _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Y", Vector3::AXIS_Y); - _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Z", Vector3::AXIS_Z); + _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_X", Vector3i::AXIS_X); + _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Y", Vector3i::AXIS_Y); + _VariantCall::add_constant(Variant::VECTOR3I, "AXIS_Z", Vector3i::AXIS_Z); _VariantCall::add_variant_constant(Variant::VECTOR3I, "ZERO", Vector3i(0, 0, 0)); _VariantCall::add_variant_constant(Variant::VECTOR3I, "ONE", Vector3i(1, 1, 1)); @@ -2366,8 +2366,8 @@ void register_variant_methods() { _VariantCall::add_constant(Variant::VECTOR2, "AXIS_X", Vector2::AXIS_X); _VariantCall::add_constant(Variant::VECTOR2, "AXIS_Y", Vector2::AXIS_Y); - _VariantCall::add_constant(Variant::VECTOR2I, "AXIS_X", Vector2::AXIS_X); - _VariantCall::add_constant(Variant::VECTOR2I, "AXIS_Y", Vector2::AXIS_Y); + _VariantCall::add_constant(Variant::VECTOR2I, "AXIS_X", Vector2i::AXIS_X); + _VariantCall::add_constant(Variant::VECTOR2I, "AXIS_Y", Vector2i::AXIS_Y); _VariantCall::add_variant_constant(Variant::VECTOR2, "ZERO", Vector2(0, 0)); _VariantCall::add_variant_constant(Variant::VECTOR2, "ONE", Vector2(1, 1)); diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index b7240655af..d4abac15c0 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -430,6 +430,9 @@ <member name="v_scroll_speed" type="float" setter="set_v_scroll_speed" getter="get_v_scroll_speed" default="80.0"> Vertical scroll sensitivity. </member> + <member name="virtual_keyboard_enabled" type="bool" setter="set_virtual_keyboard_enabled" getter="is_virtual_keyboard_enabled" default="true"> + If [code]true[/code], the native virtual keyboard is shown when focused on platforms that support it. + </member> <member name="wrap_enabled" type="bool" setter="set_wrap_enabled" getter="is_wrap_enabled" default="false"> If [code]true[/code], enables text wrapping when it goes beyond the edge of what is visible. </member> diff --git a/editor/doc_data.cpp b/editor/doc_data.cpp index 75b16b4510..791b49319a 100644 --- a/editor/doc_data.cpp +++ b/editor/doc_data.cpp @@ -316,8 +316,7 @@ void DocData::generate(bool p_basic_types) { if (name == "ProjectSettings") { // Special case for project settings, so that settings are not taken from the current project's settings - if (E->get().name == "script" || - ProjectSettings::get_singleton()->get_order(E->get().name) >= ProjectSettings::NO_BUILTIN_ORDER_BASE) { + if (E->get().name == "script" || !ProjectSettings::get_singleton()->is_builtin_setting(E->get().name)) { continue; } if (E->get().usage & PROPERTY_USAGE_EDITOR) { diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index a93763810b..8d54bc8021 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -85,7 +85,8 @@ static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1, return style; } -Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float p_scale = EDSCALE, bool p_force_filter = false) { +#ifdef MODULE_SVG_ENABLED +static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float p_scale = EDSCALE, bool p_force_filter = false) { Ref<ImageTexture> icon = memnew(ImageTexture); Ref<Image> img = memnew(Image); @@ -99,6 +100,7 @@ Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float return icon; } +#endif #ifndef ADD_CONVERT_COLOR #define ADD_CONVERT_COLOR(dictionary, old_color, new_color) dictionary[Color::html(old_color)] = Color::html(new_color) diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index a68742a985..35311b32eb 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -475,10 +475,12 @@ private: set_message(TTR("Couldn't create project.godot in project path."), MESSAGE_ERROR); } else { f->store_line("[gd_resource type=\"Environment\" load_steps=2 format=2]"); + f->store_line(""); f->store_line("[sub_resource type=\"Sky\" id=1]"); + f->store_line(""); f->store_line("[resource]"); f->store_line("background_mode = 2"); - f->store_line("background_sky = SubResource( 1 )"); + f->store_line("sky = SubResource( 1 )"); memdelete(f); } } diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 0257e31ee7..9be1a7c2fe 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -30,8 +30,6 @@ #include "project_settings_editor.h" -#include "core/global_constants.h" -#include "core/os/keyboard.h" #include "core/project_settings.h" #include "editor/editor_export.h" #include "editor/editor_node.h" @@ -48,175 +46,157 @@ void ProjectSettingsEditor::popup_project_settings() { popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8); } - globals_editor->update_category_list(); + _add_feature_overrides(); + inspector->update_category_list(); + localization_editor->update_translations(); autoload_settings->update_autoload(); plugin_settings->update_plugins(); - set_process_unhandled_input(true); } -void ProjectSettingsEditor::_unhandled_input(const Ref<InputEvent> &p_event) { - const Ref<InputEventKey> k = p_event; - - if (k.is_valid() && k->is_pressed()) { - if (k->get_keycode_with_modifiers() == (KEY_MASK_CMD | KEY_F)) { - if (search_button->is_pressed()) { - search_box->grab_focus(); - search_box->select_all(); - } else { - // This toggles the search bar display while giving the button its "pressed" appearance - search_button->set_pressed(true); - } - - set_input_as_handled(); - } - } +void ProjectSettingsEditor::queue_save() { + timer->start(); } -void ProjectSettingsEditor::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_VISIBILITY_CHANGED: { - if (!is_visible()) { - EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "project_settings", Rect2(get_position(), get_size())); - set_process_unhandled_input(false); - } - } break; - case NOTIFICATION_ENTER_TREE: { - globals_editor->edit(ProjectSettings::get_singleton()); - - search_button->set_icon(get_theme_icon("Search", "EditorIcons")); - search_box->set_right_icon(get_theme_icon("Search", "EditorIcons")); - search_box->set_clear_button_enabled(true); - - restart_close_button->set_icon(get_theme_icon("Close", "EditorIcons")); - restart_container->add_theme_style_override("panel", get_theme_stylebox("bg", "Tree")); - restart_icon->set_texture(get_theme_icon("StatusWarning", "EditorIcons")); - restart_label->add_theme_color_override("font_color", get_theme_color("warning_color", "Editor")); - - } break; - case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - search_button->set_icon(get_theme_icon("Search", "EditorIcons")); - search_box->set_right_icon(get_theme_icon("Search", "EditorIcons")); - search_box->set_clear_button_enabled(true); - } break; - } +void ProjectSettingsEditor::set_plugins_page() { + tab_container->set_current_tab(plugin_settings->get_index()); } void ProjectSettingsEditor::update_plugins() { plugin_settings->update_plugins(); } -void ProjectSettingsEditor::_item_selected(const String &p_path) { - const String &selected_path = p_path; - if (selected_path == String()) { - return; +void ProjectSettingsEditor::_setting_edited(const String &p_name) { + queue_save(); +} + +void ProjectSettingsEditor::_advanced_pressed() { + if (advanced->is_pressed()) { + _update_advanced_bar(); + advanced_bar->show(); + } else { + advanced_bar->hide(); } - category->set_text(globals_editor->get_current_section()); - property->set_text(selected_path); - popup_copy_to_feature->set_disabled(false); } -void ProjectSettingsEditor::_item_adds(String) { - _item_add(); +void ProjectSettingsEditor::_setting_selected(const String &p_path) { + if (p_path == String()) { + return; + } + + category_box->set_text(inspector->get_current_section()); + property_box->set_text(p_path); + + if (advanced_bar->is_visible()) { + _update_advanced_bar(); // set_text doesn't trigger text_changed + } } -void ProjectSettingsEditor::_item_add() { +void ProjectSettingsEditor::_add_setting() { + String setting = _get_setting_name(); + // Initialize the property with the default value for the given type. // The type list starts at 1 (as we exclude Nil), so add 1 to the selected value. Callable::CallError ce; const Variant value = Variant::construct(Variant::Type(type->get_selected() + 1), nullptr, 0, ce); - String catname = category->get_text().strip_edges(); - String propname = property->get_text().strip_edges(); + undo_redo->create_action(TTR("Add Project Setting")); + undo_redo->add_do_property(ps, setting, value); + undo_redo->add_undo_property(ps, setting, ps->has_setting(setting) ? ps->get(setting) : Variant()); - if (propname.empty()) { - return; - } + undo_redo->add_do_method(inspector, "update_category_list"); + undo_redo->add_undo_method(inspector, "update_category_list"); + undo_redo->add_do_method(this, "queue_save"); + undo_redo->add_undo_method(this, "queue_save"); + undo_redo->commit_action(); - if (catname.empty()) { - catname = "global"; - } + inspector->set_current_section(setting.get_slice("/", 1)); +} - String name = catname + "/" + propname; +void ProjectSettingsEditor::_delete_setting() { + String setting = _get_setting_name(); + Variant value = ps->get(setting); + int order = ps->get_order(setting); - undo_redo->create_action(TTR("Add Global Property")); + undo_redo->create_action(TTR("Delete Item")); - undo_redo->add_do_property(ProjectSettings::get_singleton(), name, value); + undo_redo->add_do_method(ps, "clear", setting); + undo_redo->add_undo_method(ps, "set", setting, value); + undo_redo->add_undo_method(ps, "set_order", setting, order); - if (ProjectSettings::get_singleton()->has_setting(name)) { - undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, ProjectSettings::get_singleton()->get(name)); - } else { - undo_redo->add_undo_property(ProjectSettings::get_singleton(), name, Variant()); - } + undo_redo->add_do_method(inspector, "update_category_list"); + undo_redo->add_undo_method(inspector, "update_category_list"); + undo_redo->add_do_method(this, "queue_save"); + undo_redo->add_undo_method(this, "queue_save"); - undo_redo->add_do_method(globals_editor, "update_category_list"); - undo_redo->add_undo_method(globals_editor, "update_category_list"); - undo_redo->add_do_method(this, "_settings_changed"); - undo_redo->add_undo_method(this, "_settings_changed"); undo_redo->commit_action(); - globals_editor->set_current_section(catname); - - _settings_changed(); + property_box->clear(); } -void ProjectSettingsEditor::_item_del() { - String path = globals_editor->get_inspector()->get_selected_path(); - if (path == String()) { - EditorNode::get_singleton()->show_warning(TTR("Select a setting item first!")); - return; - } +void ProjectSettingsEditor::_text_field_changed(const String &p_text) { + _update_advanced_bar(); +} - String property = globals_editor->get_current_section().plus_file(path); +void ProjectSettingsEditor::_feature_selected(int p_index) { + _update_advanced_bar(); +} - if (!ProjectSettings::get_singleton()->has_setting(property)) { - EditorNode::get_singleton()->show_warning(vformat(TTR("No property '%s' exists."), property)); - return; - } +void ProjectSettingsEditor::_update_advanced_bar() { + const String property_text = property_box->get_text().strip_edges(); - if (ProjectSettings::get_singleton()->get_order(property) < ProjectSettings::NO_BUILTIN_ORDER_BASE) { - EditorNode::get_singleton()->show_warning(vformat(TTR("Setting '%s' is internal, and it can't be deleted."), property)); - return; - } - - undo_redo->create_action(TTR("Delete Item")); + String error_msg = ""; + bool disable_add = true; + bool disable_del = true; - Variant value = ProjectSettings::get_singleton()->get(property); - int order = ProjectSettings::get_singleton()->get_order(property); + if (!property_box->get_text().empty()) { + const String setting = _get_setting_name(); + bool setting_exists = ps->has_setting(setting); + if (setting_exists) { + error_msg = TTR(" - Cannot add already existing setting."); - undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", property); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", property, value); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", property, order); + disable_del = ps->is_builtin_setting(setting); + if (disable_del) { + String msg = TTR(" - Cannot delete built-in setting."); + error_msg += (error_msg == "") ? msg : "\n" + msg; + } + } else { + bool bad_category = false; // Allow empty string. + Vector<String> cats = category_box->get_text().strip_edges().split("/"); + for (int i = 0; i < cats.size(); i++) { + if (!cats[i].is_valid_identifier()) { + bad_category = true; + error_msg = TTR(" - Invalid category name."); + break; + } + } - undo_redo->add_do_method(globals_editor, "update_category_list"); - undo_redo->add_undo_method(globals_editor, "update_category_list"); + disable_add = !bad_category; - undo_redo->add_do_method(this, "_settings_changed"); - undo_redo->add_undo_method(this, "_settings_changed"); + if (!property_text.is_valid_identifier()) { + disable_add = true; + String msg = TTR(" - Invalid property name."); + error_msg += (error_msg == "") ? msg : "\n" + msg; + } + } + } - undo_redo->commit_action(); -} + add_button->set_disabled(disable_add); + del_button->set_disabled(disable_del); -void ProjectSettingsEditor::_save() { - Error err = ProjectSettings::get_singleton()->save(); - message->set_text(err != OK ? TTR("Error saving settings.") : TTR("Settings saved OK.")); - message->popup_centered(Size2(300, 100) * EDSCALE); + error_label->set_text(error_msg); + error_label->set_visible(error_msg != ""); } -void ProjectSettingsEditor::_settings_prop_edited(const String &p_name) { - // Method needed to discard the mandatory argument of the property_edited signal - _settings_changed(); -} +String ProjectSettingsEditor::_get_setting_name() const { + const String cat = category_box->get_text(); + const String name = (cat.empty() ? "global" : cat.strip_edges()).plus_file(property_box->get_text().strip_edges()); + const String feature = feature_override->get_item_text(feature_override->get_selected()); -void ProjectSettingsEditor::_settings_changed() { - timer->start(); + return (feature == "") ? name : (name + "." + feature); } -void ProjectSettingsEditor::queue_save() { - _settings_changed(); -} - -void ProjectSettingsEditor::_copy_to_platform_about_to_show() { +void ProjectSettingsEditor::_add_feature_overrides() { Set<String> presets; presets.insert("bptc"); @@ -230,25 +210,26 @@ void ProjectSettingsEditor::_copy_to_platform_about_to_show() { presets.insert("standalone"); presets.insert("32"); presets.insert("64"); - // Not available as an export platform yet, so it needs to be added manually - presets.insert("Server"); + presets.insert("Server"); // Not available as an export platform yet, so it needs to be added manually - for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) { + EditorExport *ee = EditorExport::get_singleton(); + + for (int i = 0; i < ee->get_export_platform_count(); i++) { List<String> p; - EditorExport::get_singleton()->get_export_platform(i)->get_platform_features(&p); + ee->get_export_platform(i)->get_platform_features(&p); for (List<String>::Element *E = p.front(); E; E = E->next()) { presets.insert(E->get()); } } - for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { + for (int i = 0; i < ee->get_export_preset_count(); i++) { List<String> p; - EditorExport::get_singleton()->get_export_preset(i)->get_platform()->get_preset_features(EditorExport::get_singleton()->get_export_preset(i), &p); + ee->get_export_preset(i)->get_platform()->get_preset_features(ee->get_export_preset(i), &p); for (List<String>::Element *E = p.front(); E; E = E->next()) { presets.insert(E->get()); } - String custom = EditorExport::get_singleton()->get_export_preset(i)->get_custom_features(); + String custom = ee->get_export_preset(i)->get_custom_features(); Vector<String> custom_list = custom.split(","); for (int j = 0; j < custom_list.size(); j++) { String f = custom_list[j].strip_edges(); @@ -258,70 +239,14 @@ void ProjectSettingsEditor::_copy_to_platform_about_to_show() { } } - popup_copy_to_feature->get_popup()->clear(); - int id = 0; + feature_override->clear(); + feature_override->add_item("", 0); // So it is always on top. + int id = 1; for (Set<String>::Element *E = presets.front(); E; E = E->next()) { - popup_copy_to_feature->get_popup()->add_item(E->get(), id++); + feature_override->add_item(E->get(), id++); } } -void ProjectSettingsEditor::_copy_to_platform(int p_which) { - String path = globals_editor->get_inspector()->get_selected_path(); - if (path == String()) { - EditorNode::get_singleton()->show_warning(TTR("Select a setting item first!")); - return; - } - - String property = globals_editor->get_current_section().plus_file(path); - - undo_redo->create_action(TTR("Override for Feature")); - - Variant value = ProjectSettings::get_singleton()->get(property); - if (property.find(".") != -1) { //overwriting overwrite, keep overwrite - undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", property); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", property, value); - } - - String feature = popup_copy_to_feature->get_popup()->get_item_text(p_which); - String new_path = property + "." + feature; - - undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", new_path, value); - if (ProjectSettings::get_singleton()->has_setting(new_path)) { - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", new_path, ProjectSettings::get_singleton()->get(new_path)); - } - - undo_redo->add_do_method(globals_editor, "update_category_list"); - undo_redo->add_undo_method(globals_editor, "update_category_list"); - - undo_redo->add_do_method(this, "_settings_changed"); - undo_redo->add_undo_method(this, "_settings_changed"); - - undo_redo->commit_action(); -} - -void ProjectSettingsEditor::_toggle_search_bar(bool p_pressed) { - globals_editor->get_inspector()->set_use_filter(p_pressed); - - if (p_pressed) { - search_bar->show(); - add_prop_bar->hide(); - search_box->grab_focus(); - search_box->select_all(); - } else { - search_box->clear(); - search_bar->hide(); - add_prop_bar->show(); - } -} - -void ProjectSettingsEditor::set_plugins_page() { - tab_container->set_current_tab(plugin_settings->get_index()); -} - -TabContainer *ProjectSettingsEditor::get_tabs() { - return tab_container; -} - void ProjectSettingsEditor::_editor_restart() { EditorNode::get_singleton()->save_all_scenes(); EditorNode::get_singleton()->restart_editor(); @@ -335,17 +260,48 @@ void ProjectSettingsEditor::_editor_restart_close() { restart_container->hide(); } -void ProjectSettingsEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_unhandled_input"), &ProjectSettingsEditor::_unhandled_input); - ClassDB::bind_method(D_METHOD("_save"), &ProjectSettingsEditor::_save); +void ProjectSettingsEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_VISIBILITY_CHANGED: { + if (!is_visible()) { + EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "project_settings", Rect2(get_position(), get_size())); + if (advanced->is_pressed()) { + advanced->set_pressed(false); + advanced_bar->hide(); + } + } + } break; + case NOTIFICATION_ENTER_TREE: { + inspector->edit(ps); + + error_label->add_theme_color_override("font_color", error_label->get_theme_color("error_color", "Editor")); + add_button->set_icon(get_theme_icon("Add", "EditorIcons")); + del_button->set_icon(get_theme_icon("Remove", "EditorIcons")); + + search_box->set_right_icon(get_theme_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); + + restart_close_button->set_icon(get_theme_icon("Close", "EditorIcons")); + restart_container->add_theme_style_override("panel", get_theme_stylebox("bg", "Tree")); + restart_icon->set_texture(get_theme_icon("StatusWarning", "EditorIcons")); + restart_label->add_theme_color_override("font_color", get_theme_color("warning_color", "Editor")); + } break; + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + search_box->set_right_icon(get_theme_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); + } break; + } +} - ClassDB::bind_method(D_METHOD("get_tabs"), &ProjectSettingsEditor::get_tabs); +void ProjectSettingsEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("queue_save"), &ProjectSettingsEditor::queue_save); } ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { singleton = this; set_title(TTR("Project Settings (project.godot)")); + ps = ProjectSettings::get_singleton(); undo_redo = &p_data->get_undo_redo(); data = p_data; @@ -354,103 +310,110 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { tab_container->set_use_hidden_tabs_for_min_size(true); add_child(tab_container); - VBoxContainer *props_base = memnew(VBoxContainer); - props_base->set_name(TTR("General")); - props_base->set_alignment(BoxContainer::ALIGN_BEGIN); - props_base->set_v_size_flags(Control::SIZE_EXPAND_FILL); - tab_container->add_child(props_base); - - HBoxContainer *hbc = memnew(HBoxContainer); - hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); - props_base->add_child(hbc); - - search_button = memnew(Button); - search_button->set_text(TTR("Search")); - search_button->set_toggle_mode(true); - search_button->set_pressed(false); - search_button->connect("toggled", callable_mp(this, &ProjectSettingsEditor::_toggle_search_bar)); - hbc->add_child(search_button); - - hbc->add_child(memnew(VSeparator)); - - add_prop_bar = memnew(HBoxContainer); - add_prop_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL); - hbc->add_child(add_prop_bar); - - Label *l = memnew(Label); - l->set_text(TTR("Category:")); - add_prop_bar->add_child(l); - - category = memnew(LineEdit); - category->set_h_size_flags(Control::SIZE_EXPAND_FILL); - category->connect("text_entered", callable_mp(this, &ProjectSettingsEditor::_item_adds)); - add_prop_bar->add_child(category); - - l = memnew(Label); - l->set_text(TTR("Property:")); - add_prop_bar->add_child(l); - - property = memnew(LineEdit); - property->set_h_size_flags(Control::SIZE_EXPAND_FILL); - property->connect("text_entered", callable_mp(this, &ProjectSettingsEditor::_item_adds)); - add_prop_bar->add_child(property); - - l = memnew(Label); - l->set_text(TTR("Type:")); - add_prop_bar->add_child(l); - - type = memnew(OptionButton); - type->set_h_size_flags(Control::SIZE_EXPAND_FILL); - add_prop_bar->add_child(type); - - // Start at 1 to avoid adding "Nil" as an option - for (int i = 1; i < Variant::VARIANT_MAX; i++) { - type->add_item(Variant::get_type_name(Variant::Type(i))); + VBoxContainer *general_editor = memnew(VBoxContainer); + general_editor->set_name(TTR("General")); + general_editor->set_alignment(BoxContainer::ALIGN_BEGIN); + general_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); + tab_container->add_child(general_editor); + + VBoxContainer *header = memnew(VBoxContainer); + header->set_h_size_flags(Control::SIZE_EXPAND_FILL); + general_editor->add_child(header); + + { + // Search bar. + search_bar = memnew(HBoxContainer); + search_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL); + header->add_child(search_bar); + + search_box = memnew(LineEdit); + search_box->set_custom_minimum_size(Size2(300, 0)); + search_bar->add_child(search_box); + + search_bar->add_spacer(); + + advanced = memnew(CheckButton); + advanced->set_text(TTR("Advanced")); + advanced->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_advanced_pressed)); + search_bar->add_child(advanced); } - Button *add = memnew(Button); - add->set_text(TTR("Add")); - add->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_item_add)); - add_prop_bar->add_child(add); + { + // Advanced bar. + advanced_bar = memnew(VBoxContainer); + advanced_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL); + advanced_bar->hide(); + header->add_child(advanced_bar); + + HBoxContainer *hbc = memnew(HBoxContainer); + hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); + advanced_bar->add_margin_child(TTR("Add or remove custom project settings."), hbc, true); + + category_box = memnew(LineEdit); + category_box->set_custom_minimum_size(Size2(140, 0) * EDSCALE); + category_box->connect("text_changed", callable_mp(this, &ProjectSettingsEditor::_text_field_changed)); + category_box->set_placeholder(TTR("Category")); + hbc->add_child(category_box); + + Label *l = memnew(Label); + l->set_text("/"); + hbc->add_child(l); + + property_box = memnew(LineEdit); + property_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); + property_box->set_placeholder(TTR("Property")); + property_box->connect("text_changed", callable_mp(this, &ProjectSettingsEditor::_text_field_changed)); + hbc->add_child(property_box); + + l = memnew(Label); + l->set_text(TTR("Type:")); + hbc->add_child(l); + + type = memnew(OptionButton); + type->set_custom_minimum_size(Size2(70, 0) * EDSCALE); + hbc->add_child(type); + + // Start at 1 to avoid adding "Nil" as an option + for (int i = 1; i < Variant::VARIANT_MAX; i++) { + type->add_item(Variant::get_type_name(Variant::Type(i))); + } - search_bar = memnew(HBoxContainer); - search_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL); - search_bar->hide(); - hbc->add_child(search_bar); + l = memnew(Label); + l->set_text(TTR("Feature Override:")); + hbc->add_child(l); - search_box = memnew(LineEdit); - search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); - search_bar->add_child(search_box); + feature_override = memnew(OptionButton); + feature_override->set_custom_minimum_size(Size2(70, 0) * EDSCALE); + feature_override->connect("item_selected", callable_mp(this, &ProjectSettingsEditor::_feature_selected)); + hbc->add_child(feature_override); - globals_editor = memnew(SectionedInspector); - globals_editor->get_inspector()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo()); - globals_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); - globals_editor->register_search_box(search_box); - globals_editor->get_inspector()->connect("property_selected", callable_mp(this, &ProjectSettingsEditor::_item_selected)); - globals_editor->get_inspector()->connect("property_edited", callable_mp(this, &ProjectSettingsEditor::_settings_prop_edited)); - globals_editor->get_inspector()->connect("restart_requested", callable_mp(this, &ProjectSettingsEditor::_editor_restart_request)); - props_base->add_child(globals_editor); + hbc->add_spacer(); - Button *del = memnew(Button); - del->set_text(TTR("Delete")); - del->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_item_del)); - hbc->add_child(del); + add_button = memnew(Button); + add_button->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_add_setting)); + hbc->add_child(add_button); - add_prop_bar->add_child(memnew(VSeparator)); + del_button = memnew(Button); + del_button->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_delete_setting)); + hbc->add_child(del_button); - popup_copy_to_feature = memnew(MenuButton); - popup_copy_to_feature->set_text(TTR("Override For...")); - popup_copy_to_feature->set_disabled(true); - add_prop_bar->add_child(popup_copy_to_feature); + error_label = memnew(Label); + advanced_bar->add_child(error_label); + } - popup_copy_to_feature->get_popup()->connect("id_pressed", callable_mp(this, &ProjectSettingsEditor::_copy_to_platform)); - popup_copy_to_feature->get_popup()->connect("about_to_popup", callable_mp(this, &ProjectSettingsEditor::_copy_to_platform_about_to_show)); + header->add_child(memnew(HSeparator)); - get_ok()->set_text(TTR("Close")); - set_hide_on_ok(true); + inspector = memnew(SectionedInspector); + inspector->get_inspector()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo()); + inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL); + inspector->register_search_box(search_box); + inspector->get_inspector()->connect("property_selected", callable_mp(this, &ProjectSettingsEditor::_setting_selected)); + inspector->get_inspector()->connect("property_edited", callable_mp(this, &ProjectSettingsEditor::_setting_edited)); + inspector->get_inspector()->connect("restart_requested", callable_mp(this, &ProjectSettingsEditor::_editor_restart_request)); + general_editor->add_child(inspector); restart_container = memnew(PanelContainer); - props_base->add_child(restart_container); + general_editor->add_child(restart_container); HBoxContainer *restart_hb = memnew(HBoxContainer); restart_container->hide(); @@ -475,27 +438,24 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { restart_close_button->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_editor_restart_close)); restart_hb->add_child(restart_close_button); - message = memnew(AcceptDialog); - add_child(message); - inputmap_editor = memnew(InputMapEditor); inputmap_editor->set_name(TTR("Input Map")); - inputmap_editor->connect("inputmap_changed", callable_mp(this, &ProjectSettingsEditor::_settings_changed)); + inputmap_editor->connect("inputmap_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); tab_container->add_child(inputmap_editor); localization_editor = memnew(LocalizationEditor); localization_editor->set_name(TTR("Localization")); - localization_editor->connect("localization_changed", callable_mp(this, &ProjectSettingsEditor::_settings_changed)); + localization_editor->connect("localization_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); tab_container->add_child(localization_editor); autoload_settings = memnew(EditorAutoloadSettings); autoload_settings->set_name(TTR("AutoLoad")); - autoload_settings->connect("autoload_changed", callable_mp(this, &ProjectSettingsEditor::_settings_changed)); + autoload_settings->connect("autoload_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); tab_container->add_child(autoload_settings); shaders_global_variables_editor = memnew(ShaderGlobalsEditor); shaders_global_variables_editor->set_name(TTR("Shader Globals")); - shaders_global_variables_editor->connect("globals_changed", callable_mp(this, &ProjectSettingsEditor::_settings_changed)); + shaders_global_variables_editor->connect("globals_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); tab_container->add_child(shaders_global_variables_editor); plugin_settings = memnew(EditorPluginSettings); @@ -504,7 +464,10 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { timer = memnew(Timer); timer->set_wait_time(1.5); - timer->connect("timeout", callable_mp(ProjectSettings::get_singleton(), &ProjectSettings::save)); + timer->connect("timeout", callable_mp(ps, &ProjectSettings::save)); timer->set_one_shot(true); add_child(timer); + + 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 c99c2fe9a2..0d7e19b242 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -35,12 +35,11 @@ #include "editor/editor_data.h" #include "editor/editor_plugin_settings.h" #include "editor/editor_sectioned_inspector.h" +#include "editor/input_map_editor.h" +#include "editor/localization_editor.h" +#include "editor/shader_globals_editor.h" #include "editor_autoload_settings.h" -#include "input_map_editor.h" -#include "localization_editor.h" -#include "scene/gui/dialogs.h" #include "scene/gui/tab_container.h" -#include "shader_globals_editor.h" class ProjectSettingsEditor : public AcceptDialog { GDCLASS(ProjectSettingsEditor, AcceptDialog); @@ -53,28 +52,31 @@ class ProjectSettingsEditor : public AcceptDialog { INPUT_MOUSE_BUTTON }; - TabContainer *tab_container; - AcceptDialog *message; + static ProjectSettingsEditor *singleton; + ProjectSettings *ps; Timer *timer; - HBoxContainer *search_bar; - Button *search_button; - LineEdit *search_box; - HBoxContainer *add_prop_bar; - LineEdit *category; - LineEdit *property; - OptionButton *type; - - SectionedInspector *globals_editor; - - MenuButton *popup_copy_to_feature; - + TabContainer *tab_container; + SectionedInspector *inspector; InputMapEditor *inputmap_editor; LocalizationEditor *localization_editor; EditorAutoloadSettings *autoload_settings; ShaderGlobalsEditor *shaders_global_variables_editor; EditorPluginSettings *plugin_settings; + HBoxContainer *search_bar; + LineEdit *search_box; + CheckButton *advanced; + + VBoxContainer *advanced_bar; + LineEdit *category_box; + LineEdit *property_box; + Button *add_button; + Button *del_button; + OptionButton *type; + OptionButton *feature_override; + Label *error_label; + Label *restart_label; TextureRect *restart_icon; PanelContainer *restart_container; @@ -83,30 +85,25 @@ class ProjectSettingsEditor : public AcceptDialog { EditorData *data; UndoRedo *undo_redo; - void _item_selected(const String &); - void _item_adds(String); - void _item_add(); - void _item_del(); - void _save(); - - void _settings_prop_edited(const String &p_name); - void _settings_changed(); + void _advanced_pressed(); + void _update_advanced_bar(); + void _text_field_changed(const String &p_text); + void _feature_selected(int p_index); - void _copy_to_platform(int p_which); - void _copy_to_platform_about_to_show(); - - void _toggle_search_bar(bool p_pressed); - - ProjectSettingsEditor(); - - static ProjectSettingsEditor *singleton; + String _get_setting_name() const; + void _setting_edited(const String &p_name); + void _setting_selected(const String &p_path); + void _add_setting(); + void _delete_setting(); void _editor_restart_request(); void _editor_restart(); void _editor_restart_close(); + void _add_feature_overrides(); + ProjectSettingsEditor(); + protected: - void _unhandled_input(const Ref<InputEvent> &p_event); void _notification(int p_what); static void _bind_methods(); @@ -117,8 +114,7 @@ public: void update_plugins(); EditorAutoloadSettings *get_autoload_settings() { return autoload_settings; } - - TabContainer *get_tabs(); + TabContainer *get_tabs() { return tab_container; } void queue_save(); diff --git a/main/main.cpp b/main/main.cpp index 25e9092285..75f204aa7e 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -372,7 +372,7 @@ void Main::print_help(const char *p_binary) { int Main::test_entrypoint(int argc, char *argv[], bool &tests_need_run) { #ifdef TESTS_ENABLED for (int x = 0; x < argc; x++) { - if (strncmp(argv[x], "--test", 6) == 0) { + if ((strncmp(argv[x], "--test", 6) == 0) && (strlen(argv[x]) == 6)) { tests_need_run = true; OS::get_singleton()->initialize(); StringName::setup(); diff --git a/misc/dist/linux/x-godot-project.xml b/misc/dist/linux/x-godot-project.xml index 0572e4e54e..9f28bab2ae 100644 --- a/misc/dist/linux/x-godot-project.xml +++ b/misc/dist/linux/x-godot-project.xml @@ -1,8 +1,8 @@ <?xml version="1.0"?> - <mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> - <mime-type type="application/x-godot-project"> - <comment>Godot Engine project</comment> - <icon name="godot" /> - <glob pattern="*.godot" weight="100" /> +<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info"> + <mime-type type="application/x-godot-project"> + <comment>Godot Engine project</comment> + <icon name="godot" /> + <glob pattern="*.godot" weight="100" /> </mime-type> </mime-info> diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 82a47f594b..8f2ebc7232 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -506,6 +506,12 @@ void CSGShape3D::_notification(int p_what) { _make_dirty(); } + if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { + if (use_collision && is_root_shape() && root_collision_instance.is_valid()) { + PhysicsServer3D::get_singleton()->body_set_state(root_collision_instance, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform()); + } + } + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { if (parent) { parent->_make_dirty(); @@ -641,7 +647,7 @@ CSGShape3D::~CSGShape3D() { ////////////////////////////////// CSGBrush *CSGCombiner3D::_build_brush() { - return nullptr; //does not build anything + return memnew(CSGBrush); //does not build anything } CSGCombiner3D::CSGCombiner3D() { diff --git a/platform/android/logo.png b/platform/android/logo.png Binary files differindex df445f6a9c..f44d360a25 100644 --- a/platform/android/logo.png +++ b/platform/android/logo.png diff --git a/platform/iphone/godot_view_renderer.mm b/platform/iphone/godot_view_renderer.mm index 1fc822b457..045fb451f0 100644 --- a/platform/iphone/godot_view_renderer.mm +++ b/platform/iphone/godot_view_renderer.mm @@ -44,7 +44,6 @@ @interface GodotViewRenderer () -@property(assign, nonatomic) BOOL hasFinishedLocaleSetup; @property(assign, nonatomic) BOOL hasFinishedProjectDataSetup; @property(assign, nonatomic) BOOL hasStartedMain; @property(assign, nonatomic) BOOL hasFinishedSetup; @@ -58,9 +57,8 @@ return NO; } - if (!self.hasFinishedLocaleSetup) { - [self setupLocaleAndUUID]; - return YES; + if (!OS::get_singleton()) { + exit(0); } if (!self.hasFinishedProjectDataSetup) { @@ -79,33 +77,6 @@ return NO; } -- (void)setupLocaleAndUUID { - self.hasFinishedLocaleSetup = YES; - - if (!OS::get_singleton()) { - exit(0); - } - - NSString *locale_code = [[NSLocale currentLocale] localeIdentifier]; - OSIPhone::get_singleton()->set_locale(String::utf8([locale_code UTF8String])); - - NSString *uuid; - if ([[UIDevice currentDevice] respondsToSelector:@selector(identifierForVendor)]) { - uuid = [UIDevice currentDevice].identifierForVendor.UUIDString; - } else { - // before iOS 6, so just generate an identifier and store it - uuid = [[NSUserDefaults standardUserDefaults] objectForKey:@"identiferForVendor"]; - if (!uuid) { - CFUUIDRef cfuuid = CFUUIDCreate(NULL); - uuid = [(NSString *)CFUUIDCreateString(NULL, cfuuid) autorelease]; - CFRelease(cfuuid); - [[NSUserDefaults standardUserDefaults] setObject:uuid forKey:@"identifierForVendor"]; - } - } - - OSIPhone::get_singleton()->set_unique_id(String::utf8([uuid UTF8String])); -} - - (void)setupProjectData { self.hasFinishedProjectDataSetup = YES; diff --git a/platform/iphone/logo.png b/platform/iphone/logo.png Binary files differindex 405b6f93ca..966d8aa70a 100644 --- a/platform/iphone/logo.png +++ b/platform/iphone/logo.png diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index f3bde46717..c6f95869ee 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -84,8 +84,6 @@ private: virtual void finalize() override; String user_data_dir; - String unique_id; - String locale_code; bool is_focused = false; @@ -118,10 +116,8 @@ public: void set_user_data_dir(String p_dir); virtual String get_user_data_dir() const override; - void set_locale(String p_locale); virtual String get_locale() const override; - void set_unique_id(String p_id); virtual String get_unique_id() const override; virtual void vibrate_handheld(int p_duration_ms = 500) override; diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm index f0bbbd39ca..a7366ffdec 100644 --- a/platform/iphone/os_iphone.mm +++ b/platform/iphone/os_iphone.mm @@ -305,20 +305,20 @@ String OSIPhone::get_user_data_dir() const { return user_data_dir; } -void OSIPhone::set_locale(String p_locale) { - locale_code = p_locale; -} - String OSIPhone::get_locale() const { - return locale_code; -} + NSString *preferedLanguage = [NSLocale preferredLanguages].firstObject; + + if (preferedLanguage) { + return String::utf8([preferedLanguage UTF8String]).replace("-", "_"); + } -void OSIPhone::set_unique_id(String p_id) { - unique_id = p_id; + NSString *localeIdentifier = [[NSLocale currentLocale] localeIdentifier]; + return String::utf8([localeIdentifier UTF8String]).replace("-", "_"); } String OSIPhone::get_unique_id() const { - return unique_id; + NSString *uuid = [UIDevice currentDevice].identifierForVendor.UUIDString; + return String::utf8([uuid UTF8String]); } void OSIPhone::vibrate_handheld(int p_duration_ms) { diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index ae45e0734d..0cf02ef69b 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -95,8 +95,8 @@ protected: virtual void get_export_options(List<ExportOption> *r_options) override; public: - virtual String get_name() const override { return "Mac OSX"; } - virtual String get_os_name() const override { return "OSX"; } + virtual String get_name() const override { return "macOS"; } + virtual String get_os_name() const override { return "macOS"; } virtual Ref<Texture2D> get_logo() const override { return logo; } virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { diff --git a/platform/osx/logo.png b/platform/osx/logo.png Binary files differindex 834bbf3ba6..b5a660b165 100644 --- a/platform/osx/logo.png +++ b/platform/osx/logo.png diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index b974dc2897..e17085cafc 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1631,7 +1631,7 @@ void TextEdit::_notification(int p_what) { DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor_pos, get_viewport()->get_window_id()); } - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD)) { + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), true); } } break; @@ -1647,7 +1647,7 @@ void TextEdit::_notification(int p_what) { ime_text = ""; ime_selection = Point2(); - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD)) { + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { DisplayServer::get_singleton()->virtual_keyboard_hide(); } } break; @@ -6693,6 +6693,10 @@ void TextEdit::set_shortcut_keys_enabled(bool p_enabled) { _generate_context_menu(); } +void TextEdit::set_virtual_keyboard_enabled(bool p_enable) { + virtual_keyboard_enabled = p_enable; +} + void TextEdit::set_selecting_enabled(bool p_enabled) { selecting_enabled = p_enabled; @@ -6711,6 +6715,10 @@ bool TextEdit::is_shortcut_keys_enabled() const { return shortcut_keys_enabled; } +bool TextEdit::is_virtual_keyboard_enabled() const { + return virtual_keyboard_enabled; +} + PopupMenu *TextEdit::get_menu() const { return menu; } @@ -6763,6 +6771,8 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &TextEdit::is_context_menu_enabled); ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enable"), &TextEdit::set_shortcut_keys_enabled); ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &TextEdit::is_shortcut_keys_enabled); + ClassDB::bind_method(D_METHOD("set_virtual_keyboard_enabled", "enable"), &TextEdit::set_virtual_keyboard_enabled); + ClassDB::bind_method(D_METHOD("is_virtual_keyboard_enabled"), &TextEdit::is_virtual_keyboard_enabled); ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &TextEdit::set_selecting_enabled); ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &TextEdit::is_selecting_enabled); @@ -6854,6 +6864,7 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_scrolling"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed"); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 5a6ed99845..a6bc9963cc 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -386,6 +386,8 @@ private: bool context_menu_enabled; bool shortcut_keys_enabled; + bool virtual_keyboard_enabled = true; + int executing_line; void _generate_context_menu(); @@ -749,6 +751,9 @@ public: void set_shortcut_keys_enabled(bool p_enabled); bool is_shortcut_keys_enabled() const; + void set_virtual_keyboard_enabled(bool p_enable); + bool is_virtual_keyboard_enabled() const; + PopupMenu *get_menu() const; String get_text_for_completion(); diff --git a/tests/test_basis.cpp b/tests/test_basis.cpp deleted file mode 100644 index 5904fc386a..0000000000 --- a/tests/test_basis.cpp +++ /dev/null @@ -1,325 +0,0 @@ -/*************************************************************************/ -/* test_basis.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 "test_basis.h" - -#include "core/math/random_number_generator.h" -#include "core/os/os.h" -#include "core/ustring.h" - -namespace TestBasis { - -enum RotOrder { - EulerXYZ, - EulerXZY, - EulerYZX, - EulerYXZ, - EulerZXY, - EulerZYX -}; - -Vector3 deg2rad(const Vector3 &p_rotation) { - return p_rotation / 180.0 * Math_PI; -} - -Vector3 rad2deg(const Vector3 &p_rotation) { - return p_rotation / Math_PI * 180.0; -} - -Basis EulerToBasis(RotOrder mode, const Vector3 &p_rotation) { - Basis ret; - switch (mode) { - case EulerXYZ: - ret.set_euler_xyz(p_rotation); - break; - - case EulerXZY: - ret.set_euler_xzy(p_rotation); - break; - - case EulerYZX: - ret.set_euler_yzx(p_rotation); - break; - - case EulerYXZ: - ret.set_euler_yxz(p_rotation); - break; - - case EulerZXY: - ret.set_euler_zxy(p_rotation); - break; - - case EulerZYX: - ret.set_euler_zyx(p_rotation); - break; - - default: - // If you land here, Please integrate all rotation orders. - CRASH_NOW_MSG("This is not unreachable."); - } - - return ret; -} - -Vector3 BasisToEuler(RotOrder mode, const Basis &p_rotation) { - switch (mode) { - case EulerXYZ: - return p_rotation.get_euler_xyz(); - - case EulerXZY: - return p_rotation.get_euler_xzy(); - - case EulerYZX: - return p_rotation.get_euler_yzx(); - - case EulerYXZ: - return p_rotation.get_euler_yxz(); - - case EulerZXY: - return p_rotation.get_euler_zxy(); - - case EulerZYX: - return p_rotation.get_euler_zyx(); - - default: - // If you land here, Please integrate all rotation orders. - CRASH_NOW_MSG("This is not unreachable."); - return Vector3(); - } -} - -String get_rot_order_name(RotOrder ro) { - switch (ro) { - case EulerXYZ: - return "XYZ"; - case EulerXZY: - return "XZY"; - case EulerYZX: - return "YZX"; - case EulerYXZ: - return "YXZ"; - case EulerZXY: - return "ZXY"; - case EulerZYX: - return "ZYX"; - default: - return "[Not supported]"; - } -} - -bool test_rotation(Vector3 deg_original_euler, RotOrder rot_order) { - // This test: - // 1. Converts the rotation vector from deg to rad. - // 2. Converts euler to basis. - // 3. Converts the above basis back into euler. - // 4. Converts the above euler into basis again. - // 5. Compares the basis obtained in step 2 with the basis of step 4 - // - // The conversion "basis to euler", done in the step 3, may be different from - // the original euler, even if the final rotation are the same. - // This happens because there are more ways to represents the same rotation, - // both valid, using eulers. - // For this reason is necessary to convert that euler back to basis and finally - // compares it. - // - // In this way we can assert that both functions: basis to euler / euler to basis - // are correct. - - bool pass = true; - - // Euler to rotation - const Vector3 original_euler = deg2rad(deg_original_euler); - const Basis to_rotation = EulerToBasis(rot_order, original_euler); - - // Euler from rotation - const Vector3 euler_from_rotation = BasisToEuler(rot_order, to_rotation); - const Basis rotation_from_computed_euler = EulerToBasis(rot_order, euler_from_rotation); - - Basis res = to_rotation.inverse() * rotation_from_computed_euler; - - if ((res.get_axis(0) - Vector3(1.0, 0.0, 0.0)).length() > 0.1) { - OS::get_singleton()->print("Fail due to X %ls\n", String(res.get_axis(0)).c_str()); - pass = false; - } - if ((res.get_axis(1) - Vector3(0.0, 1.0, 0.0)).length() > 0.1) { - OS::get_singleton()->print("Fail due to Y %ls\n", String(res.get_axis(1)).c_str()); - pass = false; - } - if ((res.get_axis(2) - Vector3(0.0, 0.0, 1.0)).length() > 0.1) { - OS::get_singleton()->print("Fail due to Z %ls\n", String(res.get_axis(2)).c_str()); - pass = false; - } - - if (pass) { - // Double check `to_rotation` decomposing with XYZ rotation order. - const Vector3 euler_xyz_from_rotation = to_rotation.get_euler_xyz(); - Basis rotation_from_xyz_computed_euler; - rotation_from_xyz_computed_euler.set_euler_xyz(euler_xyz_from_rotation); - - res = to_rotation.inverse() * rotation_from_xyz_computed_euler; - - if ((res.get_axis(0) - Vector3(1.0, 0.0, 0.0)).length() > 0.1) { - OS::get_singleton()->print("Double check with XYZ rot order failed, due to X %ls\n", String(res.get_axis(0)).c_str()); - pass = false; - } - if ((res.get_axis(1) - Vector3(0.0, 1.0, 0.0)).length() > 0.1) { - OS::get_singleton()->print("Double check with XYZ rot order failed, due to Y %ls\n", String(res.get_axis(1)).c_str()); - pass = false; - } - if ((res.get_axis(2) - Vector3(0.0, 0.0, 1.0)).length() > 0.1) { - OS::get_singleton()->print("Double check with XYZ rot order failed, due to Z %ls\n", String(res.get_axis(2)).c_str()); - pass = false; - } - } - - if (pass == false) { - // Print phase only if not pass. - OS *os = OS::get_singleton(); - os->print("Rotation order: %ls\n.", get_rot_order_name(rot_order).c_str()); - os->print("Original Rotation: %ls\n", String(deg_original_euler).c_str()); - os->print("Quaternion to rotation order: %ls\n", String(rad2deg(euler_from_rotation)).c_str()); - } - - return pass; -} - -void test_euler_conversion() { - Vector<RotOrder> rotorder_to_test; - rotorder_to_test.push_back(EulerXYZ); - rotorder_to_test.push_back(EulerXZY); - rotorder_to_test.push_back(EulerYZX); - rotorder_to_test.push_back(EulerYXZ); - rotorder_to_test.push_back(EulerZXY); - rotorder_to_test.push_back(EulerZYX); - - Vector<Vector3> vectors_to_test; - - // Test the special cases. - vectors_to_test.push_back(Vector3(0.0, 0.0, 0.0)); - vectors_to_test.push_back(Vector3(0.5, 0.5, 0.5)); - vectors_to_test.push_back(Vector3(-0.5, -0.5, -0.5)); - vectors_to_test.push_back(Vector3(40.0, 40.0, 40.0)); - vectors_to_test.push_back(Vector3(-40.0, -40.0, -40.0)); - vectors_to_test.push_back(Vector3(0.0, 0.0, -90.0)); - vectors_to_test.push_back(Vector3(0.0, -90.0, 0.0)); - vectors_to_test.push_back(Vector3(-90.0, 0.0, 0.0)); - vectors_to_test.push_back(Vector3(0.0, 0.0, 90.0)); - vectors_to_test.push_back(Vector3(0.0, 90.0, 0.0)); - vectors_to_test.push_back(Vector3(90.0, 0.0, 0.0)); - vectors_to_test.push_back(Vector3(0.0, 0.0, -30.0)); - vectors_to_test.push_back(Vector3(0.0, -30.0, 0.0)); - vectors_to_test.push_back(Vector3(-30.0, 0.0, 0.0)); - vectors_to_test.push_back(Vector3(0.0, 0.0, 30.0)); - vectors_to_test.push_back(Vector3(0.0, 30.0, 0.0)); - vectors_to_test.push_back(Vector3(30.0, 0.0, 0.0)); - vectors_to_test.push_back(Vector3(0.5, 50.0, 20.0)); - vectors_to_test.push_back(Vector3(-0.5, -50.0, -20.0)); - vectors_to_test.push_back(Vector3(0.5, 0.0, 90.0)); - vectors_to_test.push_back(Vector3(0.5, 0.0, -90.0)); - vectors_to_test.push_back(Vector3(360.0, 360.0, 360.0)); - vectors_to_test.push_back(Vector3(-360.0, -360.0, -360.0)); - vectors_to_test.push_back(Vector3(-90.0, 60.0, -90.0)); - vectors_to_test.push_back(Vector3(90.0, 60.0, -90.0)); - vectors_to_test.push_back(Vector3(90.0, -60.0, -90.0)); - vectors_to_test.push_back(Vector3(-90.0, -60.0, -90.0)); - vectors_to_test.push_back(Vector3(-90.0, 60.0, 90.0)); - vectors_to_test.push_back(Vector3(90.0, 60.0, 90.0)); - vectors_to_test.push_back(Vector3(90.0, -60.0, 90.0)); - vectors_to_test.push_back(Vector3(-90.0, -60.0, 90.0)); - vectors_to_test.push_back(Vector3(60.0, 90.0, -40.0)); - vectors_to_test.push_back(Vector3(60.0, -90.0, -40.0)); - vectors_to_test.push_back(Vector3(-60.0, -90.0, -40.0)); - vectors_to_test.push_back(Vector3(-60.0, 90.0, 40.0)); - vectors_to_test.push_back(Vector3(60.0, 90.0, 40.0)); - vectors_to_test.push_back(Vector3(60.0, -90.0, 40.0)); - vectors_to_test.push_back(Vector3(-60.0, -90.0, 40.0)); - vectors_to_test.push_back(Vector3(-90.0, 90.0, -90.0)); - vectors_to_test.push_back(Vector3(90.0, 90.0, -90.0)); - vectors_to_test.push_back(Vector3(90.0, -90.0, -90.0)); - vectors_to_test.push_back(Vector3(-90.0, -90.0, -90.0)); - vectors_to_test.push_back(Vector3(-90.0, 90.0, 90.0)); - vectors_to_test.push_back(Vector3(90.0, 90.0, 90.0)); - vectors_to_test.push_back(Vector3(90.0, -90.0, 90.0)); - vectors_to_test.push_back(Vector3(20.0, 150.0, 30.0)); - vectors_to_test.push_back(Vector3(20.0, -150.0, 30.0)); - vectors_to_test.push_back(Vector3(-120.0, -150.0, 30.0)); - vectors_to_test.push_back(Vector3(-120.0, -150.0, -130.0)); - vectors_to_test.push_back(Vector3(120.0, -150.0, -130.0)); - vectors_to_test.push_back(Vector3(120.0, 150.0, -130.0)); - vectors_to_test.push_back(Vector3(120.0, 150.0, 130.0)); - - // Add 1000 random vectors with weirds numbers. - RandomNumberGenerator rng; - for (int _ = 0; _ < 1000; _ += 1) { - vectors_to_test.push_back(Vector3( - rng.randf_range(-1800, 1800), - rng.randf_range(-1800, 1800), - rng.randf_range(-1800, 1800))); - } - - bool success = true; - for (int h = 0; h < rotorder_to_test.size(); h += 1) { - int passed = 0; - int failed = 0; - for (int i = 0; i < vectors_to_test.size(); i += 1) { - if (test_rotation(vectors_to_test[i], rotorder_to_test[h])) { - //OS::get_singleton()->print("Success. \n\n"); - passed += 1; - } else { - OS::get_singleton()->print("FAILED FAILED FAILED. \n\n"); - OS::get_singleton()->print("------------>\n"); - OS::get_singleton()->print("------------>\n"); - failed += 1; - success = false; - } - } - - if (failed == 0) { - OS::get_singleton()->print("%i passed tests for rotation order: %ls.\n", passed, get_rot_order_name(rotorder_to_test[h]).c_str()); - } else { - OS::get_singleton()->print("%i FAILED tests for rotation order: %ls.\n", failed, get_rot_order_name(rotorder_to_test[h]).c_str()); - } - } - - if (success) { - OS::get_singleton()->print("Euler conversion checks passed.\n"); - } else { - OS::get_singleton()->print("Euler conversion checks FAILED.\n"); - } -} - -MainLoop *test() { - OS::get_singleton()->print("Start euler conversion checks.\n"); - test_euler_conversion(); - - return NULL; -} - -} // namespace TestBasis diff --git a/tests/test_basis.h b/tests/test_basis.h index 63297bd3b8..05efe33788 100644 --- a/tests/test_basis.h +++ b/tests/test_basis.h @@ -31,10 +31,258 @@ #ifndef TEST_BASIS_H #define TEST_BASIS_H -#include "core/os/main_loop.h" +#include "core/math/random_number_generator.h" +#include "core/os/os.h" +#include "core/ustring.h" + +#include "tests/test_macros.h" namespace TestBasis { -MainLoop *test(); + +enum RotOrder { + EulerXYZ, + EulerXZY, + EulerYZX, + EulerYXZ, + EulerZXY, + EulerZYX +}; + +Vector3 deg2rad(const Vector3 &p_rotation) { + return p_rotation / 180.0 * Math_PI; +} + +Vector3 rad2deg(const Vector3 &p_rotation) { + return p_rotation / Math_PI * 180.0; +} + +Basis EulerToBasis(RotOrder mode, const Vector3 &p_rotation) { + Basis ret; + switch (mode) { + case EulerXYZ: + ret.set_euler_xyz(p_rotation); + break; + + case EulerXZY: + ret.set_euler_xzy(p_rotation); + break; + + case EulerYZX: + ret.set_euler_yzx(p_rotation); + break; + + case EulerYXZ: + ret.set_euler_yxz(p_rotation); + break; + + case EulerZXY: + ret.set_euler_zxy(p_rotation); + break; + + case EulerZYX: + ret.set_euler_zyx(p_rotation); + break; + + default: + // If you land here, Please integrate all rotation orders. + FAIL("This is not unreachable."); + } + + return ret; +} + +Vector3 BasisToEuler(RotOrder mode, const Basis &p_rotation) { + switch (mode) { + case EulerXYZ: + return p_rotation.get_euler_xyz(); + + case EulerXZY: + return p_rotation.get_euler_xzy(); + + case EulerYZX: + return p_rotation.get_euler_yzx(); + + case EulerYXZ: + return p_rotation.get_euler_yxz(); + + case EulerZXY: + return p_rotation.get_euler_zxy(); + + case EulerZYX: + return p_rotation.get_euler_zyx(); + + default: + // If you land here, Please integrate all rotation orders. + FAIL("This is not unreachable."); + return Vector3(); + } } +String get_rot_order_name(RotOrder ro) { + switch (ro) { + case EulerXYZ: + return "XYZ"; + case EulerXZY: + return "XZY"; + case EulerYZX: + return "YZX"; + case EulerYXZ: + return "YXZ"; + case EulerZXY: + return "ZXY"; + case EulerZYX: + return "ZYX"; + default: + return "[Not supported]"; + } +} + +void test_rotation(Vector3 deg_original_euler, RotOrder rot_order) { + // This test: + // 1. Converts the rotation vector from deg to rad. + // 2. Converts euler to basis. + // 3. Converts the above basis back into euler. + // 4. Converts the above euler into basis again. + // 5. Compares the basis obtained in step 2 with the basis of step 4 + // + // The conversion "basis to euler", done in the step 3, may be different from + // the original euler, even if the final rotation are the same. + // This happens because there are more ways to represents the same rotation, + // both valid, using eulers. + // For this reason is necessary to convert that euler back to basis and finally + // compares it. + // + // In this way we can assert that both functions: basis to euler / euler to basis + // are correct. + + // Euler to rotation + const Vector3 original_euler = deg2rad(deg_original_euler); + const Basis to_rotation = EulerToBasis(rot_order, original_euler); + + // Euler from rotation + const Vector3 euler_from_rotation = BasisToEuler(rot_order, to_rotation); + const Basis rotation_from_computed_euler = EulerToBasis(rot_order, euler_from_rotation); + + Basis res = to_rotation.inverse() * rotation_from_computed_euler; + + CHECK_MESSAGE((res.get_axis(0) - Vector3(1.0, 0.0, 0.0)).length() <= 0.1, vformat("Fail due to X %s\n", String(res.get_axis(0))).utf8().ptr()); + CHECK_MESSAGE((res.get_axis(1) - Vector3(0.0, 1.0, 0.0)).length() <= 0.1, vformat("Fail due to Y %s\n", String(res.get_axis(1))).utf8().ptr()); + CHECK_MESSAGE((res.get_axis(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Fail due to Z %s\n", String(res.get_axis(2))).utf8().ptr()); + + // Double check `to_rotation` decomposing with XYZ rotation order. + const Vector3 euler_xyz_from_rotation = to_rotation.get_euler_xyz(); + Basis rotation_from_xyz_computed_euler; + rotation_from_xyz_computed_euler.set_euler_xyz(euler_xyz_from_rotation); + + res = to_rotation.inverse() * rotation_from_xyz_computed_euler; + + CHECK_MESSAGE((res.get_axis(0) - Vector3(1.0, 0.0, 0.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to X %s\n", String(res.get_axis(0))).utf8().ptr()); + CHECK_MESSAGE((res.get_axis(1) - Vector3(0.0, 1.0, 0.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to Y %s\n", String(res.get_axis(1))).utf8().ptr()); + CHECK_MESSAGE((res.get_axis(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to Z %s\n", String(res.get_axis(2))).utf8().ptr()); + + INFO(vformat("Rotation order: %s\n.", get_rot_order_name(rot_order)).utf8().ptr()); + INFO(vformat("Original Rotation: %s\n", String(deg_original_euler)).utf8().ptr()); + INFO(vformat("Quaternion to rotation order: %s\n", String(rad2deg(euler_from_rotation))).utf8().ptr()); +} + +TEST_CASE("[Basis] Euler conversions") { + Vector<RotOrder> rotorder_to_test; + rotorder_to_test.push_back(EulerXYZ); + rotorder_to_test.push_back(EulerXZY); + rotorder_to_test.push_back(EulerYZX); + rotorder_to_test.push_back(EulerYXZ); + rotorder_to_test.push_back(EulerZXY); + rotorder_to_test.push_back(EulerZYX); + + Vector<Vector3> vectors_to_test; + + // Test the special cases. + vectors_to_test.push_back(Vector3(0.0, 0.0, 0.0)); + vectors_to_test.push_back(Vector3(0.5, 0.5, 0.5)); + vectors_to_test.push_back(Vector3(-0.5, -0.5, -0.5)); + vectors_to_test.push_back(Vector3(40.0, 40.0, 40.0)); + vectors_to_test.push_back(Vector3(-40.0, -40.0, -40.0)); + vectors_to_test.push_back(Vector3(0.0, 0.0, -90.0)); + vectors_to_test.push_back(Vector3(0.0, -90.0, 0.0)); + vectors_to_test.push_back(Vector3(-90.0, 0.0, 0.0)); + vectors_to_test.push_back(Vector3(0.0, 0.0, 90.0)); + vectors_to_test.push_back(Vector3(0.0, 90.0, 0.0)); + vectors_to_test.push_back(Vector3(90.0, 0.0, 0.0)); + vectors_to_test.push_back(Vector3(0.0, 0.0, -30.0)); + vectors_to_test.push_back(Vector3(0.0, -30.0, 0.0)); + vectors_to_test.push_back(Vector3(-30.0, 0.0, 0.0)); + vectors_to_test.push_back(Vector3(0.0, 0.0, 30.0)); + vectors_to_test.push_back(Vector3(0.0, 30.0, 0.0)); + vectors_to_test.push_back(Vector3(30.0, 0.0, 0.0)); + vectors_to_test.push_back(Vector3(0.5, 50.0, 20.0)); + vectors_to_test.push_back(Vector3(-0.5, -50.0, -20.0)); + vectors_to_test.push_back(Vector3(0.5, 0.0, 90.0)); + vectors_to_test.push_back(Vector3(0.5, 0.0, -90.0)); + vectors_to_test.push_back(Vector3(360.0, 360.0, 360.0)); + vectors_to_test.push_back(Vector3(-360.0, -360.0, -360.0)); + vectors_to_test.push_back(Vector3(-90.0, 60.0, -90.0)); + vectors_to_test.push_back(Vector3(90.0, 60.0, -90.0)); + vectors_to_test.push_back(Vector3(90.0, -60.0, -90.0)); + vectors_to_test.push_back(Vector3(-90.0, -60.0, -90.0)); + vectors_to_test.push_back(Vector3(-90.0, 60.0, 90.0)); + vectors_to_test.push_back(Vector3(90.0, 60.0, 90.0)); + vectors_to_test.push_back(Vector3(90.0, -60.0, 90.0)); + vectors_to_test.push_back(Vector3(-90.0, -60.0, 90.0)); + vectors_to_test.push_back(Vector3(60.0, 90.0, -40.0)); + vectors_to_test.push_back(Vector3(60.0, -90.0, -40.0)); + vectors_to_test.push_back(Vector3(-60.0, -90.0, -40.0)); + vectors_to_test.push_back(Vector3(-60.0, 90.0, 40.0)); + vectors_to_test.push_back(Vector3(60.0, 90.0, 40.0)); + vectors_to_test.push_back(Vector3(60.0, -90.0, 40.0)); + vectors_to_test.push_back(Vector3(-60.0, -90.0, 40.0)); + vectors_to_test.push_back(Vector3(-90.0, 90.0, -90.0)); + vectors_to_test.push_back(Vector3(90.0, 90.0, -90.0)); + vectors_to_test.push_back(Vector3(90.0, -90.0, -90.0)); + vectors_to_test.push_back(Vector3(-90.0, -90.0, -90.0)); + vectors_to_test.push_back(Vector3(-90.0, 90.0, 90.0)); + vectors_to_test.push_back(Vector3(90.0, 90.0, 90.0)); + vectors_to_test.push_back(Vector3(90.0, -90.0, 90.0)); + vectors_to_test.push_back(Vector3(20.0, 150.0, 30.0)); + vectors_to_test.push_back(Vector3(20.0, -150.0, 30.0)); + vectors_to_test.push_back(Vector3(-120.0, -150.0, 30.0)); + vectors_to_test.push_back(Vector3(-120.0, -150.0, -130.0)); + vectors_to_test.push_back(Vector3(120.0, -150.0, -130.0)); + vectors_to_test.push_back(Vector3(120.0, 150.0, -130.0)); + vectors_to_test.push_back(Vector3(120.0, 150.0, 130.0)); + + for (int h = 0; h < rotorder_to_test.size(); h += 1) { + for (int i = 0; i < vectors_to_test.size(); i += 1) { + test_rotation(vectors_to_test[i], rotorder_to_test[h]); + } + } +} + +TEST_CASE("[Stress][Basis] Euler conversions") { + Vector<RotOrder> rotorder_to_test; + rotorder_to_test.push_back(EulerXYZ); + rotorder_to_test.push_back(EulerXZY); + rotorder_to_test.push_back(EulerYZX); + rotorder_to_test.push_back(EulerYXZ); + rotorder_to_test.push_back(EulerZXY); + rotorder_to_test.push_back(EulerZYX); + + Vector<Vector3> vectors_to_test; + // Add 1000 random vectors with weirds numbers. + RandomNumberGenerator rng; + for (int _ = 0; _ < 1000; _ += 1) { + vectors_to_test.push_back(Vector3( + rng.randf_range(-1800, 1800), + rng.randf_range(-1800, 1800), + rng.randf_range(-1800, 1800))); + } + + for (int h = 0; h < rotorder_to_test.size(); h += 1) { + for (int i = 0; i < vectors_to_test.size(); i += 1) { + test_rotation(vectors_to_test[i], rotorder_to_test[h]); + } + } +} + +} // namespace TestBasis + #endif diff --git a/tests/test_macros.h b/tests/test_macros.h index e4494ce11a..45ba8581dd 100644 --- a/tests/test_macros.h +++ b/tests/test_macros.h @@ -47,4 +47,61 @@ #define ERR_PRINT_OFF _print_error_enabled = false; #define ERR_PRINT_ON _print_error_enabled = true; +// Stringify all `Variant` compatible types for doctest output by default. +// https://github.com/onqtam/doctest/blob/master/doc/markdown/stringification.md + +#define DOCTEST_STRINGIFY_VARIANT(m_type) \ + template <> \ + struct doctest::StringMaker<m_type> { \ + static doctest::String convert(const m_type &p_val) { \ + const Variant val = p_val; \ + return val.get_construct_string().utf8().get_data(); \ + } \ + }; + +#define DOCTEST_STRINGIFY_VARIANT_POINTER(m_type) \ + template <> \ + struct doctest::StringMaker<m_type> { \ + static doctest::String convert(const m_type *p_val) { \ + const Variant val = p_val; \ + return val.get_construct_string().utf8().get_data(); \ + } \ + }; + +DOCTEST_STRINGIFY_VARIANT(Variant); +DOCTEST_STRINGIFY_VARIANT(::String); // Disambiguate from `doctest::String`. + +DOCTEST_STRINGIFY_VARIANT(Vector2); +DOCTEST_STRINGIFY_VARIANT(Vector2i); +DOCTEST_STRINGIFY_VARIANT(Rect2); +DOCTEST_STRINGIFY_VARIANT(Rect2i); +DOCTEST_STRINGIFY_VARIANT(Vector3); +DOCTEST_STRINGIFY_VARIANT(Vector3i); +DOCTEST_STRINGIFY_VARIANT(Transform2D); +DOCTEST_STRINGIFY_VARIANT(Plane); +DOCTEST_STRINGIFY_VARIANT(Quat); +DOCTEST_STRINGIFY_VARIANT(AABB); +DOCTEST_STRINGIFY_VARIANT(Basis); +DOCTEST_STRINGIFY_VARIANT(Transform); + +DOCTEST_STRINGIFY_VARIANT(::Color); // Disambiguate from `doctest::Color`. +DOCTEST_STRINGIFY_VARIANT(StringName); +DOCTEST_STRINGIFY_VARIANT(NodePath); +DOCTEST_STRINGIFY_VARIANT(RID); +DOCTEST_STRINGIFY_VARIANT_POINTER(Object); +DOCTEST_STRINGIFY_VARIANT(Callable); +DOCTEST_STRINGIFY_VARIANT(Signal); +DOCTEST_STRINGIFY_VARIANT(Dictionary); +DOCTEST_STRINGIFY_VARIANT(Array); + +DOCTEST_STRINGIFY_VARIANT(PackedByteArray); +DOCTEST_STRINGIFY_VARIANT(PackedInt32Array); +DOCTEST_STRINGIFY_VARIANT(PackedInt64Array); +DOCTEST_STRINGIFY_VARIANT(PackedFloat32Array); +DOCTEST_STRINGIFY_VARIANT(PackedFloat64Array); +DOCTEST_STRINGIFY_VARIANT(PackedStringArray); +DOCTEST_STRINGIFY_VARIANT(PackedVector2Array); +DOCTEST_STRINGIFY_VARIANT(PackedVector3Array); +DOCTEST_STRINGIFY_VARIANT(PackedColorArray); + #endif // TEST_MACROS_H diff --git a/tests/test_validate_testing.h b/tests/test_validate_testing.h index 24acdec96c..4bcc57d9c5 100644 --- a/tests/test_validate_testing.h +++ b/tests/test_validate_testing.h @@ -53,6 +53,141 @@ TEST_SUITE("Validate tests") { ERR_PRINT_ON; CHECK_MESSAGE(_print_error_enabled, "Error printing should be re-enabled."); } + TEST_CASE("Stringify Variant types") { + ClassDB::init(); // For objects. + + Variant var; + INFO(var); + + String string("Godot is finally here!"); + INFO(string); + + Vector2 vec2(0.5, 1.0); + INFO(vec2); + + Vector2i vec2i(1, 2); + INFO(vec2i); + + Rect2 rect2(0.5, 0.5, 100.5, 100.5); + INFO(rect2); + + Rect2i rect2i(0, 0, 100, 100); + INFO(rect2i); + + Vector3 vec3(0.5, 1.0, 2.0); + INFO(vec3); + + Vector3i vec3i(1, 2, 3); + INFO(vec3i); + + Transform2D trans2d(0.5, Vector2(100, 100)); + INFO(trans2d); + + Plane plane(Vector3(1, 1, 1), 1.0); + INFO(plane); + + Quat quat(Vector3(0.5, 1.0, 2.0)); + INFO(quat); + + AABB aabb(Vector3(), Vector3(100, 100, 100)); + INFO(aabb); + + Basis basis(quat); + INFO(basis); + + Transform trans(basis); + INFO(trans); + + Color color(1, 0.5, 0.2, 0.3); + INFO(color); + + StringName string_name("has_method"); + INFO(string_name); + + NodePath node_path("godot/sprite"); + INFO(node_path); + + INFO(RID()); + + Object *obj = memnew(Object); + INFO(obj); + + Callable callable(obj, "has_method"); + INFO(callable); + + Signal signal(obj, "script_changed"); + INFO(signal); + + memdelete(obj); + + Dictionary dict; + dict["string"] = string; + dict["color"] = color; + INFO(dict); + + Array arr; + arr.push_back(string); + arr.push_back(color); + INFO(arr); + + PackedByteArray byte_arr; + byte_arr.push_back(0); + byte_arr.push_back(1); + byte_arr.push_back(2); + INFO(byte_arr); + + PackedInt32Array int32_arr; + int32_arr.push_back(0); + int32_arr.push_back(1); + int32_arr.push_back(2); + INFO(int32_arr); + + PackedInt64Array int64_arr; + int64_arr.push_back(0); + int64_arr.push_back(1); + int64_arr.push_back(2); + INFO(int64_arr); + + PackedFloat32Array float32_arr; + float32_arr.push_back(0.5); + float32_arr.push_back(1.5); + float32_arr.push_back(2.5); + INFO(float32_arr); + + PackedFloat64Array float64_arr; + float64_arr.push_back(0.5); + float64_arr.push_back(1.5); + float64_arr.push_back(2.5); + INFO(float64_arr); + + PackedStringArray str_arr = string.split(" "); + INFO(str_arr); + + PackedVector2Array vec2_arr; + vec2_arr.push_back(Vector2(0, 0)); + vec2_arr.push_back(Vector2(1, 1)); + vec2_arr.push_back(Vector2(2, 2)); + INFO(vec2_arr); + + PackedVector3Array vec3_arr; + vec3_arr.push_back(Vector3(0, 0, 0)); + vec3_arr.push_back(Vector3(1, 1, 1)); + vec3_arr.push_back(Vector3(2, 2, 2)); + INFO(vec3_arr); + + PackedColorArray color_arr; + color_arr.push_back(Color(0, 0, 0)); + color_arr.push_back(Color(1, 1, 1)); + color_arr.push_back(Color(2, 2, 2)); + INFO(color_arr); + + INFO("doctest insertion operator << " + << var << " " << vec2 << " " << rect2 << " " << color); + + CHECK(true); // So all above prints. + + ClassDB::cleanup(); + } } #endif // TEST_VALIDATE_TESTING_H |