diff options
-rw-r--r-- | .github/CODEOWNERS | 1 | ||||
-rw-r--r-- | doc/classes/DisplayServer.xml | 12 | ||||
-rw-r--r-- | doc/classes/String.xml | 2 | ||||
-rw-r--r-- | editor/create_dialog.cpp | 753 | ||||
-rw-r--r-- | editor/create_dialog.h | 65 | ||||
-rw-r--r-- | editor/editor_file_dialog.cpp | 2 | ||||
-rw-r--r-- | editor/editor_file_system.cpp | 2 | ||||
-rw-r--r-- | editor/editor_fonts.cpp | 2 | ||||
-rw-r--r-- | editor/editor_node.cpp | 4 | ||||
-rw-r--r-- | editor/editor_run.cpp | 33 | ||||
-rw-r--r-- | editor/plugins/animation_player_editor_plugin.cpp | 30 | ||||
-rw-r--r-- | editor/project_manager.cpp | 7 | ||||
-rw-r--r-- | platform/linuxbsd/display_server_x11.cpp | 1 | ||||
-rw-r--r-- | platform/osx/display_server_osx.h | 3 | ||||
-rw-r--r-- | platform/osx/display_server_osx.mm | 466 | ||||
-rw-r--r-- | platform/osx/os_osx.mm | 8 | ||||
-rw-r--r-- | scene/main/window.cpp | 1 | ||||
-rw-r--r-- | scene/resources/default_theme/default_theme.cpp | 50 | ||||
-rw-r--r-- | servers/display_server.cpp | 1 | ||||
-rw-r--r-- | servers/display_server.h | 8 | ||||
-rw-r--r-- | servers/physics_2d/area_2d_sw.cpp | 28 | ||||
-rw-r--r-- | servers/physics_3d/area_3d_sw.cpp | 27 |
22 files changed, 736 insertions, 770 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3bbe47af2a..33ec541bc7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -35,6 +35,7 @@ doc_classes/* @godotengine/documentation /modules/mbedtls/ @godotengine/network /modules/mobile_vr/ @BastiaanOlij /modules/mono/ @neikeq +/modules/mono/glue/GodotSharp @aaronfranke /modules/opensimplex/ @JFonS /modules/regex/ @LeeZH /modules/upnp/ @godotengine/network diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 1fb1de2c12..d118cf8205 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -523,6 +523,15 @@ <description> </description> </method> + <method name="screen_get_max_scale" qualifiers="const"> + <return type="float"> + </return> + <description> + Return the greatest scale factor of all screens. + [b]Note:[/b] On macOS returned value is [code]2.0[/code] if there is at least one hiDPI (Retina) screen in the system, and [code]1.0[/code] in all other cases. + [b]Note:[/b] This method is implemented on macOS. + </description> + </method> <method name="screen_get_orientation" qualifiers="const"> <return type="int" enum="DisplayServer.ScreenOrientation"> </return> @@ -545,6 +554,9 @@ <argument index="0" name="screen" type="int" default="-1"> </argument> <description> + Return the scale factor of the specified screen by index. + [b]Note:[/b] On macOS returned value is [code]2.0[/code] for hiDPI (Retina) screen, and [code]1.0[/code] for all other cases. + [b]Note:[/b] This method is implemented on macOS. </description> </method> <method name="screen_get_size" qualifiers="const"> diff --git a/doc/classes/String.xml b/doc/classes/String.xml index b692051097..78168562f1 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -332,7 +332,7 @@ <return type="String"> </return> <description> - Changes the case of some letters. Replaces underscores with spaces, converts all letters to lowercase, then capitalizes first and every letter following the space character. For [code]capitalize camelCase mixed_with_underscores[/code], it will return [code]Capitalize Camelcase Mixed With Underscores[/code]. + Changes the case of some letters. Replaces underscores with spaces, adds spaces before in-word uppercase characters, converts all letters to lowercase, then capitalizes the first letter and every letter following a space character. For [code]capitalize camelCase mixed_with_underscores[/code], it will return [code]Capitalize Camel Case Mixed With Underscores[/code]. </description> </method> <method name="casecmp_to"> diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 310de9dd90..a1bc7a9522 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -32,60 +32,38 @@ #include "core/class_db.h" #include "core/os/keyboard.h" -#include "core/print_string.h" #include "editor_feature_profile.h" -#include "editor_help.h" #include "editor_node.h" #include "editor_scale.h" #include "editor_settings.h" -#include "scene/gui/box_container.h" void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode, const String &p_select_type) { - type_list.clear(); - ClassDB::get_class_list(&type_list); - ScriptServer::get_global_class_list(&type_list); - type_list.sort_custom<StringName::AlphCompare>(); - - recent->clear(); - - FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("create_recent." + base_type), FileAccess::READ); + _fill_type_list(); - if (f) { - TreeItem *root = recent->create_item(); + icon_fallback = search_options->has_theme_icon(base_type, "EditorIcons") ? base_type : "Object"; - String icon_fallback = search_options->has_theme_icon(base_type, "EditorIcons") ? base_type : "Object"; - - while (!f->eof_reached()) { - String l = f->get_line().strip_edges(); - String name = l.split(" ")[0]; - if ((ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) && !_is_class_disabled_by_feature_profile(name)) { - TreeItem *ti = recent->create_item(root); - ti->set_text(0, l); - ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(name, icon_fallback)); - } - } - - memdelete(f); + if (p_dont_clear) { + search_box->select_all(); + } else { + search_box->clear(); } - favorites->clear(); - - f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("favorites." + base_type), FileAccess::READ); - - favorite_list.clear(); - - if (f) { - while (!f->eof_reached()) { - String l = f->get_line().strip_edges(); + if (p_replace_mode) { + search_box->set_text(p_select_type); + } - if (l != String()) { - favorite_list.push_back(l); - } - } + search_box->grab_focus(); + _update_search(); - memdelete(f); + if (p_replace_mode) { + set_title(vformat(TTR("Change %s Type"), base_type)); + get_ok()->set_text(TTR("Change")); + } else { + set_title(vformat(TTR("Create New %s"), base_type)); + get_ok()->set_text(TTR("Create")); } + _load_favorites_and_history(); _save_and_update_favorite_list(); // Restore valid window bounds or pop up at default size. @@ -95,357 +73,263 @@ void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode, const St } else { popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8); } +} - if (p_dont_clear) { - search_box->select_all(); - } else { - search_box->clear(); - } +void CreateDialog::_fill_type_list() { + List<StringName> complete_type_list; + ClassDB::get_class_list(&complete_type_list); + ScriptServer::get_global_class_list(&complete_type_list); - search_box->grab_focus(); + EditorData &ed = EditorNode::get_editor_data(); - _update_search(); + for (List<StringName>::Element *I = complete_type_list.front(); I; I = I->next()) { + String type = I->get(); + if (!_should_hide_type(type)) { + type_list.push_back(type); - is_replace_mode = p_replace_mode; + if (!ed.get_custom_types().has(type)) { + continue; + } - if (p_replace_mode) { - select_type(p_select_type); - set_title(vformat(TTR("Change %s Type"), base_type)); - get_ok()->set_text(TTR("Change")); - } else { - set_title(vformat(TTR("Create New %s"), base_type)); - get_ok()->set_text(TTR("Create")); + const Vector<EditorData::CustomType> &ct = ed.get_custom_types()[type]; + for (int i = 0; i < ct.size(); i++) { + custom_type_parents[ct[i].name] = type; + custom_type_indices[ct[i].name] = i; + type_list.push_back(ct[i].name); + } + } } + type_list.sort_custom<StringName::AlphCompare>(); } -void CreateDialog::_text_changed(const String &p_newtext) { - _update_search(); +bool CreateDialog::_is_type_preferred(const String &p_type) const { + if (ClassDB::class_exists(p_type)) { + return ClassDB::is_parent_class(p_type, preferred_search_result_type); + } + + return EditorNode::get_editor_data().script_class_is_parent(p_type, preferred_search_result_type); } -void CreateDialog::_sbox_input(const Ref<InputEvent> &p_ie) { - Ref<InputEventKey> k = p_ie; - if (k.is_valid() && (k->get_keycode() == KEY_UP || - k->get_keycode() == KEY_DOWN || - k->get_keycode() == KEY_PAGEUP || - k->get_keycode() == KEY_PAGEDOWN)) { - search_options->call("_gui_input", k); - search_box->accept_event(); - } +bool CreateDialog::_is_class_disabled_by_feature_profile(const StringName &p_class) const { + Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile(); + + return !profile.is_null() && profile->is_class_disabled(p_class); } -void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p_types, TreeItem *p_root, TreeItem **to_select) { - if (p_types.has(p_type)) { - return; +bool CreateDialog::_should_hide_type(const String &p_type) const { + if (_is_class_disabled_by_feature_profile(p_type)) { + return true; } - bool cpp_type = ClassDB::class_exists(p_type); - EditorData &ed = EditorNode::get_editor_data(); + if (base_type == "Node" && p_type.begins_with("Editor")) { + return true; // Do not show editor nodes. + } if (p_type == base_type) { - return; + return true; // Root is already added. } - if (cpp_type) { + if (ClassDB::class_exists(p_type)) { + if (!ClassDB::can_instance(p_type)) { + return true; // Can't create abstract class. + } + if (!ClassDB::is_parent_class(p_type, base_type)) { - return; + return true; // Wrong inheritance. + } + + for (Set<StringName>::Element *E = type_blacklist.front(); E; E = E->next()) { + if (ClassDB::is_parent_class(p_type, E->get())) { + return true; // Parent type is blacklisted. + } } } else { - if (!search_loaded_scripts.has(p_type)) { - search_loaded_scripts[p_type] = ed.script_class_load_script(p_type); + if (!EditorNode::get_editor_data().script_class_is_parent(p_type, base_type)) { + return true; // Wrong inheritance. } - - if (!ScriptServer::is_global_class(p_type) || !ed.script_class_is_parent(p_type, base_type)) { - return; + if (!ScriptServer::is_global_class(p_type)) { + return true; } String script_path = ScriptServer::get_global_class_path(p_type); - if (script_path.find("res://addons/", 0) != -1) { + if (script_path.begins_with("res://addons/")) { if (!EditorNode::get_singleton()->is_addon_plugin_enabled(script_path.get_slicec('/', 3))) { - return; + return true; // Plugin is not enabled. } } } - String inherits = cpp_type ? ClassDB::get_parent_class(p_type) : ed.script_class_get_base(p_type); - - TreeItem *parent = p_root; + return false; +} - if (inherits.length()) { - if (!p_types.has(inherits)) { - add_type(inherits, p_types, p_root, to_select); - } +void CreateDialog::_update_search() { + search_options->clear(); + search_options_types.clear(); - if (p_types.has(inherits)) { - parent = p_types[inherits]; - } else if (ScriptServer::is_global_class(inherits)) { - return; - } - } + TreeItem *root = search_options->create_item(); + root->set_text(0, base_type); + root->set_icon(0, search_options->get_theme_icon(icon_fallback, "EditorIcons")); + search_options_types[base_type] = root; - bool can_instance = (cpp_type && ClassDB::can_instance(p_type)) || ScriptServer::is_global_class(p_type); + const String search_text = search_box->get_text(); + bool empty_search = search_text == ""; - TreeItem *item = search_options->create_item(parent); - if (cpp_type) { - item->set_text(0, p_type); - } else { - item->set_metadata(0, p_type); - item->set_text(0, p_type + " (" + ScriptServer::get_global_class_path(p_type).get_file() + ")"); + // Filter all candidate results. + Vector<String> candidates; + for (List<StringName>::Element *I = type_list.front(); I; I = I->next()) { + if (empty_search || search_text.is_subsequence_ofi(I->get())) { + candidates.push_back(I->get()); + } } - if (!can_instance) { - item->set_custom_color(0, search_options->get_theme_color("disabled_font_color", "Editor")); - item->set_selectable(0, false); - } else if (!(*to_select && (*to_select)->get_text(0) == search_box->get_text())) { - String search_term = search_box->get_text().to_lower(); - - // if the node name matches exactly as the search, the node should be selected. - // this also fixes when the user clicks on recent nodes. - if (p_type.to_lower() == search_term) { - *to_select = item; - } else { - bool current_type_prefered = _is_type_prefered(p_type); - bool selected_type_prefered = *to_select ? _is_type_prefered((*to_select)->get_text(0).split(" ")[0]) : false; - - bool is_subsequence_of_type = search_box->get_text().is_subsequence_ofi(p_type); - bool is_substring_of_type = p_type.to_lower().find(search_term) >= 0; - bool is_substring_of_selected = false; - bool is_subsequence_of_selected = false; - bool is_selected_equal = false; - - if (*to_select) { - String name = (*to_select)->get_text(0).split(" ")[0].to_lower(); - is_substring_of_selected = name.find(search_term) >= 0; - is_subsequence_of_selected = search_term.is_subsequence_of(name); - is_selected_equal = name == search_term; - } - if (is_subsequence_of_type && !is_selected_equal) { - if (is_substring_of_type) { - if (!is_substring_of_selected || (current_type_prefered && !selected_type_prefered)) { - *to_select = item; - } - } else { - // substring results weigh more than subsequences, so let's make sure we don't override them - if (!is_substring_of_selected) { - if (!is_subsequence_of_selected || (current_type_prefered && !selected_type_prefered)) { - *to_select = item; - } - } - } - } - } + // Build the type tree. + for (int i = 0; i < candidates.size(); i++) { + _add_type(candidates[i], ClassDB::class_exists(candidates[i])); } - if (bool(EditorSettings::get_singleton()->get("docks/scene_tree/start_create_dialog_fully_expanded"))) { - item->set_collapsed(false); + // Select the best result. + if (empty_search) { + select_type(base_type); + } else if (candidates.size() > 0) { + select_type(_top_result(candidates, search_text)); } else { - // don't collapse search results - bool collapse = (search_box->get_text() == ""); - // don't collapse the root node - collapse &= (item != p_root); - // don't collapse abstract nodes on the first tree level - collapse &= ((parent != p_root) || (can_instance)); - item->set_collapsed(collapse); + favorite->set_disabled(true); + help_bit->set_text(""); + get_ok()->set_disabled(true); + search_options->deselect_all(); } - - const String &description = DTR(EditorHelp::get_doc_data()->class_list[p_type].brief_description); - item->set_tooltip(0, description); - - String icon_fallback = search_options->has_theme_icon(base_type, "EditorIcons") ? base_type : "Object"; - item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, icon_fallback)); - - p_types[p_type] = item; } -bool CreateDialog::_is_type_prefered(const String &type) { - bool cpp_type = ClassDB::class_exists(type); - EditorData &ed = EditorNode::get_editor_data(); - - if (cpp_type) { - return ClassDB::is_parent_class(type, preferred_search_result_type); +void CreateDialog::_add_type(const String &p_type, bool p_cpp_type) { + if (search_options_types.has(p_type)) { + return; } - return ed.script_class_is_parent(type, preferred_search_result_type); -} -bool CreateDialog::_is_class_disabled_by_feature_profile(const StringName &p_class) { - Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile(); - if (profile.is_null()) { - return false; + String inherits; + if (p_cpp_type) { + inherits = ClassDB::get_parent_class(p_type); + } else if (ScriptServer::is_global_class(p_type)) { + inherits = EditorNode::get_editor_data().script_class_get_base(p_type); + } else { + inherits = custom_type_parents[p_type]; } - return profile->is_class_disabled(p_class); + _add_type(inherits, p_cpp_type || ClassDB::class_exists(inherits)); + + TreeItem *item = search_options->create_item(search_options_types[inherits]); + search_options_types[p_type] = item; + _configure_search_option_item(item, p_type, p_cpp_type); } -void CreateDialog::select_type(const String &p_type) { - TreeItem *to_select; - if (search_options_types.has(p_type)) { - to_select = search_options_types[p_type]; +void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String &p_type, const bool p_cpp_type) { + bool script_type = ScriptServer::is_global_class(p_type); + if (p_cpp_type) { + r_item->set_text(0, p_type); + } else if (script_type) { + r_item->set_metadata(0, p_type); + r_item->set_text(0, p_type + " (" + ScriptServer::get_global_class_path(p_type).get_file() + ")"); } else { - to_select = search_options->get_root(); + r_item->set_metadata(0, custom_type_parents[p_type]); + r_item->set_text(0, p_type); } - // uncollapse from selected type to top level - // TODO: should this be in tree? - TreeItem *cur = to_select; - while (cur) { - cur->set_collapsed(false); - cur = cur->get_parent(); + bool can_instance = (p_cpp_type && ClassDB::can_instance(p_type)) || !p_cpp_type; + if (!can_instance) { + r_item->set_custom_color(0, search_options->get_theme_color("disabled_font_color", "Editor")); + r_item->set_selectable(0, false); } - to_select->select(0); - - search_options->scroll_to_item(to_select); -} - -void CreateDialog::_update_search() { - search_options->clear(); - favorite->set_disabled(true); - - help_bit->set_text(""); - - search_options_types.clear(); - - TreeItem *root = search_options->create_item(); - EditorData &ed = EditorNode::get_editor_data(); - - root->set_text(0, base_type); - String base_icon = search_options->has_theme_icon(base_type, "EditorIcons") ? base_type : "Object"; - root->set_icon(0, search_options->get_theme_icon(base_icon, "EditorIcons")); - - TreeItem *to_select = search_box->get_text() == base_type ? root : nullptr; - - for (List<StringName>::Element *I = type_list.front(); I; I = I->next()) { - String type = I->get(); + if (search_box->get_text() != "") { + r_item->set_collapsed(false); + } else { + // Don't collapse the root node or an abstract node on the first tree level. + bool should_collapse = p_type != base_type && (r_item->get_parent()->get_text(0) != base_type || can_instance); - if (_is_class_disabled_by_feature_profile(type)) { - continue; + if (should_collapse && bool(EditorSettings::get_singleton()->get("docks/scene_tree/start_create_dialog_fully_expanded"))) { + should_collapse = false; // Collapse all nodes anyway. } - bool cpp_type = ClassDB::class_exists(type); + r_item->set_collapsed(should_collapse); + } - if (base_type == "Node" && type.begins_with("Editor")) { - continue; // do not show editor nodes - } + const String &description = DTR(EditorHelp::get_doc_data()->class_list[p_type].brief_description); + r_item->set_tooltip(0, description); + r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, icon_fallback)); - if (cpp_type && !ClassDB::can_instance(type)) { - continue; // can't create what can't be instanced + if (!p_cpp_type && !script_type) { + Ref<Texture2D> icon = EditorNode::get_editor_data().get_custom_types()[custom_type_parents[p_type]][custom_type_indices[p_type]].icon; + if (icon.is_valid()) { + r_item->set_icon(0, icon); } + } +} - if (cpp_type) { - bool skip = false; - - for (Set<StringName>::Element *E = type_blacklist.front(); E && !skip; E = E->next()) { - if (ClassDB::is_parent_class(type, E->get())) { - skip = true; - } - } - if (skip) { - continue; - } +String CreateDialog::_top_result(const Vector<String> p_candidates, const String &p_search_text) const { + float highest_score = 0; + int highest_index = 0; + for (int i = 0; i < p_candidates.size(); i++) { + float score = _score_type(p_candidates[i].get_slicec(' ', 0), p_search_text); + if (score > highest_score) { + highest_score = score; + highest_index = i; } + } - if (search_box->get_text() == "") { - add_type(type, search_options_types, root, &to_select); - } else { - bool found = false; - String type2 = type; - bool cpp_type2 = cpp_type; - - if (!cpp_type && !search_loaded_scripts.has(type)) { - search_loaded_scripts[type] = ed.script_class_load_script(type); - } + return p_candidates[highest_index]; +} - while (type2 != "" && (cpp_type2 ? ClassDB::is_parent_class(type2, base_type) : ed.script_class_is_parent(type2, base_type)) && type2 != base_type) { - if (search_box->get_text().is_subsequence_ofi(type2)) { - found = true; - break; - } +float CreateDialog::_score_type(const String &p_type, const String &p_search) const { + float inverse_length = 1.f / float(p_type.length()); - type2 = cpp_type2 ? ClassDB::get_parent_class(type2) : ed.script_class_get_base(type2); - cpp_type2 = cpp_type2 || ClassDB::class_exists(type2); // Built-in class can't inherit from custom type, so we can skip the check if it's already true. + // Favor types where search term is a substring close to the start of the type. + float w = 0.5f; + int pos = p_type.findn(p_search); + float score = (pos > -1) ? 1.0f - w * MIN(1, 3 * pos * inverse_length) : MAX(0.f, .9f - w); - if (!cpp_type2 && !search_loaded_scripts.has(type2)) { - search_loaded_scripts[type2] = ed.script_class_load_script(type2); - } - } + // Favor shorter items: they resemble the search term more. + w = 0.1f; + score *= (1 - w) + w * (p_search.length() * inverse_length); - if (found) { - add_type(type, search_options_types, root, &to_select); - } - } + score *= _is_type_preferred(p_type) ? 1.0f : 0.8f; - if (EditorNode::get_editor_data().get_custom_types().has(type) && ClassDB::is_parent_class(type, base_type)) { - //there are custom types based on this... cool. + // Add score for being a favorite type. + score *= (favorite_list.find(p_type) > -1) ? 1.0f : 0.7f; - const Vector<EditorData::CustomType> &ct = EditorNode::get_editor_data().get_custom_types()[type]; - for (int i = 0; i < ct.size(); i++) { - bool show = search_box->get_text().is_subsequence_ofi(ct[i].name); - - if (!show) { - continue; - } - - if (!search_options_types.has(type)) { - add_type(type, search_options_types, root, &to_select); - } - - TreeItem *ti; - if (search_options_types.has(type)) { - ti = search_options_types[type]; - } else { - ti = search_options->get_root(); - } - - TreeItem *item = search_options->create_item(ti); - item->set_metadata(0, type); - item->set_text(0, ct[i].name); - item->set_icon(0, ct[i].icon.is_valid() ? ct[i].icon : search_options->get_theme_icon(base_icon, "EditorIcons")); - - if (!to_select || ct[i].name == search_box->get_text()) { - to_select = item; - } - } + // Look through at most 5 recent items + bool in_recent = false; + for (int i = 0; i < MIN(5, recent->get_item_count()); i++) { + if (recent->get_item_text(i) == p_type) { + in_recent = true; + break; } } + score *= in_recent ? 1.0f : 0.8f; - if (search_box->get_text() == "") { - to_select = root; - } - - if (to_select) { - to_select->select(0); - search_options->scroll_to_item(to_select); - favorite->set_disabled(false); - favorite->set_pressed(favorite_list.find(to_select->get_text(0)) != -1); - } + return score; +} - get_ok()->set_disabled(root->get_children() == nullptr); +void CreateDialog::_cleanup() { + type_list.clear(); + favorite_list.clear(); + favorites->clear(); + recent->clear(); + custom_type_parents.clear(); + custom_type_indices.clear(); } void CreateDialog::_confirmed() { - TreeItem *ti = search_options->get_selected(); - if (!ti) { + String selected_item = get_selected_type(); + if (selected_item == String()) { return; } FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("create_recent." + base_type), FileAccess::WRITE); - if (f) { - f->store_line(get_selected_type()); - TreeItem *t = recent->get_root(); - if (t) { - t = t->get_children(); - } - int count = 0; - while (t) { - if (t->get_text(0) != get_selected_type()) { - f->store_line(t->get_text(0)); - } + f->store_line(selected_item); - if (count > 32) { - //limit it to 32 entries.. - break; + for (int i = 0; i < MIN(32, recent->get_item_count()); i++) { + if (recent->get_item_text(i) != selected_item) { + f->store_line(recent->get_item_text(i)); } - t = t->get_next(); - count++; } memdelete(f); @@ -453,6 +337,26 @@ void CreateDialog::_confirmed() { emit_signal("create"); hide(); + _cleanup(); +} + +void CreateDialog::_text_changed(const String &p_newtext) { + _update_search(); +} + +void CreateDialog::_sbox_input(const Ref<InputEvent> &p_ie) { + Ref<InputEventKey> k = p_ie; + if (k.is_valid()) { + switch (k->get_keycode()) { + case KEY_UP: + case KEY_DOWN: + case KEY_PAGEUP: + case KEY_PAGEDOWN: { + search_options->call("_gui_input", k); + search_box->accept_event(); + } break; + } + } } void CreateDialog::_notification(int p_what) { @@ -472,42 +376,36 @@ void CreateDialog::_notification(int p_what) { search_box->select_all(); } else { EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "create_new_node", Rect2(get_position(), get_size())); - search_loaded_scripts.clear(); } } break; } } -void CreateDialog::set_base_type(const String &p_base) { - base_type = p_base; - if (is_replace_mode) { - set_title(vformat(TTR("Change %s Type"), p_base)); - } else { - set_title(vformat(TTR("Create New %s"), p_base)); +void CreateDialog::select_type(const String &p_type) { + if (!search_options_types.has(p_type)) { + return; } - _update_search(); -} - -String CreateDialog::get_base_type() const { - return base_type; -} + TreeItem *to_select = search_options_types[p_type]; + to_select->select(0); + search_options->scroll_to_item(to_select); -void CreateDialog::set_preferred_search_result_type(const String &p_preferred_type) { - preferred_search_result_type = p_preferred_type; -} + if (EditorHelp::get_doc_data()->class_list.has(p_type)) { + help_bit->set_text(DTR(EditorHelp::get_doc_data()->class_list[p_type].brief_description)); + } -String CreateDialog::get_preferred_search_result_type() { - return preferred_search_result_type; + favorite->set_disabled(false); + favorite->set_pressed(favorite_list.find(p_type) != -1); + get_ok()->set_disabled(false); } String CreateDialog::get_selected_type() { TreeItem *selected = search_options->get_selected(); - if (selected) { - return selected->get_text(0); - } else { + if (!selected) { return String(); } + + return selected->get_text(0); } Object *CreateDialog::instance_selected() { @@ -518,14 +416,9 @@ Object *CreateDialog::instance_selected() { } Variant md = selected->get_metadata(0); - String custom; - if (md.get_type() != Variant::NIL) { - custom = md; - } - Object *obj = nullptr; - - if (!custom.empty()) { + if (md.get_type() != Variant::NIL) { + String custom = md; if (ScriptServer::is_global_class(custom)) { obj = EditorNode::get_editor_data().script_class_instance(custom); Node *n = Object::cast_to<Node>(obj); @@ -545,9 +438,10 @@ Object *CreateDialog::instance_selected() { obj->get_property_list(&pinfo); for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - if (E->get().type == Variant::OBJECT && E->get().usage & PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT) { - Object *prop = ClassDB::instance(E->get().class_name); - obj->set(E->get().name, prop); + PropertyInfo pi = E->get(); + if (pi.type == Variant::OBJECT && pi.usage & PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT) { + Object *prop = ClassDB::instance(pi.class_name); + obj->set(pi.name, prop); } } @@ -555,29 +449,18 @@ Object *CreateDialog::instance_selected() { } void CreateDialog::_item_selected() { - TreeItem *item = search_options->get_selected(); - if (!item) { - return; - } - - String name = item->get_text(0); - - favorite->set_disabled(false); - favorite->set_pressed(favorite_list.find(name) != -1); - - if (!EditorHelp::get_doc_data()->class_list.has(name)) { - return; - } - - help_bit->set_text(DTR(EditorHelp::get_doc_data()->class_list[name].brief_description)); - - get_ok()->set_disabled(false); + String name = get_selected_type(); + select_type(name); } void CreateDialog::_hide_requested() { _cancel_pressed(); // From AcceptDialog. } +void CreateDialog::cancel_pressed() { + _cleanup(); +} + void CreateDialog::_favorite_toggled() { TreeItem *item = search_options->get_selected(); if (!item) { @@ -597,50 +480,8 @@ void CreateDialog::_favorite_toggled() { _save_and_update_favorite_list(); } -void CreateDialog::_save_favorite_list() { - FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("favorites." + base_type), FileAccess::WRITE); - - if (f) { - for (int i = 0; i < favorite_list.size(); i++) { - String l = favorite_list[i]; - String name = l.split(" ")[0]; - if (!(ClassDB::class_exists(name) || ScriptServer::is_global_class(name))) { - continue; - } - f->store_line(l); - } - memdelete(f); - } -} - -void CreateDialog::_update_favorite_list() { - favorites->clear(); - - TreeItem *root = favorites->create_item(); - - String icon_fallback = search_options->has_theme_icon(base_type, "EditorIcons") ? base_type : "Object"; - - for (int i = 0; i < favorite_list.size(); i++) { - String l = favorite_list[i]; - String name = l.split(" ")[0]; - if (!((ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) && !_is_class_disabled_by_feature_profile(name))) { - continue; - } - - TreeItem *ti = favorites->create_item(root); - ti->set_text(0, l); - ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(name, icon_fallback)); - } - emit_signal("favorites_updated"); -} - -void CreateDialog::_history_selected() { - TreeItem *item = recent->get_selected(); - if (!item) { - return; - } - - search_box->set_text(item->get_text(0).get_slicec(' ', 0)); +void CreateDialog::_history_selected(int p_idx) { + search_box->set_text(recent->get_item_text(p_idx).get_slicec(' ', 0)); favorites->deselect_all(); _update_search(); } @@ -652,12 +493,12 @@ void CreateDialog::_favorite_selected() { } search_box->set_text(item->get_text(0).get_slicec(' ', 0)); - recent->deselect_all(); + recent->unselect_all(); _update_search(); } -void CreateDialog::_history_activated() { - _history_selected(); +void CreateDialog::_history_activated(int p_idx) { + _history_selected(p_idx); _confirmed(); } @@ -740,8 +581,61 @@ void CreateDialog::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co } void CreateDialog::_save_and_update_favorite_list() { - _save_favorite_list(); - _update_favorite_list(); + favorites->clear(); + TreeItem *root = favorites->create_item(); + + FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("favorites." + base_type), FileAccess::WRITE); + if (f) { + for (int i = 0; i < favorite_list.size(); i++) { + String l = favorite_list[i]; + String name = l.get_slicec(' ', 0); + if (!(ClassDB::class_exists(name) || ScriptServer::is_global_class(name))) { + continue; + } + f->store_line(l); + + if (_is_class_disabled_by_feature_profile(name)) { + continue; + } + + TreeItem *ti = favorites->create_item(root); + ti->set_text(0, l); + ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(name, icon_fallback)); + } + memdelete(f); + } + + emit_signal("favorites_updated"); +} + +void CreateDialog::_load_favorites_and_history() { + String dir = EditorSettings::get_singleton()->get_project_settings_dir(); + FileAccess *f = FileAccess::open(dir.plus_file("create_recent." + base_type), FileAccess::READ); + if (f) { + while (!f->eof_reached()) { + String l = f->get_line().strip_edges(); + String name = l.get_slicec(' ', 0); + + if ((ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) && !_is_class_disabled_by_feature_profile(name)) { + recent->add_item(l, EditorNode::get_singleton()->get_class_icon(name, icon_fallback)); + } + } + + memdelete(f); + } + + f = FileAccess::open(dir.plus_file("favorites." + base_type), FileAccess::READ); + if (f) { + while (!f->eof_reached()) { + String l = f->get_line().strip_edges(); + + if (l != String()) { + favorite_list.push_back(l); + } + } + + memdelete(f); + } } void CreateDialog::_bind_methods() { @@ -756,7 +650,11 @@ void CreateDialog::_bind_methods() { } CreateDialog::CreateDialog() { - is_replace_mode = false; + base_type = "Object"; + preferred_search_result_type = ""; + + type_blacklist.insert("PluginScript"); // PluginScript must be initialized before use, which is not possible here. + type_blacklist.insert("ScriptCreateDialog"); // This is an exposed editor Node that doesn't have an Editor prefix. HSplitContainer *hsc = memnew(HSplitContainer); add_child(hsc); @@ -765,67 +663,64 @@ CreateDialog::CreateDialog() { hsc->add_child(vsc); VBoxContainer *fav_vb = memnew(VBoxContainer); - vsc->add_child(fav_vb); fav_vb->set_custom_minimum_size(Size2(150, 100) * EDSCALE); fav_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL); + vsc->add_child(fav_vb); favorites = memnew(Tree); - fav_vb->add_margin_child(TTR("Favorites:"), favorites, true); favorites->set_hide_root(true); favorites->set_hide_folding(true); favorites->set_allow_reselect(true); favorites->connect("cell_selected", callable_mp(this, &CreateDialog::_favorite_selected)); favorites->connect("item_activated", callable_mp(this, &CreateDialog::_favorite_activated)); + favorites->add_theme_constant_override("draw_guides", 1); #ifndef _MSC_VER #warning cant forward drag data to a non control, must be fixed #endif //favorites->set_drag_forwarding(this); - favorites->add_theme_constant_override("draw_guides", 1); + fav_vb->add_margin_child(TTR("Favorites:"), favorites, true); VBoxContainer *rec_vb = memnew(VBoxContainer); vsc->add_child(rec_vb); rec_vb->set_custom_minimum_size(Size2(150, 100) * EDSCALE); rec_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL); - recent = memnew(Tree); + recent = memnew(ItemList); rec_vb->add_margin_child(TTR("Recent:"), recent, true); - recent->set_hide_root(true); - recent->set_hide_folding(true); recent->set_allow_reselect(true); - recent->connect("cell_selected", callable_mp(this, &CreateDialog::_history_selected)); + recent->connect("item_selected", callable_mp(this, &CreateDialog::_history_selected)); recent->connect("item_activated", callable_mp(this, &CreateDialog::_history_activated)); recent->add_theme_constant_override("draw_guides", 1); VBoxContainer *vbc = memnew(VBoxContainer); - hsc->add_child(vbc); vbc->set_custom_minimum_size(Size2(300, 0) * EDSCALE); vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); - HBoxContainer *search_hb = memnew(HBoxContainer); + hsc->add_child(vbc); + search_box = memnew(LineEdit); search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); + search_box->connect("text_changed", callable_mp(this, &CreateDialog::_text_changed)); + search_box->connect("gui_input", callable_mp(this, &CreateDialog::_sbox_input)); + + HBoxContainer *search_hb = memnew(HBoxContainer); search_hb->add_child(search_box); + favorite = memnew(Button); favorite->set_flat(true); favorite->set_toggle_mode(true); - search_hb->add_child(favorite); favorite->connect("pressed", callable_mp(this, &CreateDialog::_favorite_toggled)); + search_hb->add_child(favorite); vbc->add_margin_child(TTR("Search:"), search_hb); - search_box->connect("text_changed", callable_mp(this, &CreateDialog::_text_changed)); - search_box->connect("gui_input", callable_mp(this, &CreateDialog::_sbox_input)); + search_options = memnew(Tree); - vbc->add_margin_child(TTR("Matches:"), search_options, true); - get_ok()->set_disabled(true); - register_text_enter(search_box); - set_hide_on_ok(false); search_options->connect("item_activated", callable_mp(this, &CreateDialog::_confirmed)); search_options->connect("cell_selected", callable_mp(this, &CreateDialog::_item_selected)); - base_type = "Object"; - preferred_search_result_type = ""; + vbc->add_margin_child(TTR("Matches:"), search_options, true); help_bit = memnew(EditorHelpBit); - vbc->add_margin_child(TTR("Description:"), help_bit); help_bit->connect("request_hide", callable_mp(this, &CreateDialog::_hide_requested)); + vbc->add_margin_child(TTR("Description:"), help_bit); - type_blacklist.insert("PluginScript"); // PluginScript must be initialized before use, which is not possible here - type_blacklist.insert("ScriptCreateDialog"); // This is an exposed editor Node that doesn't have an Editor prefix. + register_text_enter(search_box); + set_hide_on_ok(false); } diff --git a/editor/create_dialog.h b/editor/create_dialog.h index cdc91ae535..52eb9945af 100644 --- a/editor/create_dialog.h +++ b/editor/create_dialog.h @@ -35,60 +35,65 @@ #include "scene/gui/button.h" #include "scene/gui/dialogs.h" #include "scene/gui/item_list.h" -#include "scene/gui/label.h" #include "scene/gui/line_edit.h" #include "scene/gui/tree.h" class CreateDialog : public ConfirmationDialog { GDCLASS(CreateDialog, ConfirmationDialog); - Vector<String> favorite_list; - Tree *favorites; - Tree *recent; - - Button *favorite; LineEdit *search_box; Tree *search_options; - HashMap<String, TreeItem *> search_options_types; - HashMap<String, RES> search_loaded_scripts; - bool is_replace_mode; + String base_type; + String icon_fallback; String preferred_search_result_type; + + Button *favorite; + Vector<String> favorite_list; + Tree *favorites; + ItemList *recent; EditorHelpBit *help_bit; + + HashMap<String, TreeItem *> search_options_types; + HashMap<String, String> custom_type_parents; + HashMap<String, int> custom_type_indices; List<StringName> type_list; Set<StringName> type_blacklist; - void _item_selected(); - void _hide_requested(); - void _update_search(); - void _update_favorite_list(); - void _save_favorite_list(); - void _favorite_toggled(); + bool _should_hide_type(const String &p_type) const; + void _add_type(const String &p_current, bool p_cpp_type); + void _configure_search_option_item(TreeItem *r_item, const String &p_type, const bool p_cpp_type); + String _top_result(const Vector<String> p_candidates, const String &p_search_text) const; + float _score_type(const String &p_type, const String &p_search) const; + bool _is_type_preferred(const String &p_type) const; - void _history_selected(); - void _favorite_selected(); - - void _history_activated(); - void _favorite_activated(); + void _fill_type_list(); + void _cleanup(); void _sbox_input(const Ref<InputEvent> &p_ie); + void _text_changed(const String &p_newtext); + void select_type(const String &p_type); + void _item_selected(); + void _hide_requested(); void _confirmed(); - void _text_changed(const String &p_newtext); + virtual void cancel_pressed(); - Ref<Texture2D> _get_editor_icon(const String &p_type) const; + void _favorite_toggled(); - void add_type(const String &p_type, HashMap<String, TreeItem *> &p_types, TreeItem *p_root, TreeItem **to_select); + void _history_selected(int p_idx); + void _favorite_selected(); - void select_type(const String &p_type); + void _history_activated(int p_idx); + void _favorite_activated(); Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); - bool _is_class_disabled_by_feature_profile(const StringName &p_class); - bool _is_type_prefered(const String &type); + bool _is_class_disabled_by_feature_profile(const StringName &p_class) const; + void _load_favorites_and_history(); protected: void _notification(int p_what); @@ -100,11 +105,11 @@ public: Object *instance_selected(); String get_selected_type(); - void set_base_type(const String &p_base); - String get_base_type() const; + void set_base_type(const String &p_base) { base_type = p_base; } + String get_base_type() const { return base_type; } - void set_preferred_search_result_type(const String &p_preferred_type); - String get_preferred_search_result_type(); + void set_preferred_search_result_type(const String &p_preferred_type) { preferred_search_result_type = p_preferred_type; } + String get_preferred_search_result_type() { return preferred_search_result_type; } void popup_create(bool p_dont_clear, bool p_replace_mode = false, const String &p_select_type = "Node"); diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index eb4c587122..50be291c91 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -55,7 +55,7 @@ VBoxContainer *EditorFileDialog::get_vbox() { } void EditorFileDialog::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { + if (p_what == NOTIFICATION_READY || p_what == NOTIFICATION_THEME_CHANGED) { // update icons mode_thumbnails->set_icon(item_list->get_theme_icon("FileThumbnail", "EditorIcons")); mode_list->set_icon(item_list->get_theme_icon("FileList", "EditorIcons")); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 9ca3d387d9..e367ed4989 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1107,7 +1107,7 @@ void EditorFileSystem::_notification(int p_what) { _queue_update_script_classes(); first_scan = false; } - } else if (!scanning) { + } else if (!scanning && thread) { set_process(false); if (filesystem) { diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index cf00c536a7..100c76c32b 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -231,7 +231,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { // Default font MAKE_DEFAULT_FONT(df, default_font_size); - p_theme->set_font("font", "Node", df); // Default theme font + p_theme->set_default_font(df); // Default theme font p_theme->set_font("main", "EditorFonts", df); // Bold font diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 6e65103748..a4a53d8a92 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -5520,10 +5520,10 @@ EditorNode::EditorNode() { switch (display_scale) { case 0: { // Try applying a suitable display scale automatically - const int screen = DisplayServer::get_singleton()->window_get_current_screen(); #ifdef OSX_ENABLED - editor_set_scale(DisplayServer::get_singleton()->screen_get_scale(screen)); + editor_set_scale(DisplayServer::get_singleton()->screen_get_max_scale()); #else + const int screen = DisplayServer::get_singleton()->window_get_current_screen(); editor_set_scale(DisplayServer::get_singleton()->screen_get_dpi(screen) >= 192 && DisplayServer::get_singleton()->screen_get_size(screen).x > 2000 ? 2.0 : 1.0); #endif } break; diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index 1148a6c7ec..6a73e6c072 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -108,24 +108,33 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L } int window_placement = EditorSettings::get_singleton()->get("run/window_placement/rect"); + bool hidpi_proj = ProjectSettings::get_singleton()->get("display/window/dpi/allow_hidpi"); + int display_scale = 1; + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_HIDPI)) { + if (OS::get_singleton()->is_hidpi_allowed()) { + if (hidpi_proj) { + display_scale = 1; // Both editor and project runs in hiDPI mode, do not scale. + } else { + display_scale = DisplayServer::get_singleton()->screen_get_max_scale(); // Editor is in hiDPI mode, project is not, scale down. + } + } else { + if (hidpi_proj) { + display_scale = (1.f / DisplayServer::get_singleton()->screen_get_max_scale()); // Editor is not in hiDPI mode, project is, scale up. + } else { + display_scale = 1; // Both editor and project runs in lowDPI mode, do not scale. + } + } + screen_rect.position /= display_scale; + screen_rect.size /= display_scale; + } switch (window_placement) { case 0: { // top left - args.push_back("--position"); args.push_back(itos(screen_rect.position.x) + "," + itos(screen_rect.position.y)); } break; case 1: { // centered - int display_scale = 1; -#ifdef OSX_ENABLED - display_scale = DisplayServer::get_singleton()->screen_get_scale(screen); -#else - if (DisplayServer::get_singleton()->screen_get_dpi(screen) >= 192 && DisplayServer::get_singleton()->screen_get_size(screen).x > 2000) { - display_scale = 2; - } -#endif - - Vector2 pos = screen_rect.position + ((screen_rect.size / display_scale - desired_size) / 2).floor(); + Vector2 pos = (screen_rect.position) + ((screen_rect.size - desired_size) / 2).floor(); args.push_back("--position"); args.push_back(itos(pos.x) + "," + itos(pos.y)); } break; @@ -140,10 +149,8 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L args.push_back("--position"); args.push_back(itos(pos.x) + "," + itos(pos.y)); args.push_back("--maximized"); - } break; case 4: { // force fullscreen - Vector2 pos = screen_rect.position; args.push_back("--position"); args.push_back(itos(pos.x) + "," + itos(pos.y)); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 1312f59a70..de04a299fb 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -702,30 +702,26 @@ void AnimationPlayerEditor::_animation_edit() { } } -void AnimationPlayerEditor::_dialog_action(String p_file) { +void AnimationPlayerEditor::_dialog_action(String p_path) { switch (current_option) { case RESOURCE_LOAD: { ERR_FAIL_COND(!player); - Ref<Resource> res = ResourceLoader::load(p_file, "Animation"); - ERR_FAIL_COND_MSG(res.is_null(), "Cannot load Animation from file '" + p_file + "'."); - ERR_FAIL_COND_MSG(!res->is_class("Animation"), "Loaded resource from file '" + p_file + "' is not Animation."); - if (p_file.rfind("/") != -1) { - p_file = p_file.substr(p_file.rfind("/") + 1, p_file.length()); - } - if (p_file.rfind("\\") != -1) { - p_file = p_file.substr(p_file.rfind("\\") + 1, p_file.length()); - } + Ref<Resource> res = ResourceLoader::load(p_path, "Animation"); + ERR_FAIL_COND_MSG(res.is_null(), "Cannot load Animation from file '" + p_path + "'."); + ERR_FAIL_COND_MSG(!res->is_class("Animation"), "Loaded resource from file '" + p_path + "' is not Animation."); - if (p_file.find(".") != -1) { - p_file = p_file.substr(0, p_file.find(".")); + String anim_name = p_path.get_file(); + int ext_pos = anim_name.rfind("."); + if (ext_pos != -1) { + anim_name = anim_name.substr(0, ext_pos); } undo_redo->create_action(TTR("Load Animation")); - undo_redo->add_do_method(player, "add_animation", p_file, res); - undo_redo->add_undo_method(player, "remove_animation", p_file); - if (player->has_animation(p_file)) { - undo_redo->add_undo_method(player, "add_animation", p_file, player->get_animation(p_file)); + undo_redo->add_do_method(player, "add_animation", anim_name, res); + undo_redo->add_undo_method(player, "remove_animation", anim_name); + if (player->has_animation(anim_name)) { + undo_redo->add_undo_method(player, "add_animation", anim_name, player->get_animation(anim_name)); } undo_redo->add_do_method(this, "_animation_player_changed", player); undo_redo->add_undo_method(this, "_animation_player_changed", player); @@ -741,7 +737,7 @@ void AnimationPlayerEditor::_dialog_action(String p_file) { RES current_res = RES(Object::cast_to<Resource>(*anim)); - _animation_save_in_path(current_res, p_file); + _animation_save_in_path(current_res, p_path); } } } diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 325d7c5ce6..5184793760 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -2336,10 +2336,10 @@ ProjectManager::ProjectManager() { switch (display_scale) { case 0: { // Try applying a suitable display scale automatically - const int screen = DisplayServer::get_singleton()->window_get_current_screen(); #ifdef OSX_ENABLED - editor_set_scale(DisplayServer::get_singleton()->screen_get_scale(screen)); + editor_set_scale(DisplayServer::get_singleton()->screen_get_max_scale()); #else + const int screen = DisplayServer::get_singleton()->window_get_current_screen(); editor_set_scale(DisplayServer::get_singleton()->screen_get_dpi(screen) >= 192 && DisplayServer::get_singleton()->screen_get_size(screen).x > 2000 ? 2.0 : 1.0); #endif } break; @@ -2371,11 +2371,8 @@ ProjectManager::ProjectManager() { // Define a minimum window size to prevent UI elements from overlapping or being cut off DisplayServer::get_singleton()->window_set_min_size(Size2(750, 420) * EDSCALE); -#ifndef OSX_ENABLED - // The macOS platform implementation uses its own hiDPI window resizing code // TODO: Resize windows on hiDPI displays on Windows and Linux and remove the line below DisplayServer::get_singleton()->window_set_size(DisplayServer::get_singleton()->window_get_size() * MAX(1, EDSCALE)); -#endif } FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files")); diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index dc14580d92..c9b951f4d9 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -2364,6 +2364,7 @@ void DisplayServerX11::process_events() { for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { if (E->get().focused) { focus_found = true; + break; } } diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h index 8e27f10dc2..3e6b59f58c 100644 --- a/platform/osx/display_server_osx.h +++ b/platform/osx/display_server_osx.h @@ -132,6 +132,7 @@ public: bool on_top = false; bool borderless = false; bool resize_disabled = false; + bool no_focus = false; }; Point2i im_selection; @@ -150,7 +151,6 @@ public: void _set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window); - float _display_scale(id screen) const; Point2i _get_screens_origin() const; Point2i _get_native_screen_position(int p_screen) const; @@ -224,6 +224,7 @@ public: virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const; virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const; virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + virtual float screen_get_max_scale() const; virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const; virtual Vector<int> get_window_list() const; diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm index e3b29fc047..b7b750a975 100644 --- a/platform/osx/display_server_osx.mm +++ b/platform/osx/display_server_osx.mm @@ -47,6 +47,8 @@ #if defined(OPENGL_ENABLED) #include "drivers/gles2/rasterizer_gles2.h" //TODO - reimplement OpenGLES + +#import <AppKit/NSOpenGLView.h> #endif #if defined(VULKAN_ENABLED) @@ -68,11 +70,11 @@ static void _get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWith r_state->set_metakey((p_osx_state & NSEventModifierFlagCommand)); } -static Vector2i _get_mouse_pos(DisplayServerOSX::WindowData &p_wd, NSPoint p_locationInWindow, CGFloat p_backingScaleFactor) { +static Vector2i _get_mouse_pos(DisplayServerOSX::WindowData &p_wd, NSPoint p_locationInWindow) { const NSRect contentRect = [p_wd.window_view frame]; - const NSPoint p = p_locationInWindow; - p_wd.mouse_pos.x = p.x * p_backingScaleFactor; - p_wd.mouse_pos.y = (contentRect.size.height - p.y) * p_backingScaleFactor; + const float scale = DS_OSX->screen_get_max_scale(); + p_wd.mouse_pos.x = p_locationInWindow.x * scale; + p_wd.mouse_pos.y = (contentRect.size.height - p_locationInWindow.y) * scale; DS_OSX->last_mouse_pos = p_wd.mouse_pos; Input::get_singleton()->set_mouse_position(p_wd.mouse_pos); return p_wd.mouse_pos; @@ -131,10 +133,11 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { // From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost // This works around an AppKit bug, where key up events while holding // down the command key don't get sent to the key window. - if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand)) + if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand)) { [[self keyWindow] sendEvent:event]; - else + } else { [super sendEvent:event]; + } } @end @@ -210,8 +213,9 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { } - (void)globalMenuCallback:(id)sender { - if (![sender representedObject]) + if (![sender representedObject]) { return; + } GlobalMenuItem *value = [sender representedObject]; @@ -264,8 +268,9 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { } - (void)showAbout:(id)sender { - if (OS_OSX::get_singleton()->get_main_loop()) + if (OS_OSX::get_singleton()->get_main_loop()) { OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT); + } } @end @@ -318,6 +323,11 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { [pwd.window_object makeKeyAndOrderFront:nil]; // Move focus back to main window if there is no parent or other windows left. } +#if defined(OPENGL_ENABLED) + if (DS_OSX->rendering_driver == "opengl_es") { + //TODO - reimplement OpenGLES + } +#endif #ifdef VULKAN_ENABLED if (DS_OSX->rendering_driver == "vulkan") { DS_OSX->context_vulkan->window_destroy(window_id); @@ -345,22 +355,25 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { wd.fullscreen = false; + const float scale = DS_OSX->screen_get_max_scale(); if (wd.min_size != Size2i()) { - Size2i size = wd.min_size / DS_OSX->_display_scale([wd.window_object screen]); + Size2i size = wd.min_size / scale; [wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)]; } if (wd.max_size != Size2i()) { - Size2i size = wd.max_size / DS_OSX->_display_scale([wd.window_object screen]); + Size2i size = wd.max_size / scale; [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)]; } - if (wd.resize_disabled) + if (wd.resize_disabled) { [wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable]; + } } - (void)windowDidChangeBackingProperties:(NSNotification *)notification { - if (!DisplayServerOSX::get_singleton()) + if (!DisplayServerOSX::get_singleton()) { return; + } ERR_FAIL_COND(!DS_OSX->windows.has(window_id)); DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id]; @@ -368,34 +381,21 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { CGFloat newBackingScaleFactor = [wd.window_object backingScaleFactor]; CGFloat oldBackingScaleFactor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue]; -#if defined(OPENGL_ENABLED) - if (DS_OSX->rendering_driver == "opengl_es") { - //TODO - reimplement OpenGLES - if (OS_OSX::get_singleton()->is_hidpi_allowed()) { - [wd.window_view setWantsBestResolutionOpenGLSurface:YES]; - } else { - [wd.window_view setWantsBestResolutionOpenGLSurface:NO]; - } - } -#endif - if (newBackingScaleFactor != oldBackingScaleFactor) { //Set new display scale and window size - float newDisplayScale = OS_OSX::get_singleton()->is_hidpi_allowed() ? newBackingScaleFactor : 1.0; - + const float scale = DS_OSX->screen_get_max_scale(); const NSRect contentRect = [wd.window_view frame]; - wd.size.width = contentRect.size.width * newDisplayScale; - wd.size.height = contentRect.size.height * newDisplayScale; + wd.size.width = contentRect.size.width * scale; + wd.size.height = contentRect.size.height * scale; DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_DPI_CHANGE); -#if defined(VULKAN_ENABLED) - if (DS_OSX->rendering_driver == "vulkan") { - CALayer *layer = [wd.window_view layer]; - layer.contentsScale = DS_OSX->_display_scale([wd.window_object screen]); + CALayer *layer = [wd.window_view layer]; + if (layer) { + layer.contentsScale = scale; } -#endif + //Force window resize event [self windowDidResize:notification]; } @@ -407,22 +407,24 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { } DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id]; + const NSRect contentRect = [wd.window_view frame]; + + const float scale = DS_OSX->screen_get_max_scale(); + wd.size.width = contentRect.size.width * scale; + wd.size.height = contentRect.size.height * scale; + + CALayer *layer = [wd.window_view layer]; + if (layer) { + layer.contentsScale = scale; + } + #if defined(OPENGL_ENABLED) if (DS_OSX->rendering_driver == "opengl_es") { //TODO - reimplement OpenGLES - wd.context_gles2->update(); } #endif - const NSRect contentRect = [wd.window_view frame]; - - float displayScale = DS_OSX->_display_scale([wd.window_object screen]); - wd.size.width = contentRect.size.width * displayScale; - wd.size.height = contentRect.size.height * displayScale; - #if defined(VULKAN_ENABLED) if (DS_OSX->rendering_driver == "vulkan") { - CALayer *layer = [wd.window_view layer]; - layer.contentsScale = displayScale; DS_OSX->context_vulkan->window_resize(window_id, wd.size.width, wd.size.height); } #endif @@ -467,8 +469,7 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { } DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id]; - const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [wd.window_view backingScaleFactor] : 1.0; - _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream], backingScaleFactor); + _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); Input::get_singleton()->set_mouse_position(wd.mouse_pos); DS_OSX->window_focused = true; @@ -543,6 +544,12 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { } - (CALayer *)makeBackingLayer { +#if defined(OPENGL_ENABLED) + if (DS_OSX->rendering_driver == "opengl_es") { + CALayer *layer = [[NSOpenGLLayer class] layer]; + return layer; + } +#endif #if defined(VULKAN_ENABLED) if (DS_OSX->rendering_driver == "vulkan") { CALayer *layer = [[CAMetalLayer class] layer]; @@ -553,20 +560,17 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { } - (void)updateLayer { -#if defined(VULKAN_ENABLED) - if (DS_OSX->rendering_driver == "vulkan") { - [super updateLayer]; - } -#endif #if defined(OPENGL_ENABLED) if (DS_OSX->rendering_driver == "opengl_es") { - ERR_FAIL_COND(!DS_OSX->windows.has(window_id)); - DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id]; - - wd.context_gles2->update(); + [super updateLayer]; //TODO - reimplement OpenGLES } #endif +#if defined(VULKAN_ENABLED) + if (DS_OSX->rendering_driver == "vulkan") { + [super updateLayer]; + } +#endif } - (BOOL)wantsUpdateLayer { @@ -631,8 +635,9 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; } - (void)doCommandBySelector:(SEL)aSelector { - if ([self respondsToSelector:aSelector]) + if ([self respondsToSelector:aSelector]) { [self performSelector:aSelector]; + } } - (void)unmarkText { @@ -667,8 +672,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id]; const NSRect contentRect = [wd.window_view frame]; - float displayScale = DS_OSX->_display_scale([wd.window_object screen]); - NSRect pointInWindowRect = NSMakeRect(wd.im_position.x / displayScale, contentRect.size.height - (wd.im_position.y / displayScale) - 1, 0, 0); + const float scale = DS_OSX->screen_get_max_scale(); + NSRect pointInWindowRect = NSMakeRect(wd.im_position.x / scale, contentRect.size.height - (wd.im_position.y / scale) - 1, 0, 0); NSPoint pointOnScreen = [wd.window_object convertRectToScreen:pointInWindowRect].origin; return NSMakeRect(pointOnScreen.x, pointOnScreen.y, 0, 0); @@ -707,8 +712,9 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; for (i = 0; i < length; i++) { const unichar codepoint = [characters characterAtIndex:i]; - if ((codepoint & 0xFF00) == 0xF700) + if ((codepoint & 0xFF00) == 0xF700) { continue; + } DisplayServerOSX::KeyEvent ke; @@ -775,6 +781,12 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; } - (BOOL)canBecomeKeyView { + if (DS_OSX->windows.has(window_id)) { + DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id]; + if (wd.no_focus) { + return NO; + } + } return YES; } @@ -801,8 +813,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i Ref<InputEventMouseButton> mb; mb.instance(); mb->set_window_id(window_id); - const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [[event window] backingScaleFactor] : 1.0; - const Vector2 pos = _get_mouse_pos(wd, [event locationInWindow], backingScaleFactor); + const Vector2 pos = _get_mouse_pos(wd, [event locationInWindow]); _get_key_modifier_state([event modifierFlags], mb); mb->set_button_index(index); mb->set_pressed(pressed); @@ -899,8 +910,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i mm->set_window_id(window_id); mm->set_button_mask(DS_OSX->last_button_state); - const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [[event window] backingScaleFactor] : 1.0; - const Vector2i pos = _get_mouse_pos(wd, mpos, backingScaleFactor); + const Vector2i pos = _get_mouse_pos(wd, mpos); mm->set_position(pos); mm->set_pressure([event pressure]); if ([event subtype] == NSEventSubtypeTabletPoint) { @@ -909,7 +919,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i } mm->set_global_position(pos); mm->set_speed(Input::get_singleton()->get_last_mouse_speed()); - const Vector2i relativeMotion = Vector2i(delta.x, delta.y) * backingScaleFactor; + const Vector2i relativeMotion = Vector2i(delta.x, delta.y) * DS_OSX->screen_get_max_scale(); mm->set_relative(relativeMotion); _get_key_modifier_state([event modifierFlags], mm); @@ -961,16 +971,18 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i ERR_FAIL_COND(!DS_OSX->windows.has(window_id)); DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id]; - if (DS_OSX->mouse_mode != DisplayServer::MOUSE_MODE_CAPTURED) + if (DS_OSX->mouse_mode != DisplayServer::MOUSE_MODE_CAPTURED) { DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_MOUSE_EXIT); + } } - (void)mouseEntered:(NSEvent *)event { ERR_FAIL_COND(!DS_OSX->windows.has(window_id)); DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id]; - if (DS_OSX->mouse_mode != DisplayServer::MOUSE_MODE_CAPTURED) + if (DS_OSX->mouse_mode != DisplayServer::MOUSE_MODE_CAPTURED) { DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_MOUSE_ENTER); + } DisplayServer::CursorShape p_shape = DS_OSX->cursor_shape; DS_OSX->cursor_shape = DisplayServer::CURSOR_MAX; @@ -985,8 +997,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i ev.instance(); ev->set_window_id(window_id); _get_key_modifier_state([event modifierFlags], ev); - const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [[event window] backingScaleFactor] : 1.0; - ev->set_position(_get_mouse_pos(wd, [event locationInWindow], backingScaleFactor)); + ev->set_position(_get_mouse_pos(wd, [event locationInWindow])); ev->set_factor([event magnification] + 1.0); Input::get_singleton()->accumulate_input_event(ev); @@ -1002,17 +1013,8 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i [trackingArea release]; } - NSTrackingAreaOptions options = - NSTrackingMouseEnteredAndExited | - NSTrackingActiveInKeyWindow | - NSTrackingCursorUpdate | - NSTrackingInVisibleRect; - - trackingArea = [[NSTrackingArea alloc] - initWithRect:[self bounds] - options:options - owner:self - userInfo:nil]; + NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingCursorUpdate | NSTrackingInVisibleRect; + trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; [self addTrackingArea:trackingArea]; [super updateTrackingAreas]; @@ -1042,8 +1044,9 @@ static bool isNumpadKey(unsigned int key) { 0x00 }; for (int i = 0; table[i] != 0; i++) { - if (key == table[i]) + if (key == table[i]) { return true; + } } return false; } @@ -1183,8 +1186,9 @@ static int translateKey(unsigned int key) { /* 7f */ KEY_UNKNOWN, }; - if (key >= 128) + if (key >= 128) { return KEY_UNKNOWN; + } return table[key]; } @@ -1253,16 +1257,19 @@ static const _KeyCodeMap _keycodes[55] = { }; static int remapKey(unsigned int key, unsigned int state) { - if (isNumpadKey(key)) + if (isNumpadKey(key)) { return translateKey(key); + } TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); - if (!currentKeyboard) + if (!currentKeyboard) { return translateKey(key); + } CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); - if (!layoutData) + if (!layoutData) { return translateKey(key); + } const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData); @@ -1335,8 +1342,9 @@ static int remapKey(unsigned int key, unsigned int state) { } // Pass events to IME handler - if (wd.im_active) + if (wd.im_active) { [self interpretKeyEvents:[NSArray arrayWithObject:event]]; + } } - (void)flagsChanged:(NSEvent *)event { @@ -1489,8 +1497,7 @@ inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy double deltaX, deltaY; - const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [[event window] backingScaleFactor] : 1.0; - _get_mouse_pos(wd, [event locationInWindow], backingScaleFactor); + _get_mouse_pos(wd, [event locationInWindow]); deltaX = [event scrollingDeltaX]; deltaY = [event scrollingDeltaY]; @@ -1527,6 +1534,25 @@ inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy - (BOOL)canBecomeKeyWindow { // Required for NSBorderlessWindowMask windows + for (Map<DisplayServer::WindowID, DisplayServerOSX::WindowData>::Element *E = DS_OSX->windows.front(); E; E = E->next()) { + if (E->get().window_object == self) { + if (E->get().no_focus) { + return NO; + } + } + } + return YES; +} + +- (BOOL)canBecomeMainWindow { + // Required for NSBorderlessWindowMask windows + for (Map<DisplayServer::WindowID, DisplayServerOSX::WindowData>::Element *E = DS_OSX->windows.front(); E; E = E->next()) { + if (E->get().window_object == self) { + if (E->get().no_focus) { + return NO; + } + } + } return YES; } @@ -1553,6 +1579,7 @@ bool DisplayServerOSX::has_feature(Feature p_feature) const { case FEATURE_HIDPI: case FEATURE_ICON: case FEATURE_NATIVE_ICON: + //case FEATURE_KEEP_SCREEN_ON: case FEATURE_SWAP_BUFFERS: return true; default: { @@ -1998,8 +2025,9 @@ Error DisplayServerOSX::dialog_input_text(String p_title, String p_description, void DisplayServerOSX::mouse_set_mode(MouseMode p_mode) { _THREAD_SAFE_METHOD_ - if (p_mode == mouse_mode) + if (p_mode == mouse_mode) { return; + } if (p_mode == MOUSE_MODE_CAPTURED) { // Apple Docs state that the display parameter is not used. @@ -2040,8 +2068,8 @@ void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) { //local point in window coords const NSRect contentRect = [wd.window_view frame]; - float displayScale = _display_scale([wd.window_object screen]); - NSRect pointInWindowRect = NSMakeRect(p_to.x / displayScale, contentRect.size.height - (p_to.y / displayScale) - 1, 0, 0); + const float scale = screen_get_max_scale(); + NSRect pointInWindowRect = NSMakeRect(p_to.x / scale, contentRect.size.height - (p_to.y / scale - 1), 0, 0); NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin; //point in scren coords @@ -2066,11 +2094,12 @@ Point2i DisplayServerOSX::mouse_get_absolute_position() const { _THREAD_SAFE_METHOD_ const NSPoint mouse_pos = [NSEvent mouseLocation]; + const float scale = screen_get_max_scale(); for (NSScreen *screen in [NSScreen screens]) { NSRect frame = [screen frame]; if (NSMouseInRect(mouse_pos, frame, NO)) { - return Vector2i((int)mouse_pos.x, (int)-mouse_pos.y) + _get_screens_origin(); + return Vector2i((int)mouse_pos.x, (int)-mouse_pos.y) * scale + _get_screens_origin(); } } return Vector2i(); @@ -2128,17 +2157,10 @@ int DisplayServerOSX::get_screen_count() const { // to convert between OS X native screen coordinates and the ones expected by Godot static bool displays_arrangement_dirty = true; +static bool displays_scale_dirty = true; static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) { displays_arrangement_dirty = true; -} - -float DisplayServerOSX::_display_scale(id screen) const { - if (OS_OSX::get_singleton()->is_hidpi_allowed()) { - if ([screen respondsToSelector:@selector(backingScaleFactor)]) { - return fmax(1.0, [screen backingScaleFactor]); - } - } - return 1.0; + displays_scale_dirty = true; } Point2i DisplayServerOSX::_get_screens_origin() const { @@ -2165,10 +2187,9 @@ Point2i DisplayServerOSX::_get_screens_origin() const { Point2i DisplayServerOSX::_get_native_screen_position(int p_screen) const { NSArray *screenArray = [NSScreen screens]; if ((NSUInteger)p_screen < [screenArray count]) { - float display_scale = _display_scale([screenArray objectAtIndex:p_screen]); NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; // Return the top-left corner of the screen, for OS X the y starts at the bottom - return Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * display_scale; + return Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * screen_get_max_scale(); } return Point2i(); @@ -2197,10 +2218,9 @@ Size2i DisplayServerOSX::screen_get_size(int p_screen) const { NSArray *screenArray = [NSScreen screens]; if ((NSUInteger)p_screen < [screenArray count]) { - float displayScale = _display_scale([screenArray objectAtIndex:p_screen]); // Note: Use frame to get the whole screen size NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; - return Size2i(nsrect.size.width, nsrect.size.height) * displayScale; + return Size2i(nsrect.size.width, nsrect.size.height) * screen_get_max_scale(); } return Size2i(); @@ -2215,13 +2235,9 @@ int DisplayServerOSX::screen_get_dpi(int p_screen) const { NSArray *screenArray = [NSScreen screens]; if ((NSUInteger)p_screen < [screenArray count]) { - float displayScale = _display_scale([screenArray objectAtIndex:p_screen]); NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription]; - NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; - CGSize displayPhysicalSize = CGDisplayScreenSize( - [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); - - return (displayPixelSize.width * 25.4f / displayPhysicalSize.width) * displayScale; + NSSize displayDPI = [[description objectForKey:NSDeviceResolution] sizeValue]; + return (displayDPI.width + displayDPI.height) / 2; } return 96; @@ -2233,14 +2249,32 @@ float DisplayServerOSX::screen_get_scale(int p_screen) const { if (p_screen == SCREEN_OF_MAIN_WINDOW) { p_screen = window_get_current_screen(); } - NSArray *screenArray = [NSScreen screens]; - if ((NSUInteger)p_screen < [screenArray count]) { - return _display_scale([screenArray objectAtIndex:p_screen]); + if (OS::get_singleton()->is_hidpi_allowed()) { + NSArray *screenArray = [NSScreen screens]; + if ((NSUInteger)p_screen < [screenArray count]) { + if ([[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) { + return fmax(1.0, [[screenArray objectAtIndex:p_screen] backingScaleFactor]); + } + } } return 1.f; } +float DisplayServerOSX::screen_get_max_scale() const { + _THREAD_SAFE_METHOD_ + + static float scale = 1.f; + if (displays_scale_dirty) { + int screen_count = get_screen_count(); + for (int i = 0; i < screen_count; i++) { + scale = fmax(scale, screen_get_scale(i)); + } + displays_scale_dirty = false; + } + return scale; +} + Rect2i DisplayServerOSX::screen_get_usable_rect(int p_screen) const { _THREAD_SAFE_METHOD_ @@ -2250,12 +2284,12 @@ Rect2i DisplayServerOSX::screen_get_usable_rect(int p_screen) const { NSArray *screenArray = [NSScreen screens]; if ((NSUInteger)p_screen < [screenArray count]) { - float displayScale = _display_scale([screenArray objectAtIndex:p_screen]); + const float scale = screen_get_max_scale(); NSRect nsrect = [[screenArray objectAtIndex:p_screen] visibleFrame]; - Point2i position = Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * displayScale - _get_screens_origin(); + Point2i position = Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * scale - _get_screens_origin(); position.y *= -1; - Size2i size = Size2i(nsrect.size.width, nsrect.size.height) * displayScale; + Size2i size = Size2i(nsrect.size.width, nsrect.size.height) * scale; return Rect2i(position, size); } @@ -2283,7 +2317,11 @@ DisplayServer::WindowID DisplayServerOSX::create_sub_window(WindowMode p_mode, u window_set_flag(WindowFlags(i), true, id); } } - [wd.window_object makeKeyAndOrderFront:nil]; + if (wd.no_focus) { + [wd.window_object orderFront:nil]; + } else { + [wd.window_object makeKeyAndOrderFront:nil]; + } return id; } @@ -2301,8 +2339,9 @@ void DisplayServerOSX::_send_window_event(const WindowData &wd, WindowEvent p_ev DisplayServerOSX::WindowID DisplayServerOSX::_find_window_id(id p_window) { for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { - if (E->get().window_object == p_window) + if (E->get().window_object == p_window) { return E->key(); + } } return INVALID_WINDOW_ID; } @@ -2447,11 +2486,12 @@ Point2i DisplayServerOSX::window_get_position(WindowID p_window) const { NSRect nsrect = [wd.window_object frame]; Point2i pos; - float display_scale = _display_scale([wd.window_object screen]); // Return the position of the top-left corner, for OS X the y starts at the bottom - pos.x = nsrect.origin.x * display_scale; - pos.y = (nsrect.origin.y + nsrect.size.height) * display_scale; + const float scale = screen_get_max_scale(); + pos.x = nsrect.origin.x; + pos.y = (nsrect.origin.y + nsrect.size.height); + pos *= scale; pos -= _get_screens_origin(); // OS X native y-coordinate relative to _get_screens_origin() is negative, // Godot expects a positive value @@ -2470,17 +2510,12 @@ void DisplayServerOSX::window_set_position(const Point2i &p_position, WindowID p // Godot passes a positive value position.y *= -1; position += _get_screens_origin(); + position /= screen_get_max_scale(); - NSPoint pos; - float displayScale = _display_scale([wd.window_object screen]); - - pos.x = position.x / displayScale; - pos.y = position.y / displayScale; - - [wd.window_object setFrameTopLeftPoint:pos]; + [wd.window_object setFrameTopLeftPoint:NSMakePoint(position.x, position.y)]; _update_window(wd); - _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream], displayScale); + _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); } void DisplayServerOSX::window_set_max_size(const Size2i p_size, WindowID p_window) { @@ -2496,7 +2531,7 @@ void DisplayServerOSX::window_set_max_size(const Size2i p_size, WindowID p_windo wd.max_size = p_size; if ((wd.max_size != Size2i()) && !wd.fullscreen) { - Size2i size = wd.max_size / _display_scale([wd.window_object screen]); + Size2i size = wd.max_size / screen_get_max_scale(); [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)]; } else { [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; @@ -2524,7 +2559,7 @@ void DisplayServerOSX::window_set_min_size(const Size2i p_size, WindowID p_windo wd.min_size = p_size; if ((wd.min_size != Size2i()) && !wd.fullscreen) { - Size2i size = wd.min_size / _display_scale([wd.window_object screen]); + Size2i size = wd.min_size / screen_get_max_scale(); [wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)]; } else { [wd.window_object setContentMinSize:NSMakeSize(0, 0)]; @@ -2546,7 +2581,7 @@ void DisplayServerOSX::window_set_size(const Size2i p_size, WindowID p_window) { ERR_FAIL_COND(!windows.has(p_window)); WindowData &wd = windows[p_window]; - Size2i size = p_size / _display_scale([wd.window_object screen]); + Size2i size = p_size / screen_get_max_scale(); if (!wd.borderless) { // NSRect used by setFrame includes the title bar, so add it to our size.y @@ -2576,7 +2611,7 @@ Size2i DisplayServerOSX::window_get_real_size(WindowID p_window) const { ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); const WindowData &wd = windows[p_window]; NSRect frame = [wd.window_object frame]; - return Size2i(frame.size.width, frame.size.height) * _display_scale([wd.window_object screen]); + return Size2i(frame.size.width, frame.size.height) * screen_get_max_scale(); } bool DisplayServerOSX::window_is_maximize_allowed(WindowID p_window) const { @@ -2587,24 +2622,26 @@ void DisplayServerOSX::_set_window_per_pixel_transparency_enabled(bool p_enabled ERR_FAIL_COND(!windows.has(p_window)); WindowData &wd = windows[p_window]; - if (!OS_OSX::get_singleton()->is_layered_allowed()) + if (!OS_OSX::get_singleton()->is_layered_allowed()) { return; + } if (wd.layered_window != p_enabled) { if (p_enabled) { [wd.window_object setBackgroundColor:[NSColor clearColor]]; [wd.window_object setOpaque:NO]; [wd.window_object setHasShadow:NO]; + CALayer *layer = [wd.window_view layer]; + if (layer) { + [layer setOpaque:NO]; + } #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { - CALayer *layer = [wd.window_view layer]; - [layer setOpaque:NO]; //TODO - implement transparency for Vulkan } #endif #if defined(OPENGL_ENABLED) if (rendering_driver == "opengl_es") { //TODO - reimplement OpenGLES - wd.context_gles2->set_opacity(0); } #endif wd.layered_window = true; @@ -2612,17 +2649,18 @@ void DisplayServerOSX::_set_window_per_pixel_transparency_enabled(bool p_enabled [wd.window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]]; [wd.window_object setOpaque:YES]; [wd.window_object setHasShadow:YES]; + CALayer *layer = [wd.window_view layer]; + if (layer) { + [layer setOpaque:YES]; + } #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { - CALayer *layer = [wd.window_view layer]; - [layer setOpaque:YES]; //TODO - implement transparency for Vulkan } #endif #if defined(OPENGL_ENABLED) if (rendering_driver == "opengl_es") { //TODO - reimplement OpenGLES - wd.context_gles2->set_opacity(1); } #endif wd.layered_window = false; @@ -2630,7 +2668,11 @@ void DisplayServerOSX::_set_window_per_pixel_transparency_enabled(bool p_enabled #if defined(OPENGL_ENABLED) if (rendering_driver == "opengl_es") { //TODO - reimplement OpenGLES - wd.context_gles2->update(); + } +#endif +#if defined(VULKAN_ENABLED) + if (rendering_driver == "vulkan") { + //TODO - implement transparency for Vulkan } #endif NSRect frameRect = [wd.window_object frame]; @@ -2658,16 +2700,18 @@ void DisplayServerOSX::window_set_mode(WindowMode p_mode, WindowID p_window) { [wd.window_object deminiaturize:nil]; } break; case WINDOW_MODE_FULLSCREEN: { - if (wd.layered_window) + if (wd.layered_window) { _set_window_per_pixel_transparency_enabled(true, p_window); - if (wd.resize_disabled) //restore resize disabled + } + if (wd.resize_disabled) { //restore resize disabled [wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable]; + } if (wd.min_size != Size2i()) { - Size2i size = wd.min_size / _display_scale([wd.window_object screen]); + Size2i size = wd.min_size / screen_get_max_scale(); [wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)]; } if (wd.max_size != Size2i()) { - Size2i size = wd.max_size / _display_scale([wd.window_object screen]); + Size2i size = wd.max_size / screen_get_max_scale(); [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)]; } [wd.window_object toggleFullScreen:nil]; @@ -2736,8 +2780,9 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo switch (p_flag) { case WINDOW_FLAG_RESIZE_DISABLED: { wd.resize_disabled = p_enabled; - if (wd.fullscreen) //fullscreen window should be resizable, style will be applyed on exiting fs + if (wd.fullscreen) { //fullscreen window should be resizable, style will be applyed on exiting fs return; + } if (p_enabled) { [wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable]; } else { @@ -2778,7 +2823,9 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo [wd.window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (wd.resize_disabled ? 0 : NSWindowStyleMaskResizable)]; } _set_window_per_pixel_transparency_enabled(p_enabled, p_window); - + } break; + case WINDOW_FLAG_NO_FOCUS: { + wd.no_focus = p_enabled; } break; default: { } @@ -2804,6 +2851,9 @@ bool DisplayServerOSX::window_get_flag(WindowFlags p_flag, WindowID p_window) co case WINDOW_FLAG_TRANSPARENT: { return wd.layered_window; } break; + case WINDOW_FLAG_NO_FOCUS: { + return wd.no_focus; + } break; default: { } } @@ -2849,8 +2899,9 @@ void DisplayServerOSX::window_set_ime_active(const bool p_active, WindowID p_win wd.im_active = p_active; - if (!p_active) + if (!p_active) { [wd.window_view cancelComposition]; + } } void DisplayServerOSX::window_set_ime_position(const Point2i &p_pos, WindowID p_window) { @@ -2871,8 +2922,9 @@ void DisplayServerOSX::cursor_set_shape(CursorShape p_shape) { ERR_FAIL_INDEX(p_shape, CURSOR_MAX); - if (cursor_shape == p_shape) + if (cursor_shape == p_shape) { return; + } if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) { cursor_shape = p_shape; @@ -3266,8 +3318,9 @@ void DisplayServerOSX::process_events() { inMode:NSDefaultRunLoopMode dequeue:YES]; - if (event == nil) + if (event == nil) { break; + } [NSApp sendEvent:event]; } @@ -3355,11 +3408,11 @@ void DisplayServerOSX::set_icon(const Ref<Image> &p_icon) { Vector<String> DisplayServerOSX::get_rendering_drivers_func() { Vector<String> drivers; -#ifdef VULKAN_ENABLED +#if defined(VULKAN_ENABLED) drivers.push_back("vulkan"); #endif -#ifdef OPENGL_ENABLED - drivers.push_back("opengl"); +#if defined(OPENGL_ENABLED) + drivers.push_back("opengl_es"); #endif return drivers; @@ -3374,11 +3427,14 @@ String DisplayServerOSX::ime_get_text() const { } DisplayServer::WindowID DisplayServerOSX::get_window_at_screen_position(const Point2i &p_position) const { -#warning This is an incorrect implementation, if windows overlap, it should return the topmost visible one or none if occluded by a foreign window + Point2i position = p_position; + position.y *= -1; + position += _get_screens_origin(); + position /= screen_get_max_scale(); + NSInteger wnum = [NSWindow windowNumberAtPoint:NSMakePoint(position.x, position.y) belowWindowWithWindowNumber:0 /*topmost*/]; for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { - Rect2i win_rect = Rect2i(window_get_position(E->key()), window_get_size(E->key())); - if (win_rect.has_point(p_position)) { + if ([E->get().window_object windowNumber] == wnum) { return E->key(); } } @@ -3405,18 +3461,10 @@ DisplayServer *DisplayServerOSX::create_func(const String &p_rendering_driver, W DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, const Rect2i &p_rect) { WindowID id; + const float scale = screen_get_max_scale(); { WindowData wd; - float displayScale = 1.0; - if (OS_OSX::get_singleton()->is_hidpi_allowed()) { - // note that mainScreen is not screen #0 but the one with the keyboard focus. - NSScreen *screen = [NSScreen mainScreen]; - if ([screen respondsToSelector:@selector(backingScaleFactor)]) { - displayScale = fmax(displayScale, [screen backingScaleFactor]); - } - } - wd.window_delegate = [[GodotWindowDelegate alloc] init]; ERR_FAIL_COND_V_MSG(wd.window_delegate == nil, INVALID_WINDOW_ID, "Can't create a window delegate"); [wd.window_delegate setWindowID:window_id_counter]; @@ -3429,7 +3477,7 @@ DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, c // initWithContentRect uses bottom-left corner of the window’s frame as origin. wd.window_object = [[GodotWindow alloc] - initWithContentRect:NSMakeRect(position.x / displayScale, (position.y - p_rect.size.height) / displayScale, p_rect.size.width / displayScale, p_rect.size.height / displayScale) + initWithContentRect:NSMakeRect(position.x / scale, (position.y - p_rect.size.height) / scale, p_rect.size.width / scale, p_rect.size.height / scale) styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO]; @@ -3442,54 +3490,32 @@ DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, c [wd.window_view setWantsLayer:TRUE]; } - if (displayScale > 1.0) { -#if defined(OPENGL_ENABLED) - if (rendering_driver == "opengl_es") { - [wd.window_view setWantsBestResolutionOpenGLSurface:YES]; - } -#endif - [wd.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; - } else { -#if defined(OPENGL_ENABLED) - if (rendering_driver == "opengl_es") { - [wd.window_view setWantsBestResolutionOpenGLSurface:NO]; - } -#endif - } - [wd.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; [wd.window_object setContentView:wd.window_view]; [wd.window_object setDelegate:wd.window_delegate]; [wd.window_object setAcceptsMouseMovedEvents:YES]; [wd.window_object setRestorable:NO]; - if ([wd.window_object respondsToSelector:@selector(setTabbingMode:)]) + if ([wd.window_object respondsToSelector:@selector(setTabbingMode:)]) { [wd.window_object setTabbingMode:NSWindowTabbingModeDisallowed]; + } + + CALayer *layer = [wd.window_view layer]; + if (layer) { + layer.contentsScale = scale; + } #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { if (context_vulkan) { - CALayer *layer = [wd.window_view layer]; - layer.contentsScale = displayScale; Error err = context_vulkan->window_create(window_id_counter, wd.window_view, p_rect.size.width, p_rect.size.height); ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan context"); } } #endif -#ifdef OPENGL_ENABLED +#if defined(OPENGL_ENABLED) if (rendering_driver == "opengl_es") { //TODO - reimplement OpenGLES - wd.context_gles2 = memnew(ContextGL_OSX(wd.window_view, false)); - - if (wd.context_gles2->initialize() != OK) { - memdelete(wd.context_gles2); - ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a OpenGL context"); - } - - //if (RasterizerGLES2::is_viable() == OK) { - // RasterizerGLES2::register_config(); - // RasterizerGLES2::make_current(); - //} } #endif id = window_id_counter++; @@ -3499,25 +3525,22 @@ DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, c WindowData &wd = windows[id]; window_set_mode(p_mode, id); - float displayScale = _display_scale([wd.window_object screen]); const NSRect contentRect = [wd.window_view frame]; - wd.size.width = contentRect.size.width * displayScale; - wd.size.height = contentRect.size.height * displayScale; + wd.size.width = contentRect.size.width * scale; + wd.size.height = contentRect.size.height * scale; + + CALayer *layer = [wd.window_view layer]; + if (layer) { + layer.contentsScale = scale; + } #if defined(OPENGL_ENABLED) if (rendering_driver == "opengl_es") { - if (OS_OSX::singleton->is_hidpi_allowed()) { - [wd.window_view setWantsBestResolutionOpenGLSurface:YES]; - } else { - [wd.window_view setWantsBestResolutionOpenGLSurface:NO]; - } - wd.context_gles2->update(); + //TODO - reimplement OpenGLES } #endif #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { - CALayer *layer = [wd.window_view layer]; - layer.contentsScale = displayScale; context_vulkan->window_resize(id, wd.size.width, wd.size.height); } #endif @@ -3612,6 +3635,7 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode keyboard_layout_dirty = true; displays_arrangement_dirty = true; + displays_scale_dirty = true; // Register to be notified on keyboard layout changes CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), @@ -3629,8 +3653,9 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode NSString *title; NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; - if (nsappname == nil) + if (nsappname == nil) { nsappname = [[NSProcessInfo processInfo] processName]; + } // Setup Dock menu dock_menu = [[NSMenu alloc] initWithTitle:@"_dock"]; @@ -3683,8 +3708,9 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode inMode:NSDefaultRunLoopMode dequeue:YES]; - if (event == nil) + if (event == nil) { break; + } [NSApp sendEvent:event]; } @@ -3716,8 +3742,8 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode #endif Point2i window_position( - (screen_get_size(0).width - p_resolution.width) / 2, - (screen_get_size(0).height - p_resolution.height) / 2); + screen_get_position(0).x + (screen_get_size(0).width - p_resolution.width) / 2, + screen_get_position(0).y + (screen_get_size(0).height - p_resolution.height) / 2); WindowID main_window = _create_window(p_mode, Rect2i(window_position, p_resolution)); for (int i = 0; i < WINDOW_FLAG_MAX; i++) { if (p_flags & (1 << i)) { @@ -3726,6 +3752,11 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode } [windows[main_window].window_object makeKeyAndOrderFront:nil]; +#if defined(OPENGL_ENABLED) + if (rendering_driver == "opengl_es") { + //TODO - reimplement OpenGLES + } +#endif #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { rendering_device_vulkan = memnew(RenderingDeviceVulkan); @@ -3736,14 +3767,6 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode #endif [NSApp activateIgnoringOtherApps:YES]; - - /* - visual_server = memnew(VisualServerRaster); - if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { - visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD)); - } - visual_server->init(); - */ } DisplayServerOSX::~DisplayServerOSX() { @@ -3762,6 +3785,11 @@ DisplayServerOSX::~DisplayServerOSX() { } //destroy drivers +#if defined(OPENGL_ENABLED) + if (rendering_driver == "opengl_es") { + //TODO - reimplement OpenGLES + } +#endif #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { if (rendering_device_vulkan) { @@ -3769,8 +3797,9 @@ DisplayServerOSX::~DisplayServerOSX() { memdelete(rendering_device_vulkan); } - if (context_vulkan) + if (context_vulkan) { memdelete(context_vulkan); + } } #endif @@ -3778,9 +3807,6 @@ DisplayServerOSX::~DisplayServerOSX() { CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL); cursors_cache.clear(); - - //visual_server->finish(); - //memdelete(visual_server); } void DisplayServerOSX::register_osx_driver() { diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index dba96ccfcd..4ca89ff4b2 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -272,7 +272,13 @@ String OS_OSX::get_system_dir(SystemDir p_dir) const { } Error OS_OSX::shell_open(String p_uri) { - [[NSWorkspace sharedWorkspace] openURL:[[NSURL alloc] initWithString:[[NSString stringWithUTF8String:p_uri.utf8().get_data()] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]]]]; + NSString *string = [NSString stringWithUTF8String:p_uri.utf8().get_data()]; + NSURL *uri = [[NSURL alloc] initWithString:string]; + // Escape special characters in filenames + if (!uri || !uri.scheme || [uri.scheme isEqual:@"file"]) { + uri = [[NSURL alloc] initWithString:[string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]]]; + } + [[NSWorkspace sharedWorkspace] openURL:uri]; return OK; } diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 48540b7bc9..ae30972558 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -338,6 +338,7 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) { emit_signal("go_back_requested"); } break; case DisplayServer::WINDOW_EVENT_DPI_CHANGE: { + _update_viewport_size(); _propagate_window_notification(this, NOTIFICATION_WM_DPI_CHANGE); emit_signal("dpi_changed"); } break; diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 83d4db7bae..b0c01da2b0 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -181,7 +181,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("disabled", "Button", sb_button_disabled); theme->set_stylebox("focus", "Button", sb_button_focus); - theme->set_font("font", "Button", default_font); + theme->set_font("font", "Button", Ref<Font>()); theme->set_color("font_color", "Button", control_font_color); theme->set_color("font_color_pressed", "Button", control_font_color_pressed); @@ -194,7 +194,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("focus", "LinkButton", focus); - theme->set_font("font", "LinkButton", default_font); + theme->set_font("font", "LinkButton", Ref<Font>()); theme->set_color("font_color", "LinkButton", control_font_color); theme->set_color("font_color_pressed", "LinkButton", control_font_color_pressed); @@ -210,7 +210,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("disabled", "ColorPickerButton", sb_button_disabled); theme->set_stylebox("focus", "ColorPickerButton", sb_button_focus); - theme->set_font("font", "ColorPickerButton", default_font); + theme->set_font("font", "ColorPickerButton", Ref<Font>()); theme->set_color("font_color", "ColorPickerButton", Color(1, 1, 1, 1)); theme->set_color("font_color_pressed", "ColorPickerButton", Color(0.8, 0.8, 0.8, 1)); @@ -235,7 +235,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("arrow", "OptionButton", make_icon(option_arrow_png)); - theme->set_font("font", "OptionButton", default_font); + theme->set_font("font", "OptionButton", Ref<Font>()); theme->set_color("font_color", "OptionButton", control_font_color); theme->set_color("font_color_pressed", "OptionButton", control_font_color_pressed); @@ -253,7 +253,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("disabled", "MenuButton", sb_button_disabled); theme->set_stylebox("focus", "MenuButton", sb_button_focus); - theme->set_font("font", "MenuButton", default_font); + theme->set_font("font", "MenuButton", Ref<Font>()); theme->set_color("font_color", "MenuButton", control_font_color); theme->set_color("font_color_pressed", "MenuButton", control_font_color_pressed); @@ -287,7 +287,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("radio_checked", "CheckBox", make_icon(radio_checked_png)); theme->set_icon("radio_unchecked", "CheckBox", make_icon(radio_unchecked_png)); - theme->set_font("font", "CheckBox", default_font); + theme->set_font("font", "CheckBox", Ref<Font>()); theme->set_color("font_color", "CheckBox", control_font_color); theme->set_color("font_color_pressed", "CheckBox", control_font_color_pressed); @@ -318,7 +318,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("off", "CheckButton", make_icon(toggle_off_png)); theme->set_icon("off_disabled", "CheckButton", make_icon(toggle_off_disabled_png)); - theme->set_font("font", "CheckButton", default_font); + theme->set_font("font", "CheckButton", Ref<Font>()); theme->set_color("font_color", "CheckButton", control_font_color); theme->set_color("font_color_pressed", "CheckButton", control_font_color_pressed); @@ -332,7 +332,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const // Label theme->set_stylebox("normal", "Label", memnew(StyleBoxEmpty)); - theme->set_font("font", "Label", default_font); + theme->set_font("font", "Label", Ref<Font>()); theme->set_color("font_color", "Label", Color(1, 1, 1)); theme->set_color("font_color_shadow", "Label", Color(0, 0, 0, 0)); @@ -349,7 +349,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("focus", "LineEdit", focus); theme->set_stylebox("read_only", "LineEdit", make_stylebox(line_edit_disabled_png, 6, 6, 6, 6)); - theme->set_font("font", "LineEdit", default_font); + theme->set_font("font", "LineEdit", Ref<Font>()); theme->set_color("font_color", "LineEdit", control_font_color); theme->set_color("font_color_selected", "LineEdit", Color(0, 0, 0)); @@ -368,7 +368,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("bg", "ProgressBar", make_stylebox(progress_bar_png, 4, 4, 4, 4, 0, 0, 0, 0)); theme->set_stylebox("fg", "ProgressBar", make_stylebox(progress_fill_png, 6, 6, 6, 6, 2, 1, 2, 1)); - theme->set_font("font", "ProgressBar", default_font); + theme->set_font("font", "ProgressBar", Ref<Font>()); theme->set_color("font_color", "ProgressBar", control_font_color_hover); theme->set_color("font_color_shadow", "ProgressBar", Color(0, 0, 0)); @@ -385,7 +385,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("folded", "TextEdit", make_icon(arrow_right_png)); theme->set_icon("fold", "TextEdit", make_icon(arrow_down_png)); - theme->set_font("font", "TextEdit", default_font); + theme->set_font("font", "TextEdit", Ref<Font>()); theme->set_color("background_color", "TextEdit", Color(0, 0, 0, 0)); theme->set_color("completion_background_color", "TextEdit", Color(0.17, 0.16, 0.2)); @@ -531,7 +531,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("radio_unchecked", "PopupMenu", make_icon(radio_unchecked_png)); theme->set_icon("submenu", "PopupMenu", make_icon(submenu_png)); - theme->set_font("font", "PopupMenu", default_font); + theme->set_font("font", "PopupMenu", Ref<Font>()); theme->set_color("font_color", "PopupMenu", control_font_color); theme->set_color("font_color_accel", "PopupMenu", Color(0.7, 0.7, 0.7, 0.8)); @@ -566,7 +566,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("port", "GraphNode", make_icon(graph_port_png)); theme->set_icon("close", "GraphNode", make_icon(graph_node_close_png)); theme->set_icon("resizer", "GraphNode", make_icon(window_resizer_png)); - theme->set_font("title_font", "GraphNode", default_font); + theme->set_font("title_font", "GraphNode", Ref<Font>()); theme->set_color("title_color", "GraphNode", Color(0, 0, 0, 1)); theme->set_color("close_color", "GraphNode", Color(0, 0, 0, 1)); theme->set_color("resizer_color", "GraphNode", Color(0, 0, 0, 1)); @@ -600,8 +600,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("arrow", "Tree", make_icon(arrow_down_png)); theme->set_icon("arrow_collapsed", "Tree", make_icon(arrow_right_png)); - theme->set_font("title_button_font", "Tree", default_font); - theme->set_font("font", "Tree", default_font); + theme->set_font("title_button_font", "Tree", Ref<Font>()); + theme->set_font("font", "Tree", Ref<Font>()); theme->set_color("title_button_color", "Tree", control_font_color); theme->set_color("font_color", "Tree", control_font_color_low); @@ -630,7 +630,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("vseparation", "ItemList", 2); theme->set_constant("icon_margin", "ItemList", 4); theme->set_constant("line_separation", "ItemList", 2 * scale); - theme->set_font("font", "ItemList", default_font); + theme->set_font("font", "ItemList", Ref<Font>()); theme->set_color("font_color", "ItemList", control_font_color_lower); theme->set_color("font_color_selected", "ItemList", control_font_color_pressed); theme->set_color("guide_color", "ItemList", Color(0, 0, 0, 0.1)); @@ -658,7 +658,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("menu", "TabContainer", make_icon(tab_menu_png)); theme->set_icon("menu_highlight", "TabContainer", make_icon(tab_menu_hl_png)); - theme->set_font("font", "TabContainer", default_font); + theme->set_font("font", "TabContainer", Ref<Font>()); theme->set_color("font_color_fg", "TabContainer", control_font_color_hover); theme->set_color("font_color_bg", "TabContainer", control_font_color_low); @@ -682,7 +682,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("decrement_highlight", "Tabs", make_icon(scroll_button_left_hl_png)); theme->set_icon("close", "Tabs", make_icon(tab_close_png)); - theme->set_font("font", "Tabs", default_font); + theme->set_font("font", "Tabs", Ref<Font>()); theme->set_color("font_color_fg", "Tabs", control_font_color_hover); theme->set_color("font_color_bg", "Tabs", control_font_color_low); @@ -696,7 +696,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("separator", "VSeparator", make_stylebox(hseparator_png, 3, 3, 3, 3)); theme->set_icon("close", "Icons", make_icon(icon_close_png)); - theme->set_font("normal", "Fonts", default_font); + theme->set_font("normal", "Fonts", Ref<Font>()); theme->set_font("large", "Fonts", large_font); theme->set_constant("separation", "HSeparator", 4 * scale); @@ -741,7 +741,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("panel", "TooltipPanel", style_tt); - theme->set_font("font", "TooltipLabel", default_font); + theme->set_font("font", "TooltipLabel", Ref<Font>()); theme->set_color("font_color", "TooltipLabel", Color(0, 0, 0)); theme->set_color("font_color_shadow", "TooltipLabel", Color(0, 0, 0, 0.1)); @@ -754,11 +754,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("focus", "RichTextLabel", focus); theme->set_stylebox("normal", "RichTextLabel", make_empty_stylebox(0, 0, 0, 0)); - theme->set_font("normal_font", "RichTextLabel", default_font); - theme->set_font("bold_font", "RichTextLabel", default_font); - theme->set_font("italics_font", "RichTextLabel", default_font); - theme->set_font("bold_italics_font", "RichTextLabel", default_font); - theme->set_font("mono_font", "RichTextLabel", default_font); + theme->set_font("normal_font", "RichTextLabel", Ref<Font>()); + theme->set_font("bold_font", "RichTextLabel", Ref<Font>()); + theme->set_font("italics_font", "RichTextLabel", Ref<Font>()); + theme->set_font("bold_italics_font", "RichTextLabel", Ref<Font>()); + theme->set_font("mono_font", "RichTextLabel", Ref<Font>()); theme->set_color("default_color", "RichTextLabel", Color(1, 1, 1)); theme->set_color("font_color_selected", "RichTextLabel", font_color_selection); diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 83e0a797a9..72cfd87880 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -392,6 +392,7 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("screen_get_dpi", "screen"), &DisplayServer::screen_get_dpi, DEFVAL(SCREEN_OF_MAIN_WINDOW)); ClassDB::bind_method(D_METHOD("screen_get_scale", "screen"), &DisplayServer::screen_get_scale, DEFVAL(SCREEN_OF_MAIN_WINDOW)); ClassDB::bind_method(D_METHOD("screen_is_touchscreen", "screen"), &DisplayServer::screen_is_touchscreen, DEFVAL(SCREEN_OF_MAIN_WINDOW)); + ClassDB::bind_method(D_METHOD("screen_get_max_scale"), &DisplayServer::screen_get_max_scale); ClassDB::bind_method(D_METHOD("screen_set_orientation", "orientation", "screen"), &DisplayServer::screen_set_orientation, DEFVAL(SCREEN_OF_MAIN_WINDOW)); ClassDB::bind_method(D_METHOD("screen_get_orientation", "screen"), &DisplayServer::screen_get_orientation, DEFVAL(SCREEN_OF_MAIN_WINDOW)); diff --git a/servers/display_server.h b/servers/display_server.h index a4fcd29a4a..79f6f5d0fc 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -167,6 +167,14 @@ public: virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0; virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0; virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + virtual float screen_get_max_scale() const { + float scale = 1.f; + int screen_count = get_screen_count(); + for (int i = 0; i < screen_count; i++) { + scale = fmax(scale, screen_get_scale(i)); + } + return scale; + } virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const; enum ScreenOrientation { diff --git a/servers/physics_2d/area_2d_sw.cpp b/servers/physics_2d/area_2d_sw.cpp index acbbb7e1e8..7485f31afc 100644 --- a/servers/physics_2d/area_2d_sw.cpp +++ b/servers/physics_2d/area_2d_sw.cpp @@ -213,9 +213,10 @@ void Area2DSW::call_queries() { return; } - for (Map<BodyKey, BodyState>::Element *E = monitored_bodies.front(); E; E = E->next()) { - if (E->get().state == 0) { - continue; //nothing happened + for (Map<BodyKey, BodyState>::Element *E = monitored_bodies.front(); E;) { + if (E->get().state == 0) { // Nothing happened + E = E->next(); + continue; } res[0] = E->get().state > 0 ? PhysicsServer2D::AREA_BODY_ADDED : PhysicsServer2D::AREA_BODY_REMOVED; @@ -224,13 +225,15 @@ void Area2DSW::call_queries() { res[3] = E->key().body_shape; res[4] = E->key().area_shape; + Map<BodyKey, BodyState>::Element *next = E->next(); + monitored_bodies.erase(E); + E = next; + Callable::CallError ce; obj->call(monitor_callback_method, (const Variant **)resptr, 5, ce); } } - monitored_bodies.clear(); - if (area_monitor_callback_id.is_valid() && !monitored_areas.empty()) { Variant res[5]; Variant *resptr[5]; @@ -245,9 +248,10 @@ void Area2DSW::call_queries() { return; } - for (Map<BodyKey, BodyState>::Element *E = monitored_areas.front(); E; E = E->next()) { - if (E->get().state == 0) { - continue; //nothing happened + for (Map<BodyKey, BodyState>::Element *E = monitored_areas.front(); E;) { + if (E->get().state == 0) { // Nothing happened + E = E->next(); + continue; } res[0] = E->get().state > 0 ? PhysicsServer2D::AREA_BODY_ADDED : PhysicsServer2D::AREA_BODY_REMOVED; @@ -256,14 +260,14 @@ void Area2DSW::call_queries() { res[3] = E->key().body_shape; res[4] = E->key().area_shape; + Map<BodyKey, BodyState>::Element *next = E->next(); + monitored_areas.erase(E); + E = next; + Callable::CallError ce; obj->call(area_monitor_callback_method, (const Variant **)resptr, 5, ce); } } - - monitored_areas.clear(); - - //get_space()->area_remove_from_monitor_query_list(&monitor_query_list); } Area2DSW::Area2DSW() : diff --git a/servers/physics_3d/area_3d_sw.cpp b/servers/physics_3d/area_3d_sw.cpp index 98237dd91c..571f1435de 100644 --- a/servers/physics_3d/area_3d_sw.cpp +++ b/servers/physics_3d/area_3d_sw.cpp @@ -213,9 +213,10 @@ void Area3DSW::call_queries() { return; } - for (Map<BodyKey, BodyState>::Element *E = monitored_bodies.front(); E; E = E->next()) { - if (E->get().state == 0) { - continue; //nothing happened + for (Map<BodyKey, BodyState>::Element *E = monitored_bodies.front(); E;) { + if (E->get().state == 0) { // Nothing happened + E = E->next(); + continue; } res[0] = E->get().state > 0 ? PhysicsServer3D::AREA_BODY_ADDED : PhysicsServer3D::AREA_BODY_REMOVED; @@ -224,13 +225,15 @@ void Area3DSW::call_queries() { res[3] = E->key().body_shape; res[4] = E->key().area_shape; + Map<BodyKey, BodyState>::Element *next = E->next(); + monitored_bodies.erase(E); + E = next; + Callable::CallError ce; obj->call(monitor_callback_method, (const Variant **)resptr, 5, ce); } } - monitored_bodies.clear(); - if (area_monitor_callback_id.is_valid() && !monitored_areas.empty()) { Variant res[5]; Variant *resptr[5]; @@ -245,9 +248,10 @@ void Area3DSW::call_queries() { return; } - for (Map<BodyKey, BodyState>::Element *E = monitored_areas.front(); E; E = E->next()) { - if (E->get().state == 0) { - continue; //nothing happened + for (Map<BodyKey, BodyState>::Element *E = monitored_areas.front(); E;) { + if (E->get().state == 0) { // Nothing happened + E = E->next(); + continue; } res[0] = E->get().state > 0 ? PhysicsServer3D::AREA_BODY_ADDED : PhysicsServer3D::AREA_BODY_REMOVED; @@ -256,13 +260,14 @@ void Area3DSW::call_queries() { res[3] = E->key().body_shape; res[4] = E->key().area_shape; + Map<BodyKey, BodyState>::Element *next = E->next(); + monitored_areas.erase(E); + E = next; + Callable::CallError ce; obj->call(area_monitor_callback_method, (const Variant **)resptr, 5, ce); } } - - monitored_areas.clear(); - //get_space()->area_remove_from_monitor_query_list(&monitor_query_list); } Area3DSW::Area3DSW() : |