diff options
Diffstat (limited to 'editor')
45 files changed, 2284 insertions, 1903 deletions
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp new file mode 100644 index 0000000000..55640ca590 --- /dev/null +++ b/editor/action_map_editor.cpp @@ -0,0 +1,1167 @@ +/*************************************************************************/ +/* action_map_editor.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "action_map_editor.h" +#include "core/input/input_map.h" +#include "core/os/keyboard.h" +#include "editor/editor_scale.h" +#include "scene/gui/center_container.h" + +///////////////////////////////////////// + +// Maps to 2*axis if value is neg, or + 1 if value is pos. +static const char *_joy_axis_descriptions[JOY_AXIS_MAX * 2] = { + TTRC("Left Stick Left, Joystick 0 Left"), + TTRC("Left Stick Right, Joystick 0 Right"), + TTRC("Left Stick Up, Joystick 0 Up"), + TTRC("Left Stick Down, Joystick 0 Down"), + TTRC("Right Stick Left, Joystick 1 Left"), + TTRC("Right Stick Right, Joystick 1 Right"), + TTRC("Right Stick Up, Joystick 1 Up"), + TTRC("Right Stick Down, Joystick 1 Down"), + TTRC("Joystick 2 Left"), + TTRC("Left Trigger, Sony L2, Xbox LT, Joystick 2 Right"), + TTRC("Joystick 2 Up"), + TTRC("Right Trigger, Sony R2, Xbox RT, Joystick 2 Down"), + TTRC("Joystick 3 Left"), + TTRC("Joystick 3 Right"), + TTRC("Joystick 3 Up"), + TTRC("Joystick 3 Down"), + TTRC("Joystick 4 Left"), + TTRC("Joystick 4 Right"), + TTRC("Joystick 4 Up"), + TTRC("Joystick 4 Down"), +}; + +String InputEventConfigurationDialog::get_event_text(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND_V_MSG(p_event.is_null(), String(), "Provided event is not a valid instance of InputEvent"); + + // Joypad motion events will display slighlty differently than what the event->as_text() provides. See #43660. + Ref<InputEventJoypadMotion> jpmotion = p_event; + if (jpmotion.is_valid()) { + String desc = TTR("Unknown Joypad Axis"); + if (jpmotion->get_axis() < JOY_AXIS_MAX) { + desc = RTR(_joy_axis_descriptions[2 * jpmotion->get_axis() + (jpmotion->get_axis_value() < 0 ? 0 : 1)]); + } + + return vformat("Joypad Axis %s %s (%s)", itos(jpmotion->get_axis()), jpmotion->get_axis_value() < 0 ? "-" : "+", desc); + } else { + return p_event->as_text(); + } +} + +void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event) { + if (p_event.is_valid()) { + event = p_event; + + // Update Label + event_as_text->set_text(get_event_text(event)); + + Ref<InputEventKey> k = p_event; + Ref<InputEventMouseButton> mb = p_event; + Ref<InputEventJoypadButton> joyb = p_event; + Ref<InputEventJoypadMotion> joym = p_event; + Ref<InputEventWithModifiers> mod = p_event; + + // Update option values and visibility + bool show_mods = false; + bool show_device = false; + bool show_phys_key = false; + + if (mod.is_valid()) { + show_mods = true; + mod_checkboxes[MOD_ALT]->set_pressed(mod->get_alt()); + mod_checkboxes[MOD_SHIFT]->set_pressed(mod->get_shift()); + mod_checkboxes[MOD_COMMAND]->set_pressed(mod->get_command()); + mod_checkboxes[MOD_CONTROL]->set_pressed(mod->get_control()); + mod_checkboxes[MOD_META]->set_pressed(mod->get_metakey()); + + store_command_checkbox->set_pressed(mod->is_storing_command()); + } + + if (k.is_valid()) { + show_phys_key = true; + physical_key_checkbox->set_pressed(k->get_physical_keycode() != 0 && k->get_keycode() == 0); + + } else if (joyb.is_valid() || joym.is_valid() || mb.is_valid()) { + show_device = true; + _set_current_device(event->get_device()); + } + + mod_container->set_visible(show_mods); + device_container->set_visible(show_device); + physical_key_checkbox->set_visible(show_phys_key); + additional_options_container->show(); + + // Update selected item in input list for keys, joybuttons and joyaxis only (since the mouse cannot be "listened" for). + if (k.is_valid() || joyb.is_valid() || joym.is_valid()) { + TreeItem *category = input_list_tree->get_root()->get_children(); + while (category) { + TreeItem *input_item = category->get_children(); + + // has_type this should be always true, unless the tree structure has been misconfigured. + bool has_type = input_item->get_parent()->has_meta("__type"); + int input_type = input_item->get_parent()->get_meta("__type"); + if (!has_type) { + return; + } + + // If event type matches input types of this category. + if ((k.is_valid() && input_type == INPUT_KEY) || (joyb.is_valid() && input_type == INPUT_JOY_BUTTON) || (joym.is_valid() && input_type == INPUT_JOY_MOTION)) { + // Loop through all items of this category until one matches. + while (input_item) { + bool key_match = k.is_valid() && (Variant(k->get_keycode()) == input_item->get_meta("__keycode") || Variant(k->get_physical_keycode()) == input_item->get_meta("__keycode")); + bool joyb_match = joyb.is_valid() && Variant(joyb->get_button_index()) == input_item->get_meta("__index"); + bool joym_match = joym.is_valid() && Variant(joym->get_axis()) == input_item->get_meta("__axis") && joym->get_axis_value() == (float)input_item->get_meta("__value"); + if (key_match || joyb_match || joym_match) { + category->set_collapsed(false); + input_item->select(0); + input_list_tree->ensure_cursor_is_visible(); + return; + } + input_item = input_item->get_next(); + } + } + + category->set_collapsed(true); // Event not in this category, so collapse; + category = category->get_next(); + } + } + } else { + // Event is not valid, reset dialog + event = p_event; + Vector<String> strings; + + // Reset message, promp for input according to which input types are allowed. + String text = TTR("Perform an Input (%s)."); + + if (allowed_input_types & INPUT_KEY) { + strings.append(TTR("Key")); + } + // We don't check for INPUT_MOUSE_BUTTON since it is ignored in the "Listen Window Input" method. + + if (allowed_input_types & INPUT_JOY_BUTTON) { + strings.append(TTR("Joypad Button")); + } + if (allowed_input_types & INPUT_JOY_MOTION) { + strings.append(TTR("Joypad Axis")); + } + + if (strings.size() == 0) { + text = TTR("Input Event dialog has been misconfigured: No input types are allowed."); + event_as_text->set_text(text); + } else { + String insert_text = String(", ").join(strings); + event_as_text->set_text(vformat(text, insert_text)); + } + + additional_options_container->hide(); + input_list_tree->deselect_all(); + _update_input_list(); + } +} + +void InputEventConfigurationDialog::_tab_selected(int p_tab) { + Callable signal_method = callable_mp(this, &InputEventConfigurationDialog::_listen_window_input); + if (p_tab == 0) { + // Start Listening. + if (!is_connected("window_input", signal_method)) { + connect("window_input", signal_method); + } + } else { + // Stop Listening. + if (is_connected("window_input", signal_method)) { + disconnect("window_input", signal_method); + } + input_list_tree->call_deferred("ensure_cursor_is_visible"); + if (input_list_tree->get_selected() == nullptr) { + // If nothing selected, scroll to top. + input_list_tree->scroll_to_item(input_list_tree->get_root()); + } + } +} + +void InputEventConfigurationDialog::_listen_window_input(const Ref<InputEvent> &p_event) { + // Ignore if echo or not pressed + if (p_event->is_echo() || !p_event->is_pressed()) { + return; + } + + // Ignore mouse + Ref<InputEventMouse> m = p_event; + if (m.is_valid()) { + return; + } + + // Check what the type is and if it is allowed. + Ref<InputEventKey> k = p_event; + Ref<InputEventJoypadButton> joyb = p_event; + Ref<InputEventJoypadMotion> joym = p_event; + + int type = k.is_valid() ? INPUT_KEY : joyb.is_valid() ? INPUT_JOY_BUTTON : + joym.is_valid() ? INPUT_JOY_MOTION : + 0; + + if (!(allowed_input_types & type)) { + return; + } + + if (joym.is_valid()) { + float axis_value = joym->get_axis_value(); + if (ABS(axis_value) < 0.9) { + // Ignore motion below 0.9 magnitude to avoid accidental touches + return; + } else { + // Always make the value 1 or -1 for display consistency + joym->set_axis_value(SGN(axis_value)); + } + } + + if (k.is_valid()) { + k->set_pressed(false); // to avoid serialisation of 'pressed' property - doesn't matter for actions anyway. + // Maintain physical keycode option state + if (physical_key_checkbox->is_pressed()) { + k->set_physical_keycode(k->get_keycode()); + k->set_keycode(0); + } else { + k->set_keycode(k->get_physical_keycode()); + k->set_physical_keycode(0); + } + } + + Ref<InputEventWithModifiers> mod = p_event; + if (mod.is_valid()) { + // Maintain store command option state + mod->set_store_command(store_command_checkbox->is_pressed()); + + mod->set_window_id(0); + } + + _set_event(p_event); + set_input_as_handled(); +} + +void InputEventConfigurationDialog::_search_term_updated(const String &) { + _update_input_list(); +} + +void InputEventConfigurationDialog::_update_input_list() { + input_list_tree->clear(); + + TreeItem *root = input_list_tree->create_item(); + String search_term = input_list_search->get_text(); + + bool collapse = input_list_search->get_text().is_empty(); + + if (allowed_input_types & INPUT_KEY) { + TreeItem *kb_root = input_list_tree->create_item(root); + kb_root->set_text(0, TTR("Keyboard Keys")); + kb_root->set_icon(0, icon_cache.keyboard); + kb_root->set_collapsed(collapse); + kb_root->set_meta("__type", INPUT_KEY); + + for (int i = 0; i < keycode_get_count(); i++) { + String name = keycode_get_name_by_index(i); + + if (!search_term.is_empty() && name.findn(search_term) == -1) { + continue; + } + + TreeItem *item = input_list_tree->create_item(kb_root); + item->set_text(0, name); + item->set_meta("__keycode", keycode_get_value_by_index(i)); + } + } + + if (allowed_input_types & INPUT_MOUSE_BUTTON) { + TreeItem *mouse_root = input_list_tree->create_item(root); + mouse_root->set_text(0, TTR("Mouse Buttons")); + mouse_root->set_icon(0, icon_cache.mouse); + mouse_root->set_collapsed(collapse); + mouse_root->set_meta("__type", INPUT_MOUSE_BUTTON); + + int mouse_buttons[9] = { BUTTON_LEFT, BUTTON_RIGHT, BUTTON_MIDDLE, BUTTON_WHEEL_UP, BUTTON_WHEEL_DOWN, BUTTON_WHEEL_LEFT, BUTTON_WHEEL_RIGHT, BUTTON_XBUTTON1, BUTTON_XBUTTON2 }; + for (int i = 0; i < 9; i++) { + Ref<InputEventMouseButton> mb; + mb.instance(); + mb->set_button_index(mouse_buttons[i]); + String desc = get_event_text(mb); + + if (!search_term.is_empty() && desc.findn(search_term) == -1) { + continue; + } + + TreeItem *item = input_list_tree->create_item(mouse_root); + item->set_text(0, desc); + item->set_meta("__index", mouse_buttons[i]); + } + } + + if (allowed_input_types & INPUT_JOY_BUTTON) { + TreeItem *joyb_root = input_list_tree->create_item(root); + joyb_root->set_text(0, TTR("Joypad Buttons")); + joyb_root->set_icon(0, icon_cache.joypad_button); + joyb_root->set_collapsed(collapse); + joyb_root->set_meta("__type", INPUT_JOY_BUTTON); + + for (int i = 0; i < JOY_BUTTON_MAX; i++) { + Ref<InputEventJoypadButton> joyb; + joyb.instance(); + joyb->set_button_index(i); + String desc = get_event_text(joyb); + + if (!search_term.is_empty() && desc.findn(search_term) == -1) { + continue; + } + + TreeItem *item = input_list_tree->create_item(joyb_root); + item->set_text(0, desc); + item->set_meta("__index", i); + } + } + + if (allowed_input_types & INPUT_JOY_MOTION) { + TreeItem *joya_root = input_list_tree->create_item(root); + joya_root->set_text(0, TTR("Joypad Axes")); + joya_root->set_icon(0, icon_cache.joypad_axis); + joya_root->set_collapsed(collapse); + joya_root->set_meta("__type", INPUT_JOY_MOTION); + + for (int i = 0; i < JOY_AXIS_MAX * 2; i++) { + int axis = i / 2; + int direction = (i & 1) ? 1 : -1; + Ref<InputEventJoypadMotion> joym; + joym.instance(); + joym->set_axis(axis); + joym->set_axis_value(direction); + String desc = get_event_text(joym); + + if (!search_term.is_empty() && desc.findn(search_term) == -1) { + continue; + } + + TreeItem *item = input_list_tree->create_item(joya_root); + item->set_text(0, desc); + item->set_meta("__axis", i >> 1); + item->set_meta("__value", (i & 1) ? 1 : -1); + } + } +} + +void InputEventConfigurationDialog::_mod_toggled(bool p_checked, int p_index) { + Ref<InputEventWithModifiers> ie = event; + + // Not event with modifiers + if (ie.is_null()) { + return; + } + + if (p_index == 0) { + ie->set_alt(p_checked); + } else if (p_index == 1) { + ie->set_shift(p_checked); + } else if (p_index == 2) { + ie->set_command(p_checked); + } else if (p_index == 3) { + ie->set_control(p_checked); + } else if (p_index == 4) { + ie->set_metakey(p_checked); + } + + _set_event(ie); +} + +void InputEventConfigurationDialog::_store_command_toggled(bool p_checked) { + Ref<InputEventWithModifiers> ie = event; + if (ie.is_valid()) { + ie->set_store_command(p_checked); + _set_event(ie); + } + + if (p_checked) { + // If storing Command, show it's checkbox and hide Control (Win/Lin) or Meta (Mac) +#ifdef APPLE_STYLE_KEYS + mod_checkboxes[MOD_META]->hide(); + + mod_checkboxes[MOD_COMMAND]->show(); + mod_checkboxes[MOD_COMMAND]->set_text("Meta (Command)"); +#else + mod_checkboxes[MOD_CONTROL]->hide(); + + mod_checkboxes[MOD_COMMAND]->show(); + mod_checkboxes[MOD_COMMAND]->set_text("Control (Command)"); +#endif + } else { + // If not, hide Command, show Control and Meta. + mod_checkboxes[MOD_COMMAND]->hide(); + mod_checkboxes[MOD_CONTROL]->show(); + mod_checkboxes[MOD_META]->show(); + } +} + +void InputEventConfigurationDialog::_physical_keycode_toggled(bool p_checked) { + Ref<InputEventKey> k = event; + + if (k.is_null()) { + return; + } + + if (p_checked) { + k->set_physical_keycode(k->get_keycode()); + k->set_keycode(0); + } else { + k->set_keycode(k->get_physical_keycode()); + k->set_physical_keycode(0); + } + + _set_event(k); +} + +void InputEventConfigurationDialog::_input_list_item_selected() { + TreeItem *selected = input_list_tree->get_selected(); + + // Invalid tree selection - type only exists on the "category" items, which are not a valid selection. + if (selected->has_meta("__type")) { + return; + } + + int input_type = selected->get_parent()->get_meta("__type"); + + switch (input_type) { + case InputEventConfigurationDialog::INPUT_KEY: { + int kc = selected->get_meta("__keycode"); + Ref<InputEventKey> k; + k.instance(); + + if (physical_key_checkbox->is_pressed()) { + k->set_physical_keycode(kc); + k->set_keycode(0); + } else { + k->set_physical_keycode(0); + k->set_keycode(kc); + } + + // Maintain modifier state from checkboxes + k->set_alt(mod_checkboxes[MOD_ALT]->is_pressed()); + k->set_shift(mod_checkboxes[MOD_SHIFT]->is_pressed()); + k->set_command(mod_checkboxes[MOD_COMMAND]->is_pressed()); + k->set_control(mod_checkboxes[MOD_CONTROL]->is_pressed()); + k->set_metakey(mod_checkboxes[MOD_META]->is_pressed()); + k->set_store_command(store_command_checkbox->is_pressed()); + + _set_event(k); + } break; + case InputEventConfigurationDialog::INPUT_MOUSE_BUTTON: { + int idx = selected->get_meta("__index"); + Ref<InputEventMouseButton> mb; + mb.instance(); + mb->set_button_index(idx); + // Maintain modifier state from checkboxes + mb->set_alt(mod_checkboxes[MOD_ALT]->is_pressed()); + mb->set_shift(mod_checkboxes[MOD_SHIFT]->is_pressed()); + mb->set_command(mod_checkboxes[MOD_COMMAND]->is_pressed()); + mb->set_control(mod_checkboxes[MOD_CONTROL]->is_pressed()); + mb->set_metakey(mod_checkboxes[MOD_META]->is_pressed()); + mb->set_store_command(store_command_checkbox->is_pressed()); + + _set_event(mb); + } break; + case InputEventConfigurationDialog::INPUT_JOY_BUTTON: { + int idx = selected->get_meta("__index"); + Ref<InputEventJoypadButton> jb = InputEventJoypadButton::create_reference(idx); + _set_event(jb); + } break; + case InputEventConfigurationDialog::INPUT_JOY_MOTION: { + int axis = selected->get_meta("__axis"); + int value = selected->get_meta("__value"); + + Ref<InputEventJoypadMotion> jm; + jm.instance(); + jm->set_axis(axis); + jm->set_axis_value(value); + _set_event(jm); + } break; + default: + break; + } +} + +void InputEventConfigurationDialog::_set_current_device(int i_device) { + device_id_option->select(i_device + 1); +} + +int InputEventConfigurationDialog::_get_current_device() const { + return device_id_option->get_selected() - 1; +} + +String InputEventConfigurationDialog::_get_device_string(int i_device) const { + if (i_device == InputMap::ALL_DEVICES) { + return TTR("All Devices"); + } + return TTR("Device") + " " + itos(i_device); +} + +void InputEventConfigurationDialog::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + input_list_search->set_right_icon(input_list_search->get_theme_icon("Search", "EditorIcons")); + + physical_key_checkbox->set_icon(get_theme_icon("KeyboardPhysical", "EditorIcons")); + + icon_cache.keyboard = get_theme_icon("Keyboard", "EditorIcons"); + icon_cache.mouse = get_theme_icon("Mouse", "EditorIcons"); + icon_cache.joypad_button = get_theme_icon("JoyButton", "EditorIcons"); + icon_cache.joypad_axis = get_theme_icon("JoyAxis", "EditorIcons"); + + _update_input_list(); + } break; + default: + break; + } +} + +void InputEventConfigurationDialog::popup_and_configure(const Ref<InputEvent> &p_event) { + if (p_event.is_valid()) { + _set_event(p_event); + } else { + // Clear Event + _set_event(p_event); + + // Clear Checkbox Values + for (int i = 0; i < MOD_MAX; i++) { + mod_checkboxes[i]->set_pressed(false); + } + physical_key_checkbox->set_pressed(false); + store_command_checkbox->set_pressed(true); + _set_current_device(0); + + // Switch to "Listen" tab + tab_container->set_current_tab(0); + } + + popup_centered(); +} + +Ref<InputEvent> InputEventConfigurationDialog::get_event() const { + return event; +} + +void InputEventConfigurationDialog::set_allowed_input_types(int p_type_masks) { + allowed_input_types = p_type_masks; +} + +InputEventConfigurationDialog::InputEventConfigurationDialog() { + allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION; + + set_title("Event Configuration"); + set_min_size(Size2i(550 * EDSCALE, 0)); // Min width + + VBoxContainer *main_vbox = memnew(VBoxContainer); + add_child(main_vbox); + + tab_container = memnew(TabContainer); + tab_container->set_tab_align(TabContainer::TabAlign::ALIGN_LEFT); + tab_container->set_use_hidden_tabs_for_min_size(true); + tab_container->set_v_size_flags(Control::SIZE_EXPAND_FILL); + tab_container->connect("tab_selected", callable_mp(this, &InputEventConfigurationDialog::_tab_selected)); + main_vbox->add_child(tab_container); + + CenterContainer *cc = memnew(CenterContainer); + cc->set_name("Listen for Input"); + event_as_text = memnew(Label); + event_as_text->set_align(Label::ALIGN_CENTER); + cc->add_child(event_as_text); + tab_container->add_child(cc); + + // List of all input options to manually select from. + + VBoxContainer *manual_vbox = memnew(VBoxContainer); + manual_vbox->set_name("Manual Selection"); + manual_vbox->set_v_size_flags(Control::SIZE_EXPAND_FILL); + tab_container->add_child(manual_vbox); + + input_list_search = memnew(LineEdit); + input_list_search->set_h_size_flags(Control::SIZE_EXPAND_FILL); + input_list_search->set_placeholder(TTR("Filter Inputs")); + input_list_search->set_clear_button_enabled(true); + input_list_search->connect("text_changed", callable_mp(this, &InputEventConfigurationDialog::_search_term_updated)); + manual_vbox->add_child(input_list_search); + + input_list_tree = memnew(Tree); + input_list_tree->set_custom_minimum_size(Size2(0, 100 * EDSCALE)); // Min height for tree + input_list_tree->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_input_list_item_selected)); + input_list_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); + manual_vbox->add_child(input_list_tree); + + input_list_tree->set_hide_root(true); + input_list_tree->set_columns(1); + + _update_input_list(); + + // Additional Options + additional_options_container = memnew(VBoxContainer); + additional_options_container->hide(); + + Label *opts_label = memnew(Label); + opts_label->set_text("Additional Options"); + additional_options_container->add_child(opts_label); + + // Device Selection + device_container = memnew(HBoxContainer); + device_container->set_h_size_flags(Control::SIZE_EXPAND_FILL); + + Label *device_label = memnew(Label); + device_label->set_text("Device:"); + device_container->add_child(device_label); + + device_id_option = memnew(OptionButton); + device_id_option->set_h_size_flags(Control::SIZE_EXPAND_FILL); + device_container->add_child(device_id_option); + + for (int i = -1; i < 8; i++) { + device_id_option->add_item(_get_device_string(i)); + } + _set_current_device(0); + device_container->hide(); + additional_options_container->add_child(device_container); + + // Modifier Selection + mod_container = memnew(HBoxContainer); + for (int i = 0; i < MOD_MAX; i++) { + String name = mods[i]; + mod_checkboxes[i] = memnew(CheckBox); + mod_checkboxes[i]->connect("toggled", callable_mp(this, &InputEventConfigurationDialog::_mod_toggled), varray(i)); + mod_checkboxes[i]->set_text(name); + mod_container->add_child(mod_checkboxes[i]); + } + + mod_container->add_child(memnew(VSeparator)); + + store_command_checkbox = memnew(CheckBox); + store_command_checkbox->connect("toggled", callable_mp(this, &InputEventConfigurationDialog::_store_command_toggled)); + store_command_checkbox->set_pressed(true); + store_command_checkbox->set_text(TTR("Store Command")); +#ifdef APPLE_STYLE_KEYS + store_command_checkbox->set_tooltip(TTR("Toggles between serializing 'command' and 'meta'. Used for compatibility with Windows/Linux style keyboard.")); +#else + store_command_checkbox->set_tooltip(TTR("Toggles between serializing 'command' and 'control'. Used for compatibility with Apple Style keyboards.")); +#endif + mod_container->add_child(store_command_checkbox); + + mod_container->hide(); + additional_options_container->add_child(mod_container); + + // Physical Key Checkbox + + physical_key_checkbox = memnew(CheckBox); + physical_key_checkbox->set_text(TTR("Use Physical Keycode")); + physical_key_checkbox->set_tooltip(TTR("Stores the physical position of the key on the keyboard rather than the keys value. Used for compatibility with non-latin layouts.")); + physical_key_checkbox->connect("toggled", callable_mp(this, &InputEventConfigurationDialog::_physical_keycode_toggled)); + physical_key_checkbox->hide(); + additional_options_container->add_child(physical_key_checkbox); + + main_vbox->add_child(additional_options_container); + + // Default to first tab + tab_container->set_current_tab(0); +} + +///////////////////////////////////////// + +static bool _is_action_name_valid(const String &p_name) { + const char32_t *cstr = p_name.get_data(); + for (int i = 0; cstr[i]; i++) { + if (cstr[i] == '/' || cstr[i] == ':' || cstr[i] == '"' || + cstr[i] == '=' || cstr[i] == '\\' || cstr[i] < 32) { + return false; + } + } + return true; +} + +void ActionMapEditor::_event_config_confirmed() { + Ref<InputEvent> ev = event_config_dialog->get_event(); + + Dictionary new_action = current_action.duplicate(); + Array events = new_action["events"]; + + if (current_action_event_index == -1) { + // Add new event + events.push_back(ev); + } else { + // Edit existing event + events[current_action_event_index] = ev; + } + + new_action["events"] = events; + emit_signal("action_edited", current_action_name, new_action); +} + +void ActionMapEditor::_add_action_pressed() { + _add_action(add_edit->get_text()); +} + +void ActionMapEditor::_add_action(const String &p_name) { + if (!allow_editing_actions) { + return; + } + + if (p_name == "" || !_is_action_name_valid(p_name)) { + show_message(TTR("Invalid action name. it cannot be.is_empty()() nor contain '/', ':', '=', '\\' or '\"'")); + return; + } + + add_edit->clear(); + emit_signal("action_added", p_name); +} + +void ActionMapEditor::_action_edited() { + if (!allow_editing_actions) { + return; + } + + TreeItem *ti = action_tree->get_edited(); + if (!ti) { + return; + } + + if (action_tree->get_selected_column() == 0) { + // Name Edited + String new_name = ti->get_text(0); + String old_name = ti->get_meta("__name"); + + if (new_name == old_name) { + return; + } + + if (new_name == "" || !_is_action_name_valid(new_name)) { + ti->set_text(0, old_name); + show_message(TTR("Invalid action name. it cannot be.is_empty()() nor contain '/', ':', '=', '\\' or '\"'")); + return; + } + + emit_signal("action_renamed", old_name, new_name); + } else if (action_tree->get_selected_column() == 1) { + // Deadzone Edited + String name = ti->get_meta("__name"); + Dictionary old_action = ti->get_meta("__action"); + Dictionary new_action = old_action.duplicate(); + new_action["deadzone"] = ti->get_range(1); + + // Call deferred so that input can finish propagating through tree, allowing re-making of tree to occur. + call_deferred("emit_signal", "action_edited", name, new_action); + } +} + +void ActionMapEditor::_tree_button_pressed(Object *p_item, int p_column, int p_id) { + ItemButton option = (ItemButton)p_id; + + TreeItem *item = Object::cast_to<TreeItem>(p_item); + if (!item) { + return; + } + + switch (option) { + case ActionMapEditor::BUTTON_ADD_EVENT: { + current_action = item->get_meta("__action"); + current_action_name = item->get_meta("__name"); + current_action_event_index = -1; + + event_config_dialog->popup_and_configure(); + + } break; + case ActionMapEditor::BUTTON_EDIT_EVENT: { + // Action and Action name is located on the parent of the event. + current_action = item->get_parent()->get_meta("__action"); + current_action_name = item->get_parent()->get_meta("__name"); + + current_action_event_index = item->get_meta("__index"); + + Ref<InputEvent> ie = item->get_meta("__event"); + if (ie.is_valid()) { + event_config_dialog->popup_and_configure(ie); + } + + } break; + case ActionMapEditor::BUTTON_REMOVE_ACTION: { + if (!allow_editing_actions) { + break; + } + + // Send removed action name + String name = item->get_meta("__name"); + emit_signal("action_removed", name); + } break; + case ActionMapEditor::BUTTON_REMOVE_EVENT: { + // Remove event and send updated action + Dictionary action = item->get_parent()->get_meta("__action"); + String action_name = item->get_parent()->get_meta("__name"); + + int event_index = item->get_meta("__index"); + + Array events = action["events"]; + events.remove(event_index); + action["events"] = events; + + emit_signal("action_edited", action_name, action); + } break; + default: + break; + } +} + +void ActionMapEditor::_tree_item_activated() { + TreeItem *item = action_tree->get_selected(); + + if (!item || !item->has_meta("__event")) { + return; + } + + _tree_button_pressed(item, 2, BUTTON_EDIT_EVENT); +} + +void ActionMapEditor::set_show_uneditable(bool p_show) { + show_uneditable = p_show; + show_uneditable_actions_checkbox->set_pressed(p_show); + + // Prevent unnecessary updates of action list when cache is.is_empty()(). + if (!actions_cache.is_empty()) { + update_action_list(); + } +} + +void ActionMapEditor::_search_term_updated(const String &) { + update_action_list(); +} + +Variant ActionMapEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { + TreeItem *selected = action_tree->get_selected(); + if (!selected) { + return Variant(); + } + + String name = selected->get_text(0); + Label *label = memnew(Label(name)); + label->set_modulate(Color(1, 1, 1, 1.0f)); + action_tree->set_drag_preview(label); + + Dictionary drag_data; + + if (selected->has_meta("__action")) { + drag_data["input_type"] = "action"; + } + + if (selected->has_meta("__event")) { + drag_data["input_type"] = "event"; + } + + action_tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN); + + return drag_data; +} + +bool ActionMapEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + Dictionary d = p_data; + if (!d.has("input_type")) { + return false; + } + + TreeItem *selected = action_tree->get_selected(); + TreeItem *item = action_tree->get_item_at_position(p_point); + if (!selected || !item || item == selected) { + return false; + } + + // Don't allow moving an action in-between events. + if (d["input_type"] == "action" && item->has_meta("__event")) { + return false; + } + + // Don't allow moving an event to a different action. + if (d["input_type"] == "event" && item->get_parent() != selected->get_parent()) { + return false; + } + + return true; +} + +void ActionMapEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { + if (!can_drop_data_fw(p_point, p_data, p_from)) { + return; + } + + TreeItem *selected = action_tree->get_selected(); + TreeItem *target = action_tree->get_item_at_position(p_point); + bool drop_above = action_tree->get_drop_section_at_position(p_point) == -1; + + if (!target) { + return; + } + + Dictionary d = p_data; + if (d["input_type"] == "action") { + // Change action order. + String relative_to = target->get_meta("__name"); + String action_name = selected->get_meta("__name"); + emit_signal("action_reordered", action_name, relative_to, drop_above); + + } else if (d["input_type"] == "event") { + // Change event order + int current_index = selected->get_meta("__index"); + int target_index = target->get_meta("__index"); + + // Construct new events array. + Dictionary new_action = selected->get_parent()->get_meta("__action"); + + Array events = new_action["events"]; + Array new_events; + + // The following method was used to perform the array changes since `remove` followed by `insert` was not working properly at time of writing. + // Loop thought existing events + for (int i = 0; i < events.size(); i++) { + // If you come across the current index, just skip it, as it has been moved. + if (i == current_index) { + continue; + } else if (i == target_index) { + // We are at the target index. If drop above, add selected event there first, then target, so moved event goes on top. + if (drop_above) { + new_events.push_back(events[current_index]); + new_events.push_back(events[target_index]); + } else { + new_events.push_back(events[target_index]); + new_events.push_back(events[current_index]); + } + } else { + new_events.push_back(events[i]); + } + } + + new_action["events"] = new_events; + emit_signal("action_edited", selected->get_parent()->get_meta("__name"), new_action); + } +} + +void ActionMapEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + action_list_search->set_right_icon(get_theme_icon("Search", "EditorIcons")); + } break; + default: + break; + } +} + +void ActionMapEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &ActionMapEditor::get_drag_data_fw); + ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &ActionMapEditor::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("drop_data_fw"), &ActionMapEditor::drop_data_fw); + + ADD_SIGNAL(MethodInfo("action_added", PropertyInfo(Variant::STRING, "name"))); + ADD_SIGNAL(MethodInfo("action_edited", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::DICTIONARY, "new_action"))); + ADD_SIGNAL(MethodInfo("action_removed", PropertyInfo(Variant::STRING, "name"))); + ADD_SIGNAL(MethodInfo("action_renamed", PropertyInfo(Variant::STRING, "old_name"), PropertyInfo(Variant::STRING, "new_name"))); + ADD_SIGNAL(MethodInfo("action_reordered", PropertyInfo(Variant::STRING, "action_name"), PropertyInfo(Variant::STRING, "relative_to"), PropertyInfo(Variant::BOOL, "before"))); +} + +LineEdit *ActionMapEditor::get_search_box() const { + return action_list_search; +} + +InputEventConfigurationDialog *ActionMapEditor::get_configuration_dialog() { + return event_config_dialog; +} + +void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_infos) { + if (!p_action_infos.is_empty()) { + actions_cache = p_action_infos; + } + + action_tree->clear(); + TreeItem *root = action_tree->create_item(); + + int uneditable_count = 0; + + for (int i = 0; i < actions_cache.size(); i++) { + ActionInfo action_info = actions_cache[i]; + + if (!action_info.editable) { + uneditable_count++; + } + + String search_term = action_list_search->get_text(); + if (!search_term.is_empty() && action_info.name.findn(search_term) == -1) { + continue; + } + + if (!action_info.editable && !show_uneditable) { + continue; + } + + const Array events = action_info.action["events"]; + const Variant deadzone = action_info.action["deadzone"]; + + // Update Tree... + + TreeItem *action_item = action_tree->create_item(root); + action_item->set_meta("__action", action_info.action); + action_item->set_meta("__name", action_info.name); + + // First Column - Action Name + action_item->set_text(0, action_info.name); + action_item->set_editable(0, action_info.editable); + action_item->set_icon(0, action_info.icon); + + // Second Column - Deadzone + action_item->set_editable(1, true); + action_item->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); + action_item->set_range_config(1, 0.0, 1.0, 0.01); + action_item->set_range(1, deadzone); + + // Third column - buttons + action_item->add_button(2, action_tree->get_theme_icon("Add", "EditorIcons"), BUTTON_ADD_EVENT, false, TTR("Add Event")); + action_item->add_button(2, action_tree->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_ACTION, !action_info.editable, action_info.editable ? "Remove Action" : "Cannot Remove Action"); + + action_item->set_custom_bg_color(0, action_tree->get_theme_color("prop_subsection", "Editor")); + action_item->set_custom_bg_color(1, action_tree->get_theme_color("prop_subsection", "Editor")); + + for (int evnt_idx = 0; evnt_idx < events.size(); evnt_idx++) { + Ref<InputEvent> event = events[evnt_idx]; + if (event.is_null()) { + continue; + } + + TreeItem *event_item = action_tree->create_item(action_item); + + // First Column - Text + event_item->set_text(0, event_config_dialog->get_event_text(event)); // Need to us the special description for JoypadMotion here, so don't use as_text() directly. + event_item->set_meta("__event", event); + event_item->set_meta("__index", evnt_idx); + + // Third Column - Buttons + event_item->add_button(2, action_tree->get_theme_icon("Edit", "EditorIcons"), BUTTON_EDIT_EVENT, false, TTR("Edit Event")); + event_item->add_button(2, action_tree->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_EVENT, false, TTR("Remove Event")); + event_item->set_button_color(2, 0, Color(1, 1, 1, 0.75)); + event_item->set_button_color(2, 1, Color(1, 1, 1, 0.75)); + } + } +} + +void ActionMapEditor::show_message(const String &p_message) { + message->set_text(p_message); + message->popup_centered(Size2(300, 100) * EDSCALE); +} + +void ActionMapEditor::set_allow_editing_actions(bool p_allow) { + allow_editing_actions = p_allow; + add_hbox->set_visible(p_allow); +} + +void ActionMapEditor::set_toggle_editable_label(const String &p_label) { + show_uneditable_actions_checkbox->set_text(p_label); +} + +void ActionMapEditor::use_external_search_box(LineEdit *p_searchbox) { + memdelete(action_list_search); + action_list_search = p_searchbox; + action_list_search->connect("text_changed", callable_mp(this, &ActionMapEditor::_search_term_updated)); +} + +ActionMapEditor::ActionMapEditor() { + allow_editing_actions = true; + show_uneditable = true; + + // Main Vbox Container + VBoxContainer *main_vbox = memnew(VBoxContainer); + main_vbox->set_anchors_and_offsets_preset(PRESET_WIDE); + add_child(main_vbox); + + HBoxContainer *top_hbox = memnew(HBoxContainer); + main_vbox->add_child(top_hbox); + + action_list_search = memnew(LineEdit); + action_list_search->set_h_size_flags(Control::SIZE_EXPAND_FILL); + action_list_search->set_placeholder(TTR("Filter Actions")); + action_list_search->set_clear_button_enabled(true); + action_list_search->connect("text_changed", callable_mp(this, &ActionMapEditor::_search_term_updated)); + top_hbox->add_child(action_list_search); + + show_uneditable_actions_checkbox = memnew(CheckBox); + show_uneditable_actions_checkbox->set_pressed(false); + show_uneditable_actions_checkbox->set_text(TTR("Show Uneditable Actions")); + show_uneditable_actions_checkbox->connect("toggled", callable_mp(this, &ActionMapEditor::set_show_uneditable)); + top_hbox->add_child(show_uneditable_actions_checkbox); + + // Adding Action line edit + button + add_hbox = memnew(HBoxContainer); + add_hbox->set_h_size_flags(Control::SIZE_EXPAND_FILL); + + add_edit = memnew(LineEdit); + add_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); + add_edit->set_placeholder(TTR("Add New Action")); + add_edit->set_clear_button_enabled(true); + add_edit->connect("text_entered", callable_mp(this, &ActionMapEditor::_add_action)); + add_hbox->add_child(add_edit); + + Button *add_button = memnew(Button); + add_button->set_text("Add"); + add_button->connect("pressed", callable_mp(this, &ActionMapEditor::_add_action_pressed)); + add_hbox->add_child(add_button); + + main_vbox->add_child(add_hbox); + + // Action Editor Tree + action_tree = memnew(Tree); + action_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); + action_tree->set_columns(3); + action_tree->set_hide_root(true); + action_tree->set_column_titles_visible(true); + action_tree->set_column_title(0, TTR("Action")); + action_tree->set_column_title(1, TTR("Deadzone")); + action_tree->set_column_expand(1, false); + action_tree->set_column_min_width(1, 80 * EDSCALE); + action_tree->set_column_expand(2, false); + action_tree->set_column_min_width(2, 50 * EDSCALE); + action_tree->connect("item_edited", callable_mp(this, &ActionMapEditor::_action_edited)); + action_tree->connect("item_activated", callable_mp(this, &ActionMapEditor::_tree_item_activated)); + action_tree->connect("button_pressed", callable_mp(this, &ActionMapEditor::_tree_button_pressed)); + main_vbox->add_child(action_tree); + + action_tree->set_drag_forwarding(this); + + // Adding event dialog + event_config_dialog = memnew(InputEventConfigurationDialog); + event_config_dialog->connect("confirmed", callable_mp(this, &ActionMapEditor::_event_config_confirmed)); + add_child(event_config_dialog); + + message = memnew(AcceptDialog); + add_child(message); +} diff --git a/editor/action_map_editor.h b/editor/action_map_editor.h new file mode 100644 index 0000000000..f1f7bffef4 --- /dev/null +++ b/editor/action_map_editor.h @@ -0,0 +1,203 @@ +/*************************************************************************/ +/* action_map_editor.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ACTION_MAP_EDITOR_H +#define ACTION_MAP_EDITOR_H + +#include "editor/editor_data.h" + +// Confirmation Dialog used when configuring an input event. +// Separate from ActionMapEditor for code cleanliness and separation of responsibilities. +class InputEventConfigurationDialog : public ConfirmationDialog { + GDCLASS(InputEventConfigurationDialog, ConfirmationDialog); + +public: + enum InputType { + INPUT_KEY = 1, + INPUT_MOUSE_BUTTON = 2, + INPUT_JOY_BUTTON = 4, + INPUT_JOY_MOTION = 8 + }; + +private: + struct IconCache { + Ref<Texture2D> keyboard; + Ref<Texture2D> mouse; + Ref<Texture2D> joypad_button; + Ref<Texture2D> joypad_axis; + } icon_cache; + + Ref<InputEvent> event = Ref<InputEvent>(); + + TabContainer *tab_container; + + // Listening for input + Label *event_as_text; + + // List of All Key/Mouse/Joypad input options. + int allowed_input_types; + Tree *input_list_tree; + LineEdit *input_list_search; + + // Additional Options, shown depending on event selected + VBoxContainer *additional_options_container; + + HBoxContainer *device_container; + OptionButton *device_id_option; + + HBoxContainer *mod_container; // Contains the subcontainer and the store command checkbox. + + enum ModCheckbox { + MOD_ALT, + MOD_SHIFT, + MOD_COMMAND, + MOD_CONTROL, + MOD_META, + MOD_MAX + }; + String mods[MOD_MAX] = { "Alt", "Shift", "Command", "Control", "Meta" }; + + CheckBox *mod_checkboxes[MOD_MAX]; + CheckBox *store_command_checkbox; + + CheckBox *physical_key_checkbox; + + void _set_event(const Ref<InputEvent> &p_event); + + void _tab_selected(int p_tab); + void _listen_window_input(const Ref<InputEvent> &p_event); + + void _search_term_updated(const String &p_term); + void _update_input_list(); + void _input_list_item_selected(); + + void _mod_toggled(bool p_checked, int p_index); + void _store_command_toggled(bool p_checked); + void _physical_keycode_toggled(bool p_checked); + + void _set_current_device(int i_device); + int _get_current_device() const; + String _get_device_string(int i_device) const; + +protected: + void _notification(int p_what); + +public: + // Pass an existing event to configure it. Alternatively, pass no event to start with a blank configuration. + void popup_and_configure(const Ref<InputEvent> &p_event = Ref<InputEvent>()); + Ref<InputEvent> get_event() const; + String get_event_text(const Ref<InputEvent> &p_event); + + void set_allowed_input_types(int p_type_masks); + + InputEventConfigurationDialog(); +}; + +class ActionMapEditor : public Control { + GDCLASS(ActionMapEditor, Control); + +public: + struct ActionInfo { + String name = String(); + Dictionary action = Dictionary(); + + Ref<Texture2D> icon = Ref<Texture2D>(); + bool editable = true; + }; + +private: + enum ItemButton { + BUTTON_ADD_EVENT, + BUTTON_EDIT_EVENT, + BUTTON_REMOVE_ACTION, + BUTTON_REMOVE_EVENT, + }; + + Vector<ActionInfo> actions_cache; + Tree *action_tree; + + // Storing which action/event is currently being edited in the InputEventConfigurationDialog. + + Dictionary current_action = Dictionary(); + String current_action_name = String(); + int current_action_event_index = -1; + + // Popups + + InputEventConfigurationDialog *event_config_dialog; + AcceptDialog *message; + + // Filtering and Adding actions + + bool show_uneditable; + CheckBox *show_uneditable_actions_checkbox; + LineEdit *action_list_search; + + bool allow_editing_actions; + HBoxContainer *add_hbox; + LineEdit *add_edit; + + void _event_config_confirmed(); + + void _add_action_pressed(); + void _add_action(const String &p_name); + void _action_edited(); + + void _tree_button_pressed(Object *p_item, int p_column, int p_id); + void _tree_item_activated(); + void _search_term_updated(const String &p_search_term); + + 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); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + LineEdit *get_search_box() const; + InputEventConfigurationDialog *get_configuration_dialog(); + + // Dictionary represents an Action with "events" (Array) and "deadzone" (float) items. Pass with no param to update list from cached action map. + void update_action_list(const Vector<ActionInfo> &p_action_infos = Vector<ActionInfo>()); + void show_message(const String &p_message); + + void set_show_uneditable(bool p_show); + void set_allow_editing_actions(bool p_allow); + + void set_toggle_editable_label(const String &p_label); + + void use_external_search_box(LineEdit *p_searchbox); + + ActionMapEditor(); +}; + +#endif diff --git a/editor/audio_stream_preview.cpp b/editor/audio_stream_preview.cpp index 8be8735f3e..539657afd7 100644 --- a/editor/audio_stream_preview.cpp +++ b/editor/audio_stream_preview.cpp @@ -155,7 +155,7 @@ void AudioStreamPreviewGenerator::_preview_thread(void *p_preview) { preview->playback->stop(); - preview->generating = false; + preview->generating.clear(); } Ref<AudioStreamPreview> AudioStreamPreviewGenerator::generate_preview(const Ref<AudioStream> &p_stream) { @@ -172,7 +172,7 @@ Ref<AudioStreamPreview> AudioStreamPreviewGenerator::generate_preview(const Ref< Preview *preview = &previews[p_stream->get_instance_id()]; preview->base_stream = p_stream; preview->playback = preview->base_stream->instance_playback(); - preview->generating = true; + preview->generating.set(); preview->id = p_stream->get_instance_id(); float len_s = preview->base_stream->get_length(); @@ -217,7 +217,7 @@ void AudioStreamPreviewGenerator::_notification(int p_what) { if (p_what == NOTIFICATION_PROCESS) { List<ObjectID> to_erase; for (Map<ObjectID, Preview>::Element *E = previews.front(); E; E = E->next()) { - if (!E->get().generating) { + if (!E->get().generating.is_set()) { if (E->get().thread) { E->get().thread->wait_to_finish(); memdelete(E->get().thread); diff --git a/editor/audio_stream_preview.h b/editor/audio_stream_preview.h index 21c9ea203e..accc7275c0 100644 --- a/editor/audio_stream_preview.h +++ b/editor/audio_stream_preview.h @@ -32,6 +32,7 @@ #define AUDIO_STREAM_PREVIEW_H #include "core/os/thread.h" +#include "core/templates/safe_refcount.h" #include "scene/main/node.h" #include "servers/audio/audio_stream.h" @@ -60,9 +61,20 @@ class AudioStreamPreviewGenerator : public Node { Ref<AudioStreamPreview> preview; Ref<AudioStream> base_stream; Ref<AudioStreamPlayback> playback; - volatile bool generating = false; + SafeFlag generating; ObjectID id; Thread *thread = nullptr; + + // Needed for the bookkeeping of the Map + Preview &operator=(const Preview &p_rhs) { + preview = p_rhs.preview; + base_stream = p_rhs.base_stream; + playback = p_rhs.playback; + generating.set_to(generating.is_set()); + id = p_rhs.id; + thread = p_rhs.thread; + return *this; + } }; Map<ObjectID, Preview> previews; diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index 2780b74469..25e155aafe 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -480,8 +480,8 @@ void DependencyRemoveDialog::ok_pressed() { if (files_to_delete[i] == String(ProjectSettings::get_singleton()->get("application/boot_splash/image"))) { ProjectSettings::get_singleton()->set("application/boot_splash/image", ""); } - if (files_to_delete[i] == String(ProjectSettings::get_singleton()->get("rendering/environment/default_environment"))) { - ProjectSettings::get_singleton()->set("rendering/environment/default_environment", ""); + if (files_to_delete[i] == String(ProjectSettings::get_singleton()->get("rendering/environment/defaults/default_environment"))) { + ProjectSettings::get_singleton()->set("rendering/environment/defaults/default_environment", ""); } if (files_to_delete[i] == String(ProjectSettings::get_singleton()->get("display/mouse_cursor/custom_image"))) { ProjectSettings::get_singleton()->set("display/mouse_cursor/custom_image", ""); @@ -492,8 +492,8 @@ void DependencyRemoveDialog::ok_pressed() { if (files_to_delete[i] == String(ProjectSettings::get_singleton()->get("gui/theme/custom_font"))) { ProjectSettings::get_singleton()->set("gui/theme/custom_font", ""); } - if (files_to_delete[i] == String(ProjectSettings::get_singleton()->get("audio/default_bus_layout"))) { - ProjectSettings::get_singleton()->set("audio/default_bus_layout", ""); + if (files_to_delete[i] == String(ProjectSettings::get_singleton()->get("audio/buses/default_bus_layout"))) { + ProjectSettings::get_singleton()->set("audio/buses/default_bus_layout", ""); } String path = OS::get_singleton()->get_resource_dir() + files_to_delete[i].replace_first("res://", "/"); diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index 69a13957e6..9a826ab106 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -1191,7 +1191,7 @@ void EditorAudioBuses::_load_layout() { } void EditorAudioBuses::_load_default_layout() { - String layout_path = ProjectSettings::get_singleton()->get("audio/default_bus_layout"); + String layout_path = ProjectSettings::get_singleton()->get("audio/buses/default_bus_layout"); Ref<AudioBusLayout> state = ResourceLoader::load(layout_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE); if (state.is_null()) { @@ -1257,7 +1257,7 @@ EditorAudioBuses::EditorAudioBuses() { add_child(top_hb); file = memnew(Label); - String layout_path = ProjectSettings::get_singleton()->get("audio/default_bus_layout"); + String layout_path = ProjectSettings::get_singleton()->get("audio/buses/default_bus_layout"); file->set_text(String(TTR("Layout")) + ": " + layout_path.get_file()); file->set_clip_text(true); file->set_h_size_flags(SIZE_EXPAND_FILL); @@ -1313,7 +1313,7 @@ EditorAudioBuses::EditorAudioBuses() { set_v_size_flags(SIZE_EXPAND_FILL); - edited_path = ProjectSettings::get_singleton()->get("audio/default_bus_layout"); + edited_path = ProjectSettings::get_singleton()->get("audio/buses/default_bus_layout"); file_dialog = memnew(EditorFileDialog); List<String> ext; diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index 24256b843e..949306de42 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -1403,9 +1403,9 @@ void EditorExport::add_export_preset(const Ref<EditorExportPreset> &p_preset, in } String EditorExportPlatform::test_etc2() const { - String driver = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name"); - bool etc_supported = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc"); - bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2"); + String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); + bool etc_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc"); + bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2"); if (driver == "GLES2" && !etc_supported) { return TTR("Target platform requires 'ETC' texture compression for GLES2. Enable 'Import Etc' in Project Settings."); @@ -1417,9 +1417,9 @@ String EditorExportPlatform::test_etc2() const { } String EditorExportPlatform::test_etc2_or_pvrtc() const { - String driver = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name"); - bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2"); - bool pvrtc_supported = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc"); + String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); + bool etc2_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2"); + bool pvrtc_supported = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_pvrtc"); if (driver == "GLES2" && !pvrtc_supported) { return TTR("Target platform requires 'PVRTC' texture compression for GLES2. Enable 'Import Pvrtc' in Project Settings."); @@ -1902,7 +1902,7 @@ void EditorExportTextSceneToBinaryPlugin::_export_file(const String &p_path, con return; } - bool convert = GLOBAL_GET("editor/convert_text_resources_to_binary_on_export"); + bool convert = GLOBAL_GET("editor/export/convert_text_resources_to_binary"); if (!convert) { return; } @@ -1922,5 +1922,5 @@ void EditorExportTextSceneToBinaryPlugin::_export_file(const String &p_path, con } EditorExportTextSceneToBinaryPlugin::EditorExportTextSceneToBinaryPlugin() { - GLOBAL_DEF("editor/convert_text_resources_to_binary_on_export", false); + GLOBAL_DEF("editor/export/convert_text_resources_to_binary", false); } diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 01aad0c41b..6d694358bf 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -1150,7 +1150,6 @@ void EditorFileDialog::_update_drives() { void EditorFileDialog::_favorite_selected(int p_idx) { dir_access->change_dir(favorites->get_item_metadata(p_idx)); - file->set_text(""); update_dir(); invalidate(); _push_history(); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 4b68de26e7..3c6649a66a 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1413,11 +1413,11 @@ void EditorFileSystem::_scan_script_classes(EditorFileSystemDirectory *p_dir) { } void EditorFileSystem::update_script_classes() { - if (!update_script_classes_queued) { + if (!update_script_classes_queued.is_set()) { return; } - update_script_classes_queued = false; + update_script_classes_queued.clear(); ScriptServer::global_classes_clear(); if (get_filesystem()) { _scan_script_classes(get_filesystem()); @@ -1436,11 +1436,11 @@ void EditorFileSystem::update_script_classes() { } void EditorFileSystem::_queue_update_script_classes() { - if (update_script_classes_queued) { + if (update_script_classes_queued.is_set()) { return; } - update_script_classes_queued = true; + update_script_classes_queued.set(); call_deferred("update_script_classes"); } @@ -2067,7 +2067,7 @@ void EditorFileSystem::_update_extensions() { EditorFileSystem::EditorFileSystem() { ResourceLoader::import = _resource_import; - reimport_on_missing_imported_files = GLOBAL_DEF("editor/reimport_missing_imported_files", true); + reimport_on_missing_imported_files = GLOBAL_DEF("editor/import/reimport_missing_imported_files", true); singleton = this; filesystem = memnew(EditorFileSystemDirectory); //like, empty @@ -2091,7 +2091,7 @@ EditorFileSystem::EditorFileSystem() { memdelete(da); scan_total = 0; - update_script_classes_queued = false; + update_script_classes_queued.clear(); first_scan = true; scan_changes_pending = false; revalidate_import_files = false; diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index fa0b89e667..dec2330256 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -34,6 +34,7 @@ #include "core/os/dir_access.h" #include "core/os/thread.h" #include "core/os/thread_safe.h" +#include "core/templates/safe_refcount.h" #include "core/templates/set.h" #include "scene/main/node.h" class FileAccess; @@ -220,7 +221,7 @@ class EditorFileSystem : public Node { }; void _scan_script_classes(EditorFileSystemDirectory *p_dir); - volatile bool update_script_classes_queued; + SafeFlag update_script_classes_queued; void _queue_update_script_classes(); String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const; diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index a7f808f63a..60071f6263 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1689,7 +1689,7 @@ void EditorInspector::update_tree() { bool valid = true; //if no properties in category, skip while (N) { - if (N->get().usage & PROPERTY_USAGE_EDITOR) { + if (N->get().usage & PROPERTY_USAGE_EDITOR && (!restrict_to_basic || (N->get().usage & PROPERTY_USAGE_EDITOR_BASIC_SETTING))) { break; } if (N->get().usage & PROPERTY_USAGE_CATEGORY) { @@ -1757,7 +1757,7 @@ void EditorInspector::update_tree() { continue; - } else if (!(p.usage & PROPERTY_USAGE_EDITOR) || _is_property_disabled_by_feature_profile(p.name)) { + } else if (!(p.usage & PROPERTY_USAGE_EDITOR) || _is_property_disabled_by_feature_profile(p.name) || (restrict_to_basic && !(p.usage & PROPERTY_USAGE_EDITOR_BASIC_SETTING))) { continue; } @@ -2620,6 +2620,11 @@ void EditorInspector::_update_script_class_properties(const Object &p_object, Li r_list.erase(bottom); } +void EditorInspector::set_restrict_to_basic_settings(bool p_restrict) { + restrict_to_basic = p_restrict; + update_tree(); +} + void EditorInspector::_bind_methods() { ClassDB::bind_method("_edit_request_change", &EditorInspector::_edit_request_change); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index b98801975f..18250780be 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -313,6 +313,8 @@ class EditorInspector : public ScrollContainer { String property_prefix; //used for sectioned inspector String object_class; + bool restrict_to_basic = false; + void _edit_set(const String &p_name, const Variant &p_value, bool p_refresh_all, const String &p_changed_field); void _property_changed(const String &p_path, const Variant &p_value, const String &p_name = "", bool p_changing = false); @@ -400,6 +402,8 @@ public: void set_use_deletable_properties(bool p_enabled); + void set_restrict_to_basic_settings(bool p_restrict); + EditorInspector(); }; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 57fa552845..ec8430e645 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -431,70 +431,70 @@ void EditorNode::_unhandled_input(const Ref<InputEvent> &p_event) { } void EditorNode::_update_from_settings() { - int current_filter = GLOBAL_GET("rendering/canvas_textures/default_texture_filter"); + int current_filter = GLOBAL_GET("rendering/textures/canvas_textures/default_texture_filter"); if (current_filter != scene_root->get_default_canvas_item_texture_filter()) { Viewport::DefaultCanvasItemTextureFilter tf = (Viewport::DefaultCanvasItemTextureFilter)current_filter; scene_root->set_default_canvas_item_texture_filter(tf); } - int current_repeat = GLOBAL_GET("rendering/canvas_textures/default_texture_repeat"); + int current_repeat = GLOBAL_GET("rendering/textures/canvas_textures/default_texture_repeat"); if (current_repeat != scene_root->get_default_canvas_item_texture_repeat()) { Viewport::DefaultCanvasItemTextureRepeat tr = (Viewport::DefaultCanvasItemTextureRepeat)current_repeat; scene_root->set_default_canvas_item_texture_repeat(tr); } - RS::DOFBokehShape dof_shape = RS::DOFBokehShape(int(GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_bokeh_shape"))); + RS::DOFBokehShape dof_shape = RS::DOFBokehShape(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_shape"))); RS::get_singleton()->camera_effects_set_dof_blur_bokeh_shape(dof_shape); - RS::DOFBlurQuality dof_quality = RS::DOFBlurQuality(int(GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_bokeh_quality"))); - bool dof_jitter = GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_use_jitter"); + RS::DOFBlurQuality dof_quality = RS::DOFBlurQuality(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_quality"))); + bool dof_jitter = GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_use_jitter"); RS::get_singleton()->camera_effects_set_dof_blur_quality(dof_quality, dof_jitter); - RS::get_singleton()->environment_set_ssao_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/quality/ssao/quality"))), GLOBAL_GET("rendering/quality/ssao/half_size"), GLOBAL_GET("rendering/quality/ssao/adaptive_target"), GLOBAL_GET("rendering/quality/ssao/blur_passes"), GLOBAL_GET("rendering/quality/ssao/fadeout_from"), GLOBAL_GET("rendering/quality/ssao/fadeout_to")); - RS::get_singleton()->screen_space_roughness_limiter_set_active(GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_enabled"), GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_amount"), GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_limit")); - bool glow_bicubic = int(GLOBAL_GET("rendering/quality/glow/upscale_mode")) > 0; + RS::get_singleton()->environment_set_ssao_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/environment/ssao/quality"))), GLOBAL_GET("rendering/environment/ssao/half_size"), GLOBAL_GET("rendering/environment/ssao/adaptive_target"), GLOBAL_GET("rendering/environment/ssao/blur_passes"), GLOBAL_GET("rendering/environment/ssao/fadeout_from"), GLOBAL_GET("rendering/environment/ssao/fadeout_to")); + RS::get_singleton()->screen_space_roughness_limiter_set_active(GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/enabled"), GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/amount"), GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/limit")); + bool glow_bicubic = int(GLOBAL_GET("rendering/environment/glow/upscale_mode")) > 0; RS::get_singleton()->environment_glow_set_use_bicubic_upscale(glow_bicubic); - bool glow_high_quality = GLOBAL_GET("rendering/quality/glow/use_high_quality"); + bool glow_high_quality = GLOBAL_GET("rendering/environment/glow/use_high_quality"); RS::get_singleton()->environment_glow_set_use_high_quality(glow_high_quality); - RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::EnvironmentSSRRoughnessQuality(int(GLOBAL_GET("rendering/quality/screen_space_reflection/roughness_quality"))); + RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::EnvironmentSSRRoughnessQuality(int(GLOBAL_GET("rendering/environment/screen_space_reflection/roughness_quality"))); RS::get_singleton()->environment_set_ssr_roughness_quality(ssr_roughness_quality); - RS::SubSurfaceScatteringQuality sss_quality = RS::SubSurfaceScatteringQuality(int(GLOBAL_GET("rendering/quality/subsurface_scattering/subsurface_scattering_quality"))); + RS::SubSurfaceScatteringQuality sss_quality = RS::SubSurfaceScatteringQuality(int(GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_quality"))); RS::get_singleton()->sub_surface_scattering_set_quality(sss_quality); - float sss_scale = GLOBAL_GET("rendering/quality/subsurface_scattering/subsurface_scattering_scale"); - float sss_depth_scale = GLOBAL_GET("rendering/quality/subsurface_scattering/subsurface_scattering_depth_scale"); + float sss_scale = GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_scale"); + float sss_depth_scale = GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_depth_scale"); RS::get_singleton()->sub_surface_scattering_set_scale(sss_scale, sss_depth_scale); - uint32_t directional_shadow_size = GLOBAL_GET("rendering/quality/directional_shadow/size"); - uint32_t directional_shadow_16_bits = GLOBAL_GET("rendering/quality/directional_shadow/16_bits"); + uint32_t directional_shadow_size = GLOBAL_GET("rendering/shadows/directional_shadow/size"); + uint32_t directional_shadow_16_bits = GLOBAL_GET("rendering/shadows/directional_shadow/16_bits"); RS::get_singleton()->directional_shadow_atlas_set_size(directional_shadow_size, directional_shadow_16_bits); - RS::ShadowQuality shadows_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/quality/shadows/soft_shadow_quality"))); + RS::ShadowQuality shadows_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/shadows/shadows/soft_shadow_quality"))); RS::get_singleton()->shadows_quality_set(shadows_quality); - RS::ShadowQuality directional_shadow_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/quality/directional_shadow/soft_shadow_quality"))); + RS::ShadowQuality directional_shadow_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/shadows/directional_shadow/soft_shadow_quality"))); RS::get_singleton()->directional_shadow_quality_set(directional_shadow_quality); - float probe_update_speed = GLOBAL_GET("rendering/lightmapper/probe_capture_update_speed"); + float probe_update_speed = GLOBAL_GET("rendering/lightmapping/probe_capture/update_speed"); RS::get_singleton()->lightmap_set_probe_capture_update_speed(probe_update_speed); - RS::EnvironmentSDFGIFramesToConverge frames_to_converge = RS::EnvironmentSDFGIFramesToConverge(int(GLOBAL_GET("rendering/sdfgi/frames_to_converge"))); + RS::EnvironmentSDFGIFramesToConverge frames_to_converge = RS::EnvironmentSDFGIFramesToConverge(int(GLOBAL_GET("rendering/global_illumination/sdfgi/frames_to_converge"))); RS::get_singleton()->environment_set_sdfgi_frames_to_converge(frames_to_converge); - RS::EnvironmentSDFGIRayCount ray_count = RS::EnvironmentSDFGIRayCount(int(GLOBAL_GET("rendering/sdfgi/probe_ray_count"))); + RS::EnvironmentSDFGIRayCount ray_count = RS::EnvironmentSDFGIRayCount(int(GLOBAL_GET("rendering/global_illumination/sdfgi/probe_ray_count"))); RS::get_singleton()->environment_set_sdfgi_ray_count(ray_count); - RS::GIProbeQuality gi_probe_quality = RS::GIProbeQuality(int(GLOBAL_GET("rendering/quality/gi_probes/quality"))); + RS::GIProbeQuality gi_probe_quality = RS::GIProbeQuality(int(GLOBAL_GET("rendering/global_illumination/gi_probes/quality"))); RS::get_singleton()->gi_probe_set_quality(gi_probe_quality); - RS::get_singleton()->environment_set_volumetric_fog_volume_size(GLOBAL_GET("rendering/volumetric_fog/volume_size"), GLOBAL_GET("rendering/volumetric_fog/volume_depth")); - RS::get_singleton()->environment_set_volumetric_fog_filter_active(bool(GLOBAL_GET("rendering/volumetric_fog/use_filter"))); - RS::get_singleton()->canvas_set_shadow_texture_size(GLOBAL_GET("rendering/quality/2d_shadow_atlas/size")); + RS::get_singleton()->environment_set_volumetric_fog_volume_size(GLOBAL_GET("rendering/environment/volumetric_fog/volume_size"), GLOBAL_GET("rendering/environment/volumetric_fog/volume_depth")); + RS::get_singleton()->environment_set_volumetric_fog_filter_active(bool(GLOBAL_GET("rendering/environment/volumetric_fog/use_filter"))); + RS::get_singleton()->canvas_set_shadow_texture_size(GLOBAL_GET("rendering/2d/shadow_atlas/size")); - bool use_half_res_gi = GLOBAL_DEF("rendering/quality/gi/use_half_resolution", false); + bool use_half_res_gi = GLOBAL_DEF("rendering/global_illumination/gi/use_half_resolution", false); RS::get_singleton()->gi_set_use_half_resolution(use_half_res_gi); - bool snap_2d_transforms = GLOBAL_GET("rendering/quality/2d/snap_2d_transforms_to_pixel"); + bool snap_2d_transforms = GLOBAL_GET("rendering/2d/snap/snap_2d_transforms_to_pixel"); scene_root->set_snap_2d_transforms_to_pixel(snap_2d_transforms); - bool snap_2d_vertices = GLOBAL_GET("rendering/quality/2d/snap_2d_vertices_to_pixel"); + bool snap_2d_vertices = GLOBAL_GET("rendering/2d/snap/snap_2d_vertices_to_pixel"); scene_root->set_snap_2d_vertices_to_pixel(snap_2d_vertices); - Viewport::SDFOversize sdf_oversize = Viewport::SDFOversize(int(GLOBAL_GET("rendering/quality/2d_sdf/oversize"))); + Viewport::SDFOversize sdf_oversize = Viewport::SDFOversize(int(GLOBAL_GET("rendering/2d/sdf/oversize"))); scene_root->set_sdf_oversize(sdf_oversize); - Viewport::SDFScale sdf_scale = Viewport::SDFScale(int(GLOBAL_GET("rendering/quality/2d_sdf/scale"))); + Viewport::SDFScale sdf_scale = Viewport::SDFScale(int(GLOBAL_GET("rendering/2d/sdf/scale"))); scene_root->set_sdf_scale(sdf_scale); - float lod_threshold = GLOBAL_GET("rendering/quality/mesh_lod/threshold_pixels"); + float lod_threshold = GLOBAL_GET("rendering/mesh_lod/lod_change/threshold_pixels"); scene_root->set_lod_threshold(lod_threshold); } @@ -2260,7 +2260,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) { List<String> breakpoints; editor_data.get_editor_breakpoints(&breakpoints); - args = ProjectSettings::get_singleton()->get("editor/main_run_args"); + args = ProjectSettings::get_singleton()->get("editor/run/main_run_args"); skip_breakpoints = EditorDebuggerNode::get_singleton()->is_skip_breakpoints(); EditorDebuggerNode::get_singleton()->start(); @@ -2520,16 +2520,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } break; - case FILE_IMPORT_SUBSCENE: { - if (!editor_data.get_edited_scene_root()) { - show_accept(TTR("This operation can't be done without a selected node."), TTR("OK")); - break; - } - - scene_tree_dock->import_subscene(); - - } break; - case FILE_EXTERNAL_OPEN_SCENE: { if (unsaved_cache && !p_confirmed) { confirmation->get_ok_button()->set_text(TTR("Open")); @@ -2799,7 +2789,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } break; case SET_VIDEO_DRIVER_SAVE_AND_RESTART: { - ProjectSettings::get_singleton()->set("rendering/quality/driver/driver_name", video_driver_request); + ProjectSettings::get_singleton()->set("rendering/driver/driver_name", video_driver_request); ProjectSettings::get_singleton()->save(); save_all_scenes(); @@ -5565,7 +5555,8 @@ static void _execute_thread(void *p_ud) { eta->exitcode = err; } - eta->done = true; + eta->done.set(); + ; } int EditorNode::execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok, bool p_close_on_errors) { @@ -5579,13 +5570,12 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p eta.path = p_path; eta.args = p_arguments; eta.exitcode = 255; - eta.done = false; int prev_len = 0; eta.execute_output_thread.start(_execute_thread, &eta); - while (!eta.done) { + while (!eta.done.is_set()) { { MutexLock lock(eta.execute_output_mutex); if (prev_len != eta.output.length()) { @@ -5849,7 +5839,7 @@ EditorNode::EditorNode() { register_exporters(); - GLOBAL_DEF("editor/main_run_args", ""); + GLOBAL_DEF("editor/run/main_run_args", ""); ClassDB::set_class_enabled("RootMotionView", true); @@ -6225,8 +6215,8 @@ EditorNode::EditorNode() { pm_export->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); p->add_separator(); - p->add_shortcut(ED_SHORTCUT("editor/undo", TTR("Undo"), KEY_MASK_CMD + KEY_Z), EDIT_UNDO, true); - p->add_shortcut(ED_SHORTCUT("editor/redo", TTR("Redo"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_Z), EDIT_REDO, true); + p->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO, true); + p->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO, true); p->add_separator(); p->add_shortcut(ED_SHORTCUT("editor/reload_saved_scene", TTR("Reload Saved Scene")), EDIT_RELOAD_SAVED_SCENE); @@ -6467,7 +6457,7 @@ EditorNode::EditorNode() { #warning needs to be reimplemented #endif #if 0 - String video_drivers = ProjectSettings::get_singleton()->get_custom_property_info()["rendering/quality/driver/driver_name"].hint_string; + String video_drivers = ProjectSettings::get_singleton()->get_custom_property_info()["rendering/driver/driver_name"].hint_string; String current_video_driver = OS::get_singleton()->get_video_driver_name(OS::get_singleton()->get_current_video_driver()); video_driver_current = 0; for (int i = 0; i < video_drivers.get_slice_count(","); i++) { diff --git a/editor/editor_node.h b/editor/editor_node.h index 8068ca89ee..91d873d16f 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -31,6 +31,7 @@ #ifndef EDITOR_NODE_H #define EDITOR_NODE_H +#include "core/templates/safe_refcount.h" #include "editor/editor_data.h" #include "editor/editor_export.h" #include "editor/editor_folding.h" @@ -111,7 +112,7 @@ public: Thread execute_output_thread; Mutex execute_output_mutex; int exitcode = 0; - volatile bool done = false; + SafeFlag done; }; private: @@ -128,7 +129,6 @@ private: FILE_SAVE_ALL_SCENES, FILE_SAVE_AND_RUN, FILE_SHOW_IN_FILESYSTEM, - FILE_IMPORT_SUBSCENE, FILE_EXPORT_PROJECT, FILE_EXPORT_MESH_LIBRARY, FILE_INSTALL_ANDROID_SOURCE, @@ -719,8 +719,6 @@ public: void save_resource(const Ref<Resource> &p_resource); void save_resource_as(const Ref<Resource> &p_resource, const String &p_at_path = String()); - void merge_from_scene() { _menu_option_confirm(FILE_IMPORT_SUBSCENE, false); } - void show_about() { _menu_option_confirm(HELP_ABOUT, false); } static bool has_unsaved_changes() { return singleton->unsaved_cache; } diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index 8056846f52..77288be614 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -206,8 +206,8 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref< } void EditorResourcePreview::_thread() { - exited = false; - while (!exit) { + exited.clear(); + while (!exit.is_set()) { preview_sem.wait(); preview_mutex.lock(); @@ -326,7 +326,7 @@ void EditorResourcePreview::_thread() { preview_mutex.unlock(); } } - exited = true; + exited.set(); } void EditorResourcePreview::queue_edited_resource_preview(const Ref<Resource> &p_res, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata) { @@ -430,9 +430,9 @@ void EditorResourcePreview::start() { void EditorResourcePreview::stop() { if (thread.is_started()) { - exit = true; + exit.set(); preview_sem.post(); - while (!exited) { + while (!exited.is_set()) { OS::get_singleton()->delay_usec(10000); RenderingServer::get_singleton()->sync(); //sync pending stuff, as thread may be blocked on visual server } @@ -443,8 +443,6 @@ void EditorResourcePreview::stop() { EditorResourcePreview::EditorResourcePreview() { singleton = this; order = 0; - exit = false; - exited = false; } EditorResourcePreview::~EditorResourcePreview() { diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h index 99c48967d8..c4e796dcf1 100644 --- a/editor/editor_resource_preview.h +++ b/editor/editor_resource_preview.h @@ -33,6 +33,7 @@ #include "core/os/semaphore.h" #include "core/os/thread.h" +#include "core/templates/safe_refcount.h" #include "scene/main/node.h" #include "scene/resources/texture.h" @@ -71,8 +72,8 @@ class EditorResourcePreview : public Node { Mutex preview_mutex; Semaphore preview_sem; Thread thread; - volatile bool exit; - volatile bool exited; + SafeFlag exit; + SafeFlag exited; struct Item { Ref<Texture2D> preview; diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp index fb4821a760..f81c87be9e 100644 --- a/editor/editor_sectioned_inspector.cpp +++ b/editor/editor_sectioned_inspector.cpp @@ -226,7 +226,7 @@ void SectionedInspector::update_category_list() { if (pi.usage & PROPERTY_USAGE_CATEGORY) { continue; - } else if (!(pi.usage & PROPERTY_USAGE_EDITOR)) { + } else if (!(pi.usage & PROPERTY_USAGE_EDITOR) || (restrict_to_basic && !(pi.usage & PROPERTY_USAGE_EDITOR_BASIC_SETTING))) { continue; } @@ -294,6 +294,12 @@ EditorInspector *SectionedInspector::get_inspector() { return inspector; } +void SectionedInspector::set_restrict_to_basic_settings(bool p_restrict) { + restrict_to_basic = p_restrict; + update_category_list(); + inspector->set_restrict_to_basic_settings(p_restrict); +} + SectionedInspector::SectionedInspector() : sections(memnew(Tree)), filter(memnew(SectionedInspectorFilter)), diff --git a/editor/editor_sectioned_inspector.h b/editor/editor_sectioned_inspector.h index 55fb94fecc..1068a4f932 100644 --- a/editor/editor_sectioned_inspector.h +++ b/editor/editor_sectioned_inspector.h @@ -51,6 +51,8 @@ class SectionedInspector : public HSplitContainer { String selected_category; + bool restrict_to_basic = false; + static void _bind_methods(); void _section_selected(); @@ -65,6 +67,7 @@ public: void set_current_section(const String &p_section); String get_current_section() const; + void set_restrict_to_basic_settings(bool p_restrict); void update_category_list(); SectionedInspector(); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index b6fa2f6d03..40647497af 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -31,6 +31,7 @@ #include "editor_settings.h" #include "core/config/project_settings.h" +#include "core/input/input_map.h" #include "core/io/certs_compressed.gen.h" #include "core/io/compression.h" #include "core/io/config_file.h" @@ -70,7 +71,7 @@ bool EditorSettings::_set(const StringName &p_name, const Variant &p_value) { bool EditorSettings::_set_only(const StringName &p_name, const Variant &p_value) { _THREAD_SAFE_METHOD_ - if (p_name.operator String() == "shortcuts") { + if (p_name == "shortcuts") { Array arr = p_value; ERR_FAIL_COND_V(arr.size() && arr.size() & 1, true); for (int i = 0; i < arr.size(); i += 2) { @@ -84,6 +85,24 @@ bool EditorSettings::_set_only(const StringName &p_name, const Variant &p_value) } return false; + } else if (p_name == "builtin_action_overrides") { + Array actions_arr = p_value; + for (int i = 0; i < actions_arr.size(); i++) { + Dictionary action_dict = actions_arr[i]; + + String name = action_dict["name"]; + Array events = action_dict["events"]; + + InputMap *im = InputMap::get_singleton(); + im->action_erase_events(name); + + builtin_action_overrides[name].clear(); + for (int ev_idx = 0; ev_idx < events.size(); ev_idx++) { + im->action_add_event(name, events[ev_idx]); + builtin_action_overrides[name].push_back(events[ev_idx]); + } + } + return false; } bool changed = false; @@ -118,11 +137,16 @@ bool EditorSettings::_set_only(const StringName &p_name, const Variant &p_value) bool EditorSettings::_get(const StringName &p_name, Variant &r_ret) const { _THREAD_SAFE_METHOD_ - if (p_name.operator String() == "shortcuts") { + if (p_name == "shortcuts") { Array arr; for (const Map<String, Ref<Shortcut>>::Element *E = shortcuts.front(); E; E = E->next()) { Ref<Shortcut> sc = E->get(); + if (builtin_action_overrides.has(E->key())) { + // This shortcut was auto-generated from built in actions: don't save. + continue; + } + if (optimize_save) { if (!sc->has_meta("original")) { continue; //this came from settings but is not any longer used @@ -139,6 +163,27 @@ bool EditorSettings::_get(const StringName &p_name, Variant &r_ret) const { } r_ret = arr; return true; + } else if (p_name == "builtin_action_overrides") { + Array actions_arr; + for (Map<String, List<Ref<InputEvent>>>::Element *E = builtin_action_overrides.front(); E; E = E->next()) { + List<Ref<InputEvent>> events = E->get(); + + // TODO: skip actions which are the same as the builtin. + Dictionary action_dict; + action_dict["name"] = E->key(); + + Array events_arr; + for (List<Ref<InputEvent>>::Element *I = events.front(); I; I = I->next()) { + events_arr.push_back(I->get()); + } + + action_dict["events"] = events_arr; + + actions_arr.push_back(action_dict); + } + + r_ret = actions_arr; + return true; } const VariantContainer *v = props.getptr(p_name); @@ -220,6 +265,7 @@ void EditorSettings::_get_property_list(List<PropertyInfo> *p_list) const { } p_list->push_back(PropertyInfo(Variant::ARRAY, "shortcuts", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); //do not edit + p_list->push_back(PropertyInfo(Variant::ARRAY, "builtin_action_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); } void EditorSettings::_add_property_info_bind(const Dictionary &p_info) { @@ -1285,7 +1331,7 @@ String EditorSettings::get_script_templates_dir() const { } String EditorSettings::get_project_script_templates_dir() const { - return ProjectSettings::get_singleton()->get("editor/script_templates_search_path"); + return ProjectSettings::get_singleton()->get("editor/script/templates_search_path"); } // Cache directory @@ -1539,12 +1585,39 @@ bool EditorSettings::is_shortcut(const String &p_name, const Ref<InputEvent> &p_ } Ref<Shortcut> EditorSettings::get_shortcut(const String &p_name) const { - const Map<String, Ref<Shortcut>>::Element *E = shortcuts.find(p_name); - if (!E) { - return Ref<Shortcut>(); + const Map<String, Ref<Shortcut>>::Element *SC = shortcuts.find(p_name); + if (SC) { + return SC->get(); + } + + // If no shortcut with the provided name is found in the list, check the built-in shortcuts. + // Use the first item in the action list for the shortcut event, since a shortcut can only have 1 linked event. + + Ref<Shortcut> sc; + const Map<String, List<Ref<InputEvent>>>::Element *builtin_override = builtin_action_overrides.find(p_name); + if (builtin_override) { + sc.instance(); + sc->set_shortcut(builtin_override->get().front()->get()); + sc->set_name(InputMap::get_singleton()->get_builtin_display_name(p_name)); + } + + // If there was no override, check the default builtins to see if it has an InputEvent for the provided name. + if (sc.is_null()) { + const OrderedHashMap<String, List<Ref<InputEvent>>>::ConstElement builtin_default = InputMap::get_singleton()->get_builtins().find(p_name); + if (builtin_default) { + sc.instance(); + sc->set_shortcut(builtin_default.get().front()->get()); + sc->set_name(InputMap::get_singleton()->get_builtin_display_name(p_name)); + } + } + + if (sc.is_valid()) { + // Add the shortcut to the list. + shortcuts[p_name] = sc; + return sc; } - return E->get(); + return Ref<Shortcut>(); } void EditorSettings::get_shortcut_list(List<String> *r_shortcuts) { @@ -1610,6 +1683,66 @@ Ref<Shortcut> ED_SHORTCUT(const String &p_path, const String &p_name, uint32_t p return sc; } +void EditorSettings::set_builtin_action_override(const String &p_name, const Array &p_events) { + List<Ref<InputEvent>> event_list; + + // Override the whole list, since events may have their order changed or be added, removed or edited. + InputMap::get_singleton()->action_erase_events(p_name); + for (int i = 0; i < p_events.size(); i++) { + event_list.push_back(p_events[i]); + InputMap::get_singleton()->action_add_event(p_name, p_events[i]); + } + + // Check if the provided event array is same as built-in. If it is, it does not need to be added to the overrides. + // Note that event order must also be the same. + bool same_as_builtin = true; + OrderedHashMap<String, List<Ref<InputEvent>>>::ConstElement builtin_default = InputMap::get_singleton()->get_builtins().find(p_name); + if (builtin_default) { + List<Ref<InputEvent>> builtin_events = builtin_default.get(); + + if (p_events.size() == builtin_events.size()) { + int event_idx = 0; + + // Check equality of each event. + for (List<Ref<InputEvent>>::Element *E = builtin_events.front(); E; E = E->next()) { + if (!E->get()->shortcut_match(p_events[event_idx])) { + same_as_builtin = false; + break; + } + event_idx++; + } + } else { + same_as_builtin = false; + } + } + + if (same_as_builtin && builtin_action_overrides.has(p_name)) { + builtin_action_overrides.erase(p_name); + } else { + builtin_action_overrides[p_name] = event_list; + } + + // Update the shortcut (if it is used somewhere in the editor) to be the first event of the new list. + if (shortcuts.has(p_name)) { + shortcuts[p_name]->set_shortcut(event_list.front()->get()); + } +} + +const Array EditorSettings::get_builtin_action_overrides(const String &p_name) const { + const Map<String, List<Ref<InputEvent>>>::Element *AO = builtin_action_overrides.find(p_name); + if (AO) { + Array event_array; + + List<Ref<InputEvent>> events_list = AO->get(); + for (List<Ref<InputEvent>>::Element *E = events_list.front(); E; E = E->next()) { + event_array.push_back(E->get()); + } + return event_array; + } + + return Array(); +} + void EditorSettings::notify_changes() { _THREAD_SAFE_METHOD_ @@ -1648,6 +1781,8 @@ void EditorSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("set_recent_dirs", "dirs"), &EditorSettings::set_recent_dirs); ClassDB::bind_method(D_METHOD("get_recent_dirs"), &EditorSettings::get_recent_dirs); + ClassDB::bind_method(D_METHOD("set_builtin_action_override", "name", "actions_list"), &EditorSettings::set_builtin_action_override); + ADD_SIGNAL(MethodInfo("settings_changed")); BIND_CONSTANT(NOTIFICATION_EDITOR_SETTINGS_CHANGED); diff --git a/editor/editor_settings.h b/editor/editor_settings.h index 616a938a86..e5f8527faf 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -84,7 +84,8 @@ private: int last_order; Ref<Resource> clipboard; - Map<String, Ref<Shortcut>> shortcuts; + mutable Map<String, Ref<Shortcut>> shortcuts; + Map<String, List<Ref<InputEvent>>> builtin_action_overrides; String resource_path; String settings_dir; @@ -186,6 +187,9 @@ public: Ref<Shortcut> get_shortcut(const String &p_name) const; void get_shortcut_list(List<String> *r_shortcuts); + void set_builtin_action_override(const String &p_name, const Array &p_events); + const Array get_builtin_action_overrides(const String &p_name) const; + void notify_changes(); EditorSettings(); diff --git a/editor/editor_sub_scene.cpp b/editor/editor_sub_scene.cpp deleted file mode 100644 index e319fbff52..0000000000 --- a/editor/editor_sub_scene.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/*************************************************************************/ -/* editor_sub_scene.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "editor_sub_scene.h" - -#include "editor/editor_node.h" -#include "scene/gui/margin_container.h" -#include "scene/resources/packed_scene.h" - -void EditorSubScene::_path_selected(const String &p_path) { - path->set_text(p_path); - _path_changed(p_path); -} - -void EditorSubScene::_path_changed(const String &p_path) { - tree->clear(); - - if (scene) { - memdelete(scene); - scene = nullptr; - } - - if (p_path == "") { - return; - } - - Ref<PackedScene> ps = ResourceLoader::load(p_path, "PackedScene"); - - if (ps.is_null()) { - return; - } - - scene = ps->instance(); - if (!scene) { - return; - } - - _fill_tree(scene, nullptr); -} - -void EditorSubScene::_path_browse() { - file_dialog->popup_file_dialog(); -} - -void EditorSubScene::_notification(int p_what) { - if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { - if (is_visible() && scene == nullptr) { - _path_browse(); - } - } -} - -void EditorSubScene::_fill_tree(Node *p_node, TreeItem *p_parent) { - TreeItem *it = tree->create_item(p_parent); - it->set_metadata(0, p_node); - it->set_text(0, p_node->get_name()); - it->set_editable(0, false); - it->set_selectable(0, true); - it->set_icon(0, EditorNode::get_singleton()->get_object_icon(p_node, "Node")); - - for (int i = 0; i < p_node->get_child_count(); i++) { - Node *c = p_node->get_child(i); - if (c->get_owner() != scene) { - continue; - } - _fill_tree(c, it); - } -} - -void EditorSubScene::_selected_changed() { - TreeItem *item = tree->get_selected(); - ERR_FAIL_COND(!item); - Node *n = item->get_metadata(0); - - if (!n || !selection.find(n)) { - selection.clear(); - is_root = false; - } -} - -void EditorSubScene::_item_multi_selected(Object *p_object, int p_cell, bool p_selected) { - if (!is_root) { - TreeItem *item = Object::cast_to<TreeItem>(p_object); - ERR_FAIL_COND(!item); - - Node *n = item->get_metadata(0); - - if (!n) { - return; - } - if (p_selected) { - if (n == scene) { - is_root = true; - selection.clear(); - } - selection.push_back(n); - } else { - List<Node *>::Element *E = selection.find(n); - - if (E) { - selection.erase(E); - } - } - } -} - -void EditorSubScene::_item_activated() { - _ok_pressed(); // From AcceptDialog. -} - -void EditorSubScene::_remove_selection_child(Node *p_node) { - if (p_node->get_child_count() > 0) { - for (int i = 0; i < p_node->get_child_count(); i++) { - Node *c = p_node->get_child(i); - List<Node *>::Element *E = selection.find(c); - if (E) { - selection.move_to_back(E); - selection.pop_back(); - } - if (c->get_child_count() > 0) { - _remove_selection_child(c); - } - } - } -} - -void EditorSubScene::ok_pressed() { - if (selection.size() <= 0) { - return; - } - for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { - Node *c = E->get(); - _remove_selection_child(c); - } - emit_signal("subscene_selected"); - hide(); - clear(); -} - -void EditorSubScene::_reown(Node *p_node, List<Node *> *p_to_reown) { - if (p_node == scene) { - scene->set_filename(""); - p_to_reown->push_back(p_node); - } else if (p_node->get_owner() == scene) { - p_to_reown->push_back(p_node); - } - - for (int i = 0; i < p_node->get_child_count(); i++) { - Node *c = p_node->get_child(i); - _reown(c, p_to_reown); - } -} - -void EditorSubScene::move(Node *p_new_parent, Node *p_new_owner) { - if (!scene) { - return; - } - - if (selection.size() <= 0) { - return; - } - - for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { - Node *selnode = E->get(); - if (!selnode) { - return; - } - List<Node *> to_reown; - _reown(selnode, &to_reown); - if (selnode != scene) { - selnode->get_parent()->remove_child(selnode); - } - - p_new_parent->add_child(selnode); - for (List<Node *>::Element *F = to_reown.front(); F; F = F->next()) { - F->get()->set_owner(p_new_owner); - } - } - if (!is_root) { - memdelete(scene); - } - scene = nullptr; - //return selnode; -} - -void EditorSubScene::clear() { - path->set_text(""); - _path_changed(""); -} - -void EditorSubScene::_bind_methods() { - ADD_SIGNAL(MethodInfo("subscene_selected")); -} - -EditorSubScene::EditorSubScene() { - scene = nullptr; - is_root = false; - - set_title(TTR("Select Node(s) to Import")); - set_hide_on_ok(false); - - VBoxContainer *vb = memnew(VBoxContainer); - add_child(vb); - //set_child_rect(vb); - - HBoxContainer *hb = memnew(HBoxContainer); - path = memnew(LineEdit); - path->connect("text_entered", callable_mp(this, &EditorSubScene::_path_changed)); - hb->add_child(path); - path->set_h_size_flags(Control::SIZE_EXPAND_FILL); - Button *b = memnew(Button); - b->set_text(TTR("Browse")); - hb->add_child(b); - b->connect("pressed", callable_mp(this, &EditorSubScene::_path_browse)); - vb->add_margin_child(TTR("Scene Path:"), hb); - - tree = memnew(Tree); - tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); - vb->add_margin_child(TTR("Import From Node:"), tree, true); - tree->set_select_mode(Tree::SELECT_MULTI); - tree->connect("multi_selected", callable_mp(this, &EditorSubScene::_item_multi_selected)); - //tree->connect("nothing_selected", this, "_deselect_items"); - tree->connect("cell_selected", callable_mp(this, &EditorSubScene::_selected_changed)); - - tree->connect("item_activated", callable_mp(this, &EditorSubScene::_item_activated), make_binds(), CONNECT_DEFERRED); - - file_dialog = memnew(EditorFileDialog); - List<String> extensions; - ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions); - - for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - file_dialog->add_filter("*." + E->get()); - } - - file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); - add_child(file_dialog); - file_dialog->connect("file_selected", callable_mp(this, &EditorSubScene::_path_selected)); -} diff --git a/editor/editor_sub_scene.h b/editor/editor_sub_scene.h deleted file mode 100644 index 428bd5a40e..0000000000 --- a/editor/editor_sub_scene.h +++ /dev/null @@ -1,71 +0,0 @@ -/*************************************************************************/ -/* editor_sub_scene.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef EDITOR_SUB_SCENE_H -#define EDITOR_SUB_SCENE_H - -#include "editor/editor_file_dialog.h" -#include "scene/gui/dialogs.h" -#include "scene/gui/tree.h" - -class EditorSubScene : public ConfirmationDialog { - GDCLASS(EditorSubScene, ConfirmationDialog); - - List<Node *> selection; - LineEdit *path; - Tree *tree; - Node *scene; - bool is_root; - - EditorFileDialog *file_dialog; - - void _fill_tree(Node *p_node, TreeItem *p_parent); - void _selected_changed(); - void _item_multi_selected(Object *p_object, int p_cell, bool p_selected); - void _item_activated(); - void _remove_selection_child(Node *p_node); - void _reown(Node *p_node, List<Node *> *p_to_reown); - - void ok_pressed() override; - -protected: - void _notification(int p_what); - static void _bind_methods(); - void _path_browse(); - void _path_selected(const String &p_path); - void _path_changed(const String &p_path); - -public: - void move(Node *p_new_parent, Node *p_new_owner); - void clear(); - EditorSubScene(); -}; - -#endif // EDITOR_SUB_SCENE_H diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 27e539d71c..47079a92b7 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -463,7 +463,7 @@ void FindInFilesDialog::_notification(int p_what) { for (int i = 0; i < _filters_container->get_child_count(); i++) { _filters_container->get_child(i)->queue_delete(); } - Array exts = ProjectSettings::get_singleton()->get("editor/search_in_file_extensions"); + Array exts = ProjectSettings::get_singleton()->get("editor/script/search_in_file_extensions"); for (int i = 0; i < exts.size(); ++i) { CheckBox *cb = memnew(CheckBox); cb->set_text(exts[i]); diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index 3139ef5146..6d2215c379 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -391,8 +391,8 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const bool ok_on_pc = false; bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995); bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGB565); - bool can_bptc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc"); - bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc"); + bool can_bptc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_bptc"); + bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_s3tc"); if (can_bptc) { formats_imported.push_back("bptc"); //needs to be aded anyway @@ -447,13 +447,13 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const ok_on_pc = true; } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) { + if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2")) { _save_tex(slices, p_save_path + ".etc2." + extension, compress_mode, lossy, Image::COMPRESS_ETC2, csource, used_channels, mipmaps, true); r_platform_variants->push_back("etc2"); formats_imported.push_back("etc2"); } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) { + if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_pvrtc")) { _save_tex(slices, p_save_path + ".etc2." + extension, compress_mode, lossy, Image::COMPRESS_ETC2, csource, used_channels, mipmaps, true); r_platform_variants->push_back("pvrtc"); formats_imported.push_back("pvrtc"); @@ -492,7 +492,7 @@ String ResourceImporterLayeredTexture::get_import_settings_string() const { int index = 0; while (compression_formats[index]) { - String setting_path = "rendering/vram_compression/import_" + String(compression_formats[index]); + String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]); bool test = ProjectSettings::get_singleton()->get(setting_path); if (test) { s += String(compression_formats[index]); @@ -524,7 +524,7 @@ bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_p int index = 0; bool valid = true; while (compression_formats[index]) { - String setting_path = "rendering/vram_compression/import_" + String(compression_formats[index]); + String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]); bool test = ProjectSettings::get_singleton()->get(setting_path); if (test) { if (formats_imported.find(compression_formats[index]) == -1) { diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index eb16e873e6..de8031af35 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -172,7 +172,7 @@ bool ResourceImporterTexture::get_option_visibility(const String &p_option, cons if (compress_mode < COMPRESS_VRAM_COMPRESSED) { return false; } - if (!ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc")) { + if (!ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_bptc")) { return false; } } @@ -473,8 +473,8 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String bool ok_on_pc = false; bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995); bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGB565); - bool can_bptc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc"); - bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc"); + bool can_bptc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_bptc"); + bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_s3tc"); if (can_bptc) { //add to the list anyway @@ -524,19 +524,19 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String ok_on_pc = true; } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) { + if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2")) { _save_stex(image, p_save_path + ".etc2.stex", compress_mode, lossy, Image::COMPRESS_ETC2, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel); r_platform_variants->push_back("etc2"); formats_imported.push_back("etc2"); } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc")) { + if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc")) { _save_stex(image, p_save_path + ".etc.stex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel); r_platform_variants->push_back("etc"); formats_imported.push_back("etc"); } - if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) { + if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_pvrtc")) { _save_stex(image, p_save_path + ".pvrtc.stex", compress_mode, lossy, Image::COMPRESS_PVRTC1_4, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel); r_platform_variants->push_back("pvrtc"); formats_imported.push_back("pvrtc"); @@ -574,7 +574,7 @@ String ResourceImporterTexture::get_import_settings_string() const { int index = 0; while (compression_formats[index]) { - String setting_path = "rendering/vram_compression/import_" + String(compression_formats[index]); + String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]); bool test = ProjectSettings::get_singleton()->get(setting_path); if (test) { s += String(compression_formats[index]); @@ -606,7 +606,7 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co int index = 0; bool valid = true; while (compression_formats[index]) { - String setting_path = "rendering/vram_compression/import_" + String(compression_formats[index]); + String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]); bool test = ProjectSettings::get_singleton()->get(setting_path); if (test) { if (formats_imported.find(compression_formats[index]) == -1) { diff --git a/editor/import/scene_importer_mesh.cpp b/editor/import/scene_importer_mesh.cpp index 78a7cd84f1..46eb4e4fdc 100644 --- a/editor/import/scene_importer_mesh.cpp +++ b/editor/import/scene_importer_mesh.cpp @@ -444,7 +444,7 @@ void EditorSceneImporterMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &EditorSceneImporterMesh::set_blend_shape_mode); ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &EditorSceneImporterMesh::get_blend_shape_mode); - ClassDB::bind_method(D_METHOD("add_surface", "primitive", "arrays", "blend_shapes", "lods", "material"), &EditorSceneImporterMesh::add_surface, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(Ref<Material>()), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("add_surface", "primitive", "arrays", "blend_shapes", "lods", "material", "name"), &EditorSceneImporterMesh::add_surface, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(Ref<Material>()), DEFVAL(String())); ClassDB::bind_method(D_METHOD("get_surface_count"), &EditorSceneImporterMesh::get_surface_count); ClassDB::bind_method(D_METHOD("get_surface_primitive_type", "surface_idx"), &EditorSceneImporterMesh::get_surface_primitive_type); diff --git a/editor/input_map_editor.cpp b/editor/input_map_editor.cpp deleted file mode 100644 index 9a5e7d164c..0000000000 --- a/editor/input_map_editor.cpp +++ /dev/null @@ -1,1033 +0,0 @@ -/*************************************************************************/ -/* input_map_editor.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "input_map_editor.h" - -#include "core/input/input_map.h" -#include "core/os/keyboard.h" -#include "editor/editor_node.h" -#include "editor/editor_scale.h" - -void InputMapEditor::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - action_add_error->add_theme_color_override("font_color", input_editor->get_theme_color("error_color", "Editor")); - popup_add->add_icon_item(input_editor->get_theme_icon("Keyboard", "EditorIcons"), TTR("Key"), INPUT_KEY); - popup_add->add_icon_item(input_editor->get_theme_icon("KeyboardPhysical", "EditorIcons"), TTR("Physical Key"), INPUT_KEY_PHYSICAL); - popup_add->add_icon_item(input_editor->get_theme_icon("JoyButton", "EditorIcons"), TTR("Joy Button"), INPUT_JOY_BUTTON); - popup_add->add_icon_item(input_editor->get_theme_icon("JoyAxis", "EditorIcons"), TTR("Joy Axis"), INPUT_JOY_MOTION); - popup_add->add_icon_item(input_editor->get_theme_icon("Mouse", "EditorIcons"), TTR("Mouse Button"), INPUT_MOUSE_BUTTON); - _update_actions(); - } break; - case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - action_add_error->add_theme_color_override("font_color", input_editor->get_theme_color("error_color", "Editor")); - popup_add->set_item_icon(popup_add->get_item_index(INPUT_KEY), input_editor->get_theme_icon("Keyboard", "EditorIcons")); - popup_add->set_item_icon(popup_add->get_item_index(INPUT_KEY_PHYSICAL), input_editor->get_theme_icon("KeyboardPhysical", "EditorIcons")); - popup_add->set_item_icon(popup_add->get_item_index(INPUT_JOY_BUTTON), input_editor->get_theme_icon("JoyButton", "EditorIcons")); - popup_add->set_item_icon(popup_add->get_item_index(INPUT_JOY_MOTION), input_editor->get_theme_icon("JoyAxis", "EditorIcons")); - popup_add->set_item_icon(popup_add->get_item_index(INPUT_MOUSE_BUTTON), input_editor->get_theme_icon("Mouse", "EditorIcons")); - _update_actions(); - } break; - } -} - -static bool _validate_action_name(const String &p_name) { - const char32_t *cstr = p_name.get_data(); - for (int i = 0; cstr[i]; i++) { - if (cstr[i] == '/' || cstr[i] == ':' || cstr[i] == '"' || - cstr[i] == '=' || cstr[i] == '\\' || cstr[i] < 32) { - return false; - } - } - return true; -} - -void InputMapEditor::_action_selected() { - TreeItem *ti = input_editor->get_selected(); - if (!ti || !ti->is_editable(0)) { - return; - } - - add_at = "input/" + ti->get_text(0); - edit_idx = -1; -} - -void InputMapEditor::_action_edited() { - TreeItem *ti = input_editor->get_selected(); - if (!ti) { - return; - } - - if (input_editor->get_selected_column() == 0) { - String new_name = ti->get_text(0); - String old_name = add_at.substr(add_at.find("/") + 1, add_at.length()); - - if (new_name == old_name) { - return; - } - - if (new_name == "" || !_validate_action_name(new_name)) { - ti->set_text(0, old_name); - add_at = "input/" + old_name; - - message->set_text(TTR("Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or '\"'")); - message->popup_centered(Size2(300, 100) * EDSCALE); - return; - } - - String action_prop = "input/" + new_name; - - if (ProjectSettings::get_singleton()->has_setting(action_prop)) { - ti->set_text(0, old_name); - add_at = "input/" + old_name; - - message->set_text(vformat(TTR("An action with the name '%s' already exists."), new_name)); - message->popup_centered(Size2(300, 100) * EDSCALE); - return; - } - - int order = ProjectSettings::get_singleton()->get_order(add_at); - Dictionary action = ProjectSettings::get_singleton()->get(add_at); - - setting = true; - undo_redo->create_action(TTR("Rename Input Action Event")); - undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", add_at); - undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", action_prop, action); - undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", action_prop, order); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", action_prop); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", add_at, action); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", add_at, order); - undo_redo->add_do_method(this, "_update_actions"); - undo_redo->add_undo_method(this, "_update_actions"); - undo_redo->add_do_method(this, "emit_signal", inputmap_changed); - undo_redo->add_undo_method(this, "emit_signal", inputmap_changed); - undo_redo->commit_action(); - setting = false; - - add_at = action_prop; - } else if (input_editor->get_selected_column() == 1) { - String name = "input/" + ti->get_text(0); - Dictionary old_action = ProjectSettings::get_singleton()->get(name); - Dictionary new_action = old_action.duplicate(); - new_action["deadzone"] = ti->get_range(1); - - undo_redo->create_action(TTR("Change Action deadzone")); - undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, new_action); - undo_redo->add_do_method(this, "emit_signal", inputmap_changed); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", name, old_action); - undo_redo->add_undo_method(this, "emit_signal", inputmap_changed); - undo_redo->commit_action(); - } -} - -void InputMapEditor::_device_input_add() { - Ref<InputEvent> ie; - String name = add_at; - int idx = edit_idx; - Dictionary old_val = ProjectSettings::get_singleton()->get(name); - Dictionary action = old_val.duplicate(); - Array events = action["events"]; - - switch (add_type) { - case INPUT_MOUSE_BUTTON: { - Ref<InputEventMouseButton> mb; - mb.instance(); - mb->set_button_index(device_index->get_selected() + 1); - mb->set_device(_get_current_device()); - - for (int i = 0; i < events.size(); i++) { - Ref<InputEventMouseButton> aie = events[i]; - if (aie.is_null()) { - continue; - } - if (aie->get_device() == mb->get_device() && aie->get_button_index() == mb->get_button_index()) { - return; - } - } - - ie = mb; - - } break; - case INPUT_JOY_MOTION: { - Ref<InputEventJoypadMotion> jm; - jm.instance(); - jm->set_axis(device_index->get_selected() >> 1); - jm->set_axis_value((device_index->get_selected() & 1) ? 1 : -1); - jm->set_device(_get_current_device()); - - for (int i = 0; i < events.size(); i++) { - Ref<InputEventJoypadMotion> aie = events[i]; - if (aie.is_null()) { - continue; - } - - if (aie->get_device() == jm->get_device() && aie->get_axis() == jm->get_axis() && aie->get_axis_value() == jm->get_axis_value()) { - return; - } - } - - ie = jm; - - } break; - case INPUT_JOY_BUTTON: { - Ref<InputEventJoypadButton> jb; - jb.instance(); - - jb->set_button_index(device_index->get_selected()); - jb->set_device(_get_current_device()); - - for (int i = 0; i < events.size(); i++) { - Ref<InputEventJoypadButton> aie = events[i]; - if (aie.is_null()) { - continue; - } - if (aie->get_device() == jb->get_device() && aie->get_button_index() == jb->get_button_index()) { - return; - } - } - ie = jb; - - } break; - default: { - } - } - - if (idx < 0 || idx >= events.size()) { - events.push_back(ie); - } else { - events[idx] = ie; - } - action["events"] = events; - - undo_redo->create_action(TTR("Add Input Action Event")); - undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, action); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", name, old_val); - undo_redo->add_do_method(this, "_update_actions"); - undo_redo->add_undo_method(this, "_update_actions"); - undo_redo->add_do_method(this, "emit_signal", inputmap_changed); - undo_redo->add_undo_method(this, "emit_signal", inputmap_changed); - undo_redo->commit_action(); - - _show_last_added(ie, name); -} - -void InputMapEditor::_set_current_device(int i_device) { - device_id->select(i_device + 1); -} - -int InputMapEditor::_get_current_device() { - return device_id->get_selected() - 1; -} - -String InputMapEditor::_get_device_string(int i_device) { - if (i_device == InputMap::ALL_DEVICES) { - return TTR("All Devices"); - } - return TTR("Device") + " " + itos(i_device); -} - -void InputMapEditor::_press_a_key_confirm() { - if (last_wait_for_key.is_null()) { - return; - } - - Ref<InputEventKey> ie; - ie.instance(); - if (press_a_key_physical) { - ie->set_physical_keycode(last_wait_for_key->get_physical_keycode()); - ie->set_keycode(0); - } else { - ie->set_physical_keycode(0); - ie->set_keycode(last_wait_for_key->get_keycode()); - } - ie->set_shift(last_wait_for_key->get_shift()); - ie->set_alt(last_wait_for_key->get_alt()); - ie->set_control(last_wait_for_key->get_control()); - ie->set_metakey(last_wait_for_key->get_metakey()); - - String name = add_at; - int idx = edit_idx; - - Dictionary old_val = ProjectSettings::get_singleton()->get(name); - Dictionary action = old_val.duplicate(); - Array events = action["events"]; - - for (int i = 0; i < events.size(); i++) { - Ref<InputEventKey> aie = events[i]; - if (aie.is_null()) { - continue; - } - if (!press_a_key_physical) { - if (aie->get_keycode_with_modifiers() == ie->get_keycode_with_modifiers()) { - return; - } - } else { - if (aie->get_physical_keycode_with_modifiers() == ie->get_physical_keycode_with_modifiers()) { - return; - } - } - } - - if (idx < 0 || idx >= events.size()) { - events.push_back(ie); - } else { - events[idx] = ie; - } - action["events"] = events; - - undo_redo->create_action(TTR("Add Input Action Event")); - undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, action); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", name, old_val); - undo_redo->add_do_method(this, "_update_actions"); - undo_redo->add_undo_method(this, "_update_actions"); - undo_redo->add_do_method(this, "emit_signal", inputmap_changed); - undo_redo->add_undo_method(this, "emit_signal", inputmap_changed); - undo_redo->commit_action(); - - _show_last_added(ie, name); -} - -void InputMapEditor::_show_last_added(const Ref<InputEvent> &p_event, const String &p_name) { - TreeItem *r = input_editor->get_root(); - - String name = p_name; - name.erase(0, 6); - if (!r) { - return; - } - r = r->get_children(); - if (!r) { - return; - } - bool found = false; - while (r) { - if (r->get_text(0) != name) { - r = r->get_next(); - continue; - } - TreeItem *child = r->get_children(); - while (child) { - Variant input = child->get_meta("__input"); - if (p_event == input) { - r->set_collapsed(false); - child->select(0); - found = true; - break; - } - child = child->get_next(); - } - if (found) { - break; - } - r = r->get_next(); - } - - if (found) { - input_editor->ensure_cursor_is_visible(); - } -} - -// Maps to 2*axis if value is neg, or + 1 if value is pos. -static const char *_joy_axis_descriptions[JOY_AXIS_MAX * 2] = { - TTRC("Left Stick Left, Joystick 0 Left"), - TTRC("Left Stick Right, Joystick 0 Right"), - TTRC("Left Stick Up, Joystick 0 Up"), - TTRC("Left Stick Down, Joystick 0 Down"), - TTRC("Right Stick Left, Joystick 1 Left"), - TTRC("Right Stick Right, Joystick 1 Right"), - TTRC("Right Stick Up, Joystick 1 Up"), - TTRC("Right Stick Down, Joystick 1 Down"), - TTRC("Joystick 2 Left"), - TTRC("Left Trigger, Sony L2, Xbox LT, Joystick 2 Right"), - TTRC("Joystick 2 Up"), - TTRC("Right Trigger, Sony R2, Xbox RT, Joystick 2 Down"), - TTRC("Joystick 3 Left"), - TTRC("Joystick 3 Right"), - TTRC("Joystick 3 Up"), - TTRC("Joystick 3 Down"), - TTRC("Joystick 4 Left"), - TTRC("Joystick 4 Right"), - TTRC("Joystick 4 Up"), - TTRC("Joystick 4 Down"), -}; - -// Separate from `InputEvent::as_text()` since the descriptions need to be different for the input map editor. See #43660. -String InputMapEditor::_get_joypad_motion_event_text(const Ref<InputEventJoypadMotion> &p_event) { - ERR_FAIL_COND_V_MSG(p_event.is_null(), String(), "Provided event is not a valid instance of InputEventJoypadMotion"); - - String desc = TTR("Unknown Joypad Axis"); - if (p_event->get_axis() < JOY_AXIS_MAX) { - desc = RTR(_joy_axis_descriptions[2 * p_event->get_axis() + (p_event->get_axis_value() < 0 ? 0 : 1)]); - } - - return vformat("Joypad Axis %s %s (%s)", itos(p_event->get_axis()), p_event->get_axis_value() < 0 ? "-" : "+", desc); -} - -void InputMapEditor::_wait_for_key(const Ref<InputEvent> &p_event) { - Ref<InputEventKey> k = p_event; - - if (k.is_valid() && k->is_pressed() && k->get_keycode() != 0) { - last_wait_for_key = p_event; - const String str = (press_a_key_physical) ? keycode_get_string(k->get_physical_keycode_with_modifiers()) + TTR(" (Physical)") : keycode_get_string(k->get_keycode_with_modifiers()); - - press_a_key_label->set_text(str); - press_a_key->get_ok_button()->set_disabled(false); - press_a_key->set_input_as_handled(); - } -} - -void InputMapEditor::_edit_item(Ref<InputEvent> p_exiting_event) { - InputType ie_type; - - if ((Ref<InputEventKey>(p_exiting_event)).is_valid()) { - if ((Ref<InputEventKey>(p_exiting_event))->get_keycode() != 0) { - ie_type = INPUT_KEY; - } else { - ie_type = INPUT_KEY_PHYSICAL; - } - } else if ((Ref<InputEventJoypadButton>(p_exiting_event)).is_valid()) { - ie_type = INPUT_JOY_BUTTON; - } else if ((Ref<InputEventMouseButton>(p_exiting_event)).is_valid()) { - ie_type = INPUT_MOUSE_BUTTON; - } else if ((Ref<InputEventJoypadMotion>(p_exiting_event)).is_valid()) { - ie_type = INPUT_JOY_MOTION; - } else { - return; - } - - _add_item(ie_type, p_exiting_event); -} - -void InputMapEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_event) { - add_type = InputType(p_item); - - switch (add_type) { - case INPUT_KEY: { - press_a_key_physical = false; - press_a_key_label->set_text(TTR("Press a Key...")); - press_a_key->get_ok_button()->set_disabled(true); - last_wait_for_key = Ref<InputEvent>(); - press_a_key->popup_centered(Size2(250, 80) * EDSCALE); - //press_a_key->grab_focus(); - - } break; - case INPUT_KEY_PHYSICAL: { - press_a_key_physical = true; - press_a_key_label->set_text(TTR("Press a Key...")); - - last_wait_for_key = Ref<InputEvent>(); - press_a_key->popup_centered(Size2(250, 80) * EDSCALE); - press_a_key->grab_focus(); - - } break; - case INPUT_MOUSE_BUTTON: { - device_index_label->set_text(TTR("Mouse Button Index:")); - device_index->clear(); - device_index->add_item(TTR("Left Button")); - device_index->add_item(TTR("Right Button")); - device_index->add_item(TTR("Middle Button")); - device_index->add_item(TTR("Wheel Up Button")); - device_index->add_item(TTR("Wheel Down Button")); - device_index->add_item(TTR("Wheel Left Button")); - device_index->add_item(TTR("Wheel Right Button")); - device_index->add_item(TTR("X Button 1")); - device_index->add_item(TTR("X Button 2")); - device_input->popup_centered(Size2(350, 95) * EDSCALE); - - Ref<InputEventMouseButton> mb = p_exiting_event; - if (mb.is_valid()) { - device_index->select(mb->get_button_index() - 1); - _set_current_device(mb->get_device()); - device_input->get_ok_button()->set_text(TTR("Change")); - } else { - _set_current_device(0); - device_input->get_ok_button()->set_text(TTR("Add")); - } - - } break; - case INPUT_JOY_MOTION: { - device_index_label->set_text(TTR("Joypad Axis Index:")); - device_index->clear(); - for (int i = 0; i < JOY_AXIS_MAX * 2; i++) { - Ref<InputEventJoypadMotion> jm; - jm.instance(); - jm->set_axis(i / 2); - jm->set_axis_value((i & 1) ? 1 : -1); - device_index->add_item(_get_joypad_motion_event_text(jm)); - } - device_input->popup_centered(Size2(350, 95) * EDSCALE); - - Ref<InputEventJoypadMotion> jm = p_exiting_event; - if (jm.is_valid()) { - device_index->select(jm->get_axis() * 2 + (jm->get_axis_value() > 0 ? 1 : 0)); - _set_current_device(jm->get_device()); - device_input->get_ok_button()->set_text(TTR("Change")); - } else { - _set_current_device(0); - device_input->get_ok_button()->set_text(TTR("Add")); - } - - } break; - case INPUT_JOY_BUTTON: { - device_index_label->set_text(TTR("Joypad Button Index:")); - device_index->clear(); - for (int i = 0; i < JOY_BUTTON_MAX; i++) { - Ref<InputEventJoypadButton> jb; - jb.instance(); - jb->set_button_index(i); - device_index->add_item(jb->as_text()); - } - device_input->popup_centered(Size2(350, 95) * EDSCALE); - - Ref<InputEventJoypadButton> jb = p_exiting_event; - if (jb.is_valid()) { - device_index->select(jb->get_button_index()); - _set_current_device(jb->get_device()); - device_input->get_ok_button()->set_text(TTR("Change")); - } else { - _set_current_device(0); - device_input->get_ok_button()->set_text(TTR("Add")); - } - - } break; - default: { - } - } -} - -void InputMapEditor::_action_activated() { - TreeItem *ti = input_editor->get_selected(); - - if (!ti || ti->get_parent() == input_editor->get_root()) { - return; - } - - String name = "input/" + ti->get_parent()->get_text(0); - Dictionary action = ProjectSettings::get_singleton()->get(name); - Array events = action["events"]; - int idx = ti->get_metadata(0); - - ERR_FAIL_INDEX(idx, events.size()); - Ref<InputEvent> event = events[idx]; - if (event.is_null()) { - return; - } - - add_at = name; - edit_idx = idx; - _edit_item(event); -} - -void InputMapEditor::_action_button_pressed(Object *p_obj, int p_column, int p_id) { - TreeItem *ti = Object::cast_to<TreeItem>(p_obj); - - ERR_FAIL_COND(!ti); - - if (p_id == 1) { - // Add action event - Point2 ofs = input_editor->get_global_position(); - Rect2 ir = input_editor->get_item_rect(ti); - ir.position.y -= input_editor->get_scroll().y; - ofs += ir.position + ir.size; - ofs.x -= 100; - popup_add->set_position(ofs); - popup_add->popup(); - add_at = "input/" + ti->get_text(0); - edit_idx = -1; - - } else if (p_id == 2) { - // Remove - - if (ti->get_parent() == input_editor->get_root()) { - // Remove action - String name = "input/" + ti->get_text(0); - Dictionary old_val = ProjectSettings::get_singleton()->get(name); - int order = ProjectSettings::get_singleton()->get_order(name); - - undo_redo->create_action(TTR("Erase Input Action")); - undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", name); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", name, old_val); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", name, order); - undo_redo->add_do_method(this, "_update_actions"); - undo_redo->add_undo_method(this, "_update_actions"); - undo_redo->add_do_method(this, "emit_signal", inputmap_changed); - undo_redo->add_undo_method(this, "emit_signal", inputmap_changed); - undo_redo->commit_action(); - - } else { - // Remove action event - String name = "input/" + ti->get_parent()->get_text(0); - Dictionary old_val = ProjectSettings::get_singleton()->get(name); - Dictionary action = old_val.duplicate(); - int idx = ti->get_metadata(0); - - Array events = action["events"]; - ERR_FAIL_INDEX(idx, events.size()); - events.remove(idx); - action["events"] = events; - - undo_redo->create_action(TTR("Erase Input Action Event")); - undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, action); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", name, old_val); - undo_redo->add_do_method(this, "_update_actions"); - undo_redo->add_undo_method(this, "_update_actions"); - undo_redo->add_do_method(this, "emit_signal", inputmap_changed); - undo_redo->add_undo_method(this, "emit_signal", inputmap_changed); - undo_redo->commit_action(); - } - } else if (p_id == 3) { - // Edit - - if (ti->get_parent() == input_editor->get_root()) { - // Edit action name - ti->set_as_cursor(0); - input_editor->edit_selected(); - - } else { - // Edit action event - String name = "input/" + ti->get_parent()->get_text(0); - int idx = ti->get_metadata(0); - Dictionary action = ProjectSettings::get_singleton()->get(name); - - Array events = action["events"]; - ERR_FAIL_INDEX(idx, events.size()); - - Ref<InputEvent> event = events[idx]; - - if (event.is_null()) { - return; - } - - ti->set_as_cursor(0); - add_at = name; - edit_idx = idx; - _edit_item(event); - } - } -} - -void InputMapEditor::_update_actions() { - if (setting) { - return; - } - - Map<String, bool> collapsed; - - if (input_editor->get_root() && input_editor->get_root()->get_children()) { - for (TreeItem *item = input_editor->get_root()->get_children(); item; item = item->get_next()) { - collapsed[item->get_text(0)] = item->is_collapsed(); - } - } - - input_editor->clear(); - TreeItem *root = input_editor->create_item(); - input_editor->set_hide_root(true); - - List<PropertyInfo> props; - ProjectSettings::get_singleton()->get_property_list(&props); - for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - const String property_name = E->get().name; - - if (!property_name.begins_with("input/")) { - continue; - } - - const String name = property_name.get_slice("/", 1); - - TreeItem *item = input_editor->create_item(root); - item->set_text(0, name); - item->set_custom_bg_color(0, input_editor->get_theme_color("prop_subsection", "Editor")); - if (collapsed.has(name)) { - item->set_collapsed(collapsed[name]); - } - - item->set_editable(1, true); - item->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); - item->set_range_config(1, 0.0, 1.0, 0.01); - - item->set_custom_bg_color(1, input_editor->get_theme_color("prop_subsection", "Editor")); - - const bool is_builtin_input = ProjectSettings::get_singleton()->get_input_presets().find(property_name) != nullptr; - const String tooltip_remove = is_builtin_input ? TTR("Built-in actions can't be removed as they're used for UI navigation.") : TTR("Remove"); - item->add_button(2, input_editor->get_theme_icon("Add", "EditorIcons"), 1, false, TTR("Add Event")); - item->add_button(2, input_editor->get_theme_icon("Remove", "EditorIcons"), 2, false, tooltip_remove); - - if (is_builtin_input) { - item->set_button_disabled(2, 1, true); - } else { - item->set_editable(0, true); - } - - Dictionary action = ProjectSettings::get_singleton()->get(property_name); - Array events = action["events"]; - item->set_range(1, action["deadzone"]); - - for (int i = 0; i < events.size(); i++) { - Ref<InputEvent> event = events[i]; - if (event.is_null()) { - continue; - } - - TreeItem *action2 = input_editor->create_item(item); - - Ref<InputEventKey> k = event; - if (k.is_valid()) { - if (k->get_keycode() != 0) { - action2->set_text(0, keycode_get_string(k->get_keycode_with_modifiers())); - action2->set_icon(0, input_editor->get_theme_icon("Keyboard", "EditorIcons")); - } else { - action2->set_text(0, keycode_get_string(k->get_physical_keycode_with_modifiers()) + TTR(" (Physical)")); - action2->set_icon(0, input_editor->get_theme_icon("KeyboardPhysical", "EditorIcons")); - } - } - - Ref<InputEventJoypadButton> jb = event; - if (jb.is_valid()) { - action2->set_text(0, jb->as_text()); - action2->set_icon(0, input_editor->get_theme_icon("JoyButton", "EditorIcons")); - } - - Ref<InputEventMouseButton> mb = event; - if (mb.is_valid()) { - String str = _get_device_string(mb->get_device()) + ", "; - switch (mb->get_button_index()) { - case BUTTON_LEFT: - str += TTR("Left Button"); - break; - case BUTTON_RIGHT: - str += TTR("Right Button"); - break; - case BUTTON_MIDDLE: - str += TTR("Middle Button"); - break; - case BUTTON_WHEEL_UP: - str += TTR("Wheel Up"); - break; - case BUTTON_WHEEL_DOWN: - str += TTR("Wheel Down"); - break; - default: - str += vformat(TTR("%d Button"), mb->get_button_index()); - } - - action2->set_text(0, str); - action2->set_icon(0, input_editor->get_theme_icon("Mouse", "EditorIcons")); - } - - Ref<InputEventJoypadMotion> jm = event; - if (jm.is_valid()) { - device_index->add_item(_get_joypad_motion_event_text(jm)); - action2->set_text(0, jm->as_text()); - action2->set_icon(0, input_editor->get_theme_icon("JoyAxis", "EditorIcons")); - } - action2->set_metadata(0, i); - action2->set_meta("__input", event); - - action2->add_button(2, input_editor->get_theme_icon("Edit", "EditorIcons"), 3, false, TTR("Edit")); - action2->add_button(2, input_editor->get_theme_icon("Remove", "EditorIcons"), 2, false, TTR("Remove")); - // Fade out the individual event buttons slightly to make the - // Add/Remove buttons stand out more. - action2->set_button_color(2, 0, Color(1, 1, 1, 0.75)); - action2->set_button_color(2, 1, Color(1, 1, 1, 0.75)); - } - } - - _action_check(action_name->get_text()); -} - -void InputMapEditor::_action_check(String p_action) { - if (p_action == "") { - action_add->set_disabled(true); - } else { - if (!_validate_action_name(p_action)) { - action_add_error->set_text(TTR("Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\' or '\"'.")); - action_add_error->show(); - action_add->set_disabled(true); - return; - } - if (ProjectSettings::get_singleton()->has_setting("input/" + p_action)) { - action_add_error->set_text(vformat(TTR("An action with the name '%s' already exists."), p_action)); - action_add_error->show(); - action_add->set_disabled(true); - return; - } - - action_add->set_disabled(false); - } - - action_add_error->hide(); -} - -void InputMapEditor::_action_adds(String) { - if (!action_add->is_disabled()) { - _action_add(); - } -} - -void InputMapEditor::_action_add() { - Dictionary action; - action["events"] = Array(); - action["deadzone"] = 0.5f; - String name = "input/" + action_name->get_text(); - undo_redo->create_action(TTR("Add Input Action")); - undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, action); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", name); - undo_redo->add_do_method(this, "_update_actions"); - undo_redo->add_undo_method(this, "_update_actions"); - undo_redo->add_do_method(this, "emit_signal", inputmap_changed); - undo_redo->add_undo_method(this, "emit_signal", inputmap_changed); - undo_redo->commit_action(); - - TreeItem *r = input_editor->get_root(); - - if (!r) { - return; - } - r = r->get_children(); - if (!r) { - return; - } - while (r->get_next()) { - r = r->get_next(); - } - - r->select(0); - input_editor->ensure_cursor_is_visible(); - action_add_error->hide(); - action_name->clear(); -} - -Variant InputMapEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { - TreeItem *selected = input_editor->get_selected(); - if (!selected || selected->get_parent() != input_editor->get_root()) { - return Variant(); - } - - String name = selected->get_text(0); - VBoxContainer *vb = memnew(VBoxContainer); - HBoxContainer *hb = memnew(HBoxContainer); - Label *label = memnew(Label(name)); - hb->set_modulate(Color(1, 1, 1, 1.0f)); - hb->add_child(label); - vb->add_child(hb); - input_editor->set_drag_preview(vb); - - Dictionary drag_data; - drag_data["type"] = "nodes"; - - input_editor->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN); - - return drag_data; -} - -bool InputMapEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { - Dictionary d = p_data; - if (!d.has("type") || d["type"] != "nodes") { - return false; - } - - TreeItem *selected = input_editor->get_selected(); - TreeItem *item = input_editor->get_item_at_position(p_point); - if (!selected || !item || item == selected || item->get_parent() == selected) { - return false; - } - - return true; -} - -void InputMapEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { - if (!can_drop_data_fw(p_point, p_data, p_from)) { - return; - } - - TreeItem *selected = input_editor->get_selected(); - TreeItem *item = input_editor->get_item_at_position(p_point); - if (!item) { - return; - } - TreeItem *target = item->get_parent() == input_editor->get_root() ? item : item->get_parent(); - - String selected_name = "input/" + selected->get_text(0); - int old_order = ProjectSettings::get_singleton()->get_order(selected_name); - String target_name = "input/" + target->get_text(0); - int target_order = ProjectSettings::get_singleton()->get_order(target_name); - - int order = old_order; - bool is_below = target_order > old_order; - TreeItem *iterator = is_below ? selected->get_next() : selected->get_prev(); - - undo_redo->create_action(TTR("Moved Input Action Event")); - while (iterator != target) { - String iterator_name = "input/" + iterator->get_text(0); - int iterator_order = ProjectSettings::get_singleton()->get_order(iterator_name); - undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", iterator_name, order); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", iterator_name, iterator_order); - order = iterator_order; - iterator = is_below ? iterator->get_next() : iterator->get_prev(); - } - - undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", target_name, order); - undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", selected_name, target_order); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", target_name, target_order); - undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", selected_name, old_order); - - undo_redo->add_do_method(this, "_update_actions"); - undo_redo->add_undo_method(this, "_update_actions"); - undo_redo->add_do_method(this, "emit_signal", inputmap_changed); - undo_redo->add_undo_method(this, "emit_signal", inputmap_changed); - undo_redo->commit_action(); -} - -void InputMapEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_update_actions"), &InputMapEditor::_update_actions); - - ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &InputMapEditor::get_drag_data_fw); - ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &InputMapEditor::can_drop_data_fw); - ClassDB::bind_method(D_METHOD("drop_data_fw"), &InputMapEditor::drop_data_fw); - - ADD_SIGNAL(MethodInfo("inputmap_changed")); -} - -InputMapEditor::InputMapEditor() { - undo_redo = EditorNode::get_undo_redo(); - press_a_key_physical = false; - inputmap_changed = "inputmap_changed"; - - VBoxContainer *vbc = memnew(VBoxContainer); - vbc->set_anchor_and_offset(SIDE_TOP, Control::ANCHOR_BEGIN, 0); - vbc->set_anchor_and_offset(SIDE_BOTTOM, Control::ANCHOR_END, 0); - vbc->set_anchor_and_offset(SIDE_LEFT, Control::ANCHOR_BEGIN, 0); - vbc->set_anchor_and_offset(SIDE_RIGHT, Control::ANCHOR_END, 0); - add_child(vbc); - - HBoxContainer *hbc = memnew(HBoxContainer); - vbc->add_child(hbc); - - Label *l = memnew(Label); - l->set_text(TTR("Action:")); - hbc->add_child(l); - - action_name = memnew(LineEdit); - action_name->set_h_size_flags(Control::SIZE_EXPAND_FILL); - action_name->connect("text_entered", callable_mp(this, &InputMapEditor::_action_adds)); - action_name->connect("text_changed", callable_mp(this, &InputMapEditor::_action_check)); - hbc->add_child(action_name); - - action_add_error = memnew(Label); - action_add_error->hide(); - hbc->add_child(action_add_error); - - Button *add = memnew(Button); - add->set_text(TTR("Add")); - add->set_disabled(true); - add->connect("pressed", callable_mp(this, &InputMapEditor::_action_add)); - hbc->add_child(add); - action_add = add; - - input_editor = memnew(Tree); - input_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); - input_editor->set_columns(3); - input_editor->set_column_titles_visible(true); - input_editor->set_column_title(0, TTR("Action")); - input_editor->set_column_title(1, TTR("Deadzone")); - input_editor->set_column_expand(1, false); - input_editor->set_column_min_width(1, 80 * EDSCALE); - input_editor->set_column_expand(2, false); - input_editor->set_column_min_width(2, 50 * EDSCALE); - input_editor->connect("item_edited", callable_mp(this, &InputMapEditor::_action_edited)); - input_editor->connect("item_activated", callable_mp(this, &InputMapEditor::_action_activated)); - input_editor->connect("cell_selected", callable_mp(this, &InputMapEditor::_action_selected)); - input_editor->connect("button_pressed", callable_mp(this, &InputMapEditor::_action_button_pressed)); -#ifndef _MSC_VER -#warning need to make drag data forwarding to non controls happen -#endif - //input_editor->set_drag_forwarding(this); - vbc->add_child(input_editor); - - // Popups - - popup_add = memnew(PopupMenu); - popup_add->connect("id_pressed", callable_mp(this, &InputMapEditor::_add_item), make_binds(Ref<InputEvent>())); - add_child(popup_add); - - press_a_key = memnew(ConfirmationDialog); - press_a_key->get_ok_button()->set_disabled(true); - //press_a_key->set_focus_mode(Control::FOCUS_ALL); - press_a_key->connect("window_input", callable_mp(this, &InputMapEditor::_wait_for_key)); - press_a_key->connect("confirmed", callable_mp(this, &InputMapEditor::_press_a_key_confirm)); - add_child(press_a_key); - - l = memnew(Label); - l->set_text(TTR("Press a Key...")); - l->set_anchors_and_offsets_preset(Control::PRESET_WIDE); - l->set_align(Label::ALIGN_CENTER); - l->set_offset(SIDE_TOP, 20); - l->set_anchor_and_offset(SIDE_BOTTOM, Control::ANCHOR_BEGIN, 30); - press_a_key->add_child(l); - press_a_key_label = l; - - device_input = memnew(ConfirmationDialog); - device_input->get_ok_button()->set_text(TTR("Add")); - device_input->connect("confirmed", callable_mp(this, &InputMapEditor::_device_input_add)); - add_child(device_input); - - hbc = memnew(HBoxContainer); - device_input->add_child(hbc); - - VBoxContainer *vbc_left = memnew(VBoxContainer); - hbc->add_child(vbc_left); - - l = memnew(Label); - l->set_text(TTR("Device:")); - vbc_left->add_child(l); - - device_id = memnew(OptionButton); - for (int i = -1; i < 8; i++) { - device_id->add_item(_get_device_string(i)); - } - _set_current_device(0); - vbc_left->add_child(device_id); - - VBoxContainer *vbc_right = memnew(VBoxContainer); - vbc_right->set_h_size_flags(Control::SIZE_EXPAND_FILL); - hbc->add_child(vbc_right); - - l = memnew(Label); - l->set_text(TTR("Index:")); - vbc_right->add_child(l); - - device_index_label = l; - device_index = memnew(OptionButton); - device_index->set_clip_text(true); - vbc_right->add_child(device_index); - - message = memnew(AcceptDialog); - add_child(message); -} diff --git a/editor/input_map_editor.h b/editor/input_map_editor.h deleted file mode 100644 index cc6ac1660d..0000000000 --- a/editor/input_map_editor.h +++ /dev/null @@ -1,109 +0,0 @@ -/*************************************************************************/ -/* input_map_editor.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef INPUT_MAP_EDITOR_H -#define INPUT_MAP_EDITOR_H - -#include "core/object/undo_redo.h" -#include "editor/editor_data.h" - -class InputMapEditor : public Control { - GDCLASS(InputMapEditor, Control); - - enum InputType { - INPUT_KEY, - INPUT_KEY_PHYSICAL, - INPUT_JOY_BUTTON, - INPUT_JOY_MOTION, - INPUT_MOUSE_BUTTON - }; - - Tree *input_editor; - LineEdit *action_name; - Button *action_add; - Label *action_add_error; - - InputType add_type; - String add_at; - int edit_idx; - - PopupMenu *popup_add; - ConfirmationDialog *press_a_key; - bool press_a_key_physical; - Label *press_a_key_label; - ConfirmationDialog *device_input; - OptionButton *device_id; - OptionButton *device_index; - Label *device_index_label; - MenuButton *popup_copy_to_feature; - - Ref<InputEventKey> last_wait_for_key; - - AcceptDialog *message; - UndoRedo *undo_redo; - String inputmap_changed; - bool setting = false; - - void _update_actions(); - void _add_item(int p_item, Ref<InputEvent> p_exiting_event = Ref<InputEvent>()); - void _edit_item(Ref<InputEvent> p_exiting_event); - - void _action_check(String p_action); - void _action_adds(String); - void _action_add(); - void _device_input_add(); - - void _action_selected(); - void _action_edited(); - void _action_activated(); - void _action_button_pressed(Object *p_obj, int p_column, int p_id); - void _wait_for_key(const Ref<InputEvent> &p_event); - void _press_a_key_confirm(); - void _show_last_added(const Ref<InputEvent> &p_event, const String &p_name); - - String _get_joypad_motion_event_text(const Ref<InputEventJoypadMotion> &p_event); - - 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); - -protected: - int _get_current_device(); - void _set_current_device(int i_device); - String _get_device_string(int i_device); - - void _notification(int p_what); - static void _bind_methods(); - -public: - InputMapEditor(); -}; - -#endif // INPUT_MAP_EDITOR_H diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp index 60553143d5..0e68af06f0 100644 --- a/editor/localization_editor.cpp +++ b/editor/localization_editor.cpp @@ -84,7 +84,7 @@ void LocalizationEditor::add_translation(const String &p_translation) { } void LocalizationEditor::_translation_add(const PackedStringArray &p_paths) { - PackedStringArray translations = ProjectSettings::get_singleton()->get("locale/translations"); + PackedStringArray translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations"); for (int i = 0; i < p_paths.size(); i++) { if (!translations.has(p_paths[i])) { // Don't add duplicate translation paths. @@ -93,8 +93,8 @@ void LocalizationEditor::_translation_add(const PackedStringArray &p_paths) { } undo_redo->create_action(vformat(TTR("Add %d Translations"), p_paths.size())); - undo_redo->add_do_property(ProjectSettings::get_singleton(), "locale/translations", translations); - undo_redo->add_undo_property(ProjectSettings::get_singleton(), "locale/translations", ProjectSettings::get_singleton()->get("locale/translations")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translations", translations); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translations", ProjectSettings::get_singleton()->get("internationalization/locale/translations")); undo_redo->add_do_method(this, "update_translations"); undo_redo->add_undo_method(this, "update_translations"); undo_redo->add_do_method(this, "emit_signal", localization_changed); @@ -112,15 +112,15 @@ void LocalizationEditor::_translation_delete(Object *p_item, int p_column, int p int idx = ti->get_metadata(0); - PackedStringArray translations = ProjectSettings::get_singleton()->get("locale/translations"); + PackedStringArray translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations"); ERR_FAIL_INDEX(idx, translations.size()); translations.remove(idx); undo_redo->create_action(TTR("Remove Translation")); - undo_redo->add_do_property(ProjectSettings::get_singleton(), "locale/translations", translations); - undo_redo->add_undo_property(ProjectSettings::get_singleton(), "locale/translations", ProjectSettings::get_singleton()->get("locale/translations")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translations", translations); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translations", ProjectSettings::get_singleton()->get("internationalization/locale/translations")); undo_redo->add_do_method(this, "update_translations"); undo_redo->add_undo_method(this, "update_translations"); undo_redo->add_do_method(this, "emit_signal", localization_changed); @@ -136,8 +136,8 @@ void LocalizationEditor::_translation_res_add(const PackedStringArray &p_paths) Variant prev; Dictionary remaps; - if (ProjectSettings::get_singleton()->has_setting("locale/translation_remaps")) { - remaps = ProjectSettings::get_singleton()->get("locale/translation_remaps"); + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/translation_remaps")) { + remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps"); prev = remaps; } @@ -149,8 +149,8 @@ void LocalizationEditor::_translation_res_add(const PackedStringArray &p_paths) } undo_redo->create_action(vformat(TTR("Translation Resource Remap: Add %d Path(s)"), p_paths.size())); - undo_redo->add_do_property(ProjectSettings::get_singleton(), "locale/translation_remaps", remaps); - undo_redo->add_undo_property(ProjectSettings::get_singleton(), "locale/translation_remaps", prev); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", prev); undo_redo->add_do_method(this, "update_translations"); undo_redo->add_undo_method(this, "update_translations"); undo_redo->add_do_method(this, "emit_signal", localization_changed); @@ -163,9 +163,9 @@ void LocalizationEditor::_translation_res_option_file_open() { } void LocalizationEditor::_translation_res_option_add(const PackedStringArray &p_paths) { - ERR_FAIL_COND(!ProjectSettings::get_singleton()->has_setting("locale/translation_remaps")); + ERR_FAIL_COND(!ProjectSettings::get_singleton()->has_setting("internationalization/locale/translation_remaps")); - Dictionary remaps = ProjectSettings::get_singleton()->get("locale/translation_remaps"); + Dictionary remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps"); TreeItem *k = translation_remap->get_selected(); ERR_FAIL_COND(!k); @@ -180,8 +180,8 @@ void LocalizationEditor::_translation_res_option_add(const PackedStringArray &p_ remaps[key] = r; undo_redo->create_action(vformat(TTR("Translation Resource Remap: Add %d Remap(s)"), p_paths.size())); - undo_redo->add_do_property(ProjectSettings::get_singleton(), "locale/translation_remaps", remaps); - undo_redo->add_undo_property(ProjectSettings::get_singleton(), "locale/translation_remaps", ProjectSettings::get_singleton()->get("locale/translation_remaps")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps")); undo_redo->add_do_method(this, "update_translations"); undo_redo->add_undo_method(this, "update_translations"); undo_redo->add_do_method(this, "emit_signal", localization_changed); @@ -202,11 +202,11 @@ void LocalizationEditor::_translation_res_option_changed() { return; } - if (!ProjectSettings::get_singleton()->has_setting("locale/translation_remaps")) { + if (!ProjectSettings::get_singleton()->has_setting("internationalization/locale/translation_remaps")) { return; } - Dictionary remaps = ProjectSettings::get_singleton()->get("locale/translation_remaps"); + Dictionary remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps"); TreeItem *k = translation_remap->get_selected(); ERR_FAIL_COND(!k); @@ -234,8 +234,8 @@ void LocalizationEditor::_translation_res_option_changed() { updating_translations = true; undo_redo->create_action(TTR("Change Resource Remap Language")); - undo_redo->add_do_property(ProjectSettings::get_singleton(), "locale/translation_remaps", remaps); - undo_redo->add_undo_property(ProjectSettings::get_singleton(), "locale/translation_remaps", ProjectSettings::get_singleton()->get("locale/translation_remaps")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps")); undo_redo->add_do_method(this, "update_translations"); undo_redo->add_undo_method(this, "update_translations"); undo_redo->add_do_method(this, "emit_signal", localization_changed); @@ -249,11 +249,11 @@ void LocalizationEditor::_translation_res_delete(Object *p_item, int p_column, i return; } - if (!ProjectSettings::get_singleton()->has_setting("locale/translation_remaps")) { + if (!ProjectSettings::get_singleton()->has_setting("internationalization/locale/translation_remaps")) { return; } - Dictionary remaps = ProjectSettings::get_singleton()->get("locale/translation_remaps"); + Dictionary remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps"); TreeItem *k = Object::cast_to<TreeItem>(p_item); @@ -263,8 +263,8 @@ void LocalizationEditor::_translation_res_delete(Object *p_item, int p_column, i remaps.erase(key); undo_redo->create_action(TTR("Remove Resource Remap")); - undo_redo->add_do_property(ProjectSettings::get_singleton(), "locale/translation_remaps", remaps); - undo_redo->add_undo_property(ProjectSettings::get_singleton(), "locale/translation_remaps", ProjectSettings::get_singleton()->get("locale/translation_remaps")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps")); undo_redo->add_do_method(this, "update_translations"); undo_redo->add_undo_method(this, "update_translations"); undo_redo->add_do_method(this, "emit_signal", localization_changed); @@ -277,11 +277,11 @@ void LocalizationEditor::_translation_res_option_delete(Object *p_item, int p_co return; } - if (!ProjectSettings::get_singleton()->has_setting("locale/translation_remaps")) { + if (!ProjectSettings::get_singleton()->has_setting("internationalization/locale/translation_remaps")) { return; } - Dictionary remaps = ProjectSettings::get_singleton()->get("locale/translation_remaps"); + Dictionary remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps"); TreeItem *k = translation_remap->get_selected(); ERR_FAIL_COND(!k); @@ -298,8 +298,8 @@ void LocalizationEditor::_translation_res_option_delete(Object *p_item, int p_co remaps[key] = r; undo_redo->create_action(TTR("Remove Resource Remap Option")); - undo_redo->add_do_property(ProjectSettings::get_singleton(), "locale/translation_remaps", remaps); - undo_redo->add_undo_property(ProjectSettings::get_singleton(), "locale/translation_remaps", ProjectSettings::get_singleton()->get("locale/translation_remaps")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps")); undo_redo->add_do_method(this, "update_translations"); undo_redo->add_undo_method(this, "update_translations"); undo_redo->add_do_method(this, "emit_signal", localization_changed); @@ -316,8 +316,8 @@ void LocalizationEditor::_translation_filter_option_changed() { Variant prev; Array f_locales_all; - if (ProjectSettings::get_singleton()->has_setting("locale/locale_filter")) { - f_locales_all = ProjectSettings::get_singleton()->get("locale/locale_filter"); + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter")) { + f_locales_all = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter"); prev = f_locales_all; if (f_locales_all.size() != 2) { @@ -346,8 +346,8 @@ void LocalizationEditor::_translation_filter_option_changed() { f_locales.sort(); undo_redo->create_action(TTR("Changed Locale Filter")); - undo_redo->add_do_property(ProjectSettings::get_singleton(), "locale/locale_filter", f_locales_all); - undo_redo->add_undo_property(ProjectSettings::get_singleton(), "locale/locale_filter", prev); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter", f_locales_all); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter", prev); undo_redo->add_do_method(this, "update_translations"); undo_redo->add_undo_method(this, "update_translations"); undo_redo->add_do_method(this, "emit_signal", localization_changed); @@ -361,8 +361,8 @@ void LocalizationEditor::_translation_filter_mode_changed(int p_mode) { Variant prev; Array f_locales_all; - if (ProjectSettings::get_singleton()->has_setting("locale/locale_filter")) { - f_locales_all = ProjectSettings::get_singleton()->get("locale/locale_filter"); + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter")) { + f_locales_all = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter"); prev = f_locales_all; if (f_locales_all.size() != 2) { @@ -378,8 +378,8 @@ void LocalizationEditor::_translation_filter_mode_changed(int p_mode) { } undo_redo->create_action(TTR("Changed Locale Filter Mode")); - undo_redo->add_do_property(ProjectSettings::get_singleton(), "locale/locale_filter", f_locales_all); - undo_redo->add_undo_property(ProjectSettings::get_singleton(), "locale/locale_filter", prev); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter", f_locales_all); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter", prev); undo_redo->add_do_method(this, "update_translations"); undo_redo->add_undo_method(this, "update_translations"); undo_redo->add_do_method(this, "emit_signal", localization_changed); @@ -388,7 +388,7 @@ void LocalizationEditor::_translation_filter_mode_changed(int p_mode) { } void LocalizationEditor::_pot_add(const PackedStringArray &p_paths) { - PackedStringArray pot_translations = ProjectSettings::get_singleton()->get("locale/translations_pot_files"); + PackedStringArray pot_translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations_pot_files"); for (int i = 0; i < p_paths.size(); i++) { for (int j = 0; j < pot_translations.size(); j++) { @@ -401,8 +401,8 @@ void LocalizationEditor::_pot_add(const PackedStringArray &p_paths) { } undo_redo->create_action(vformat(TTR("Add %d file(s) for POT generation"), p_paths.size())); - undo_redo->add_do_property(ProjectSettings::get_singleton(), "locale/translations_pot_files", pot_translations); - undo_redo->add_undo_property(ProjectSettings::get_singleton(), "locale/translations_pot_files", ProjectSettings::get_singleton()->get("locale/translations_pot_files")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translations_pot_files", pot_translations); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translations_pot_files", ProjectSettings::get_singleton()->get("internationalization/locale/translations_pot_files")); undo_redo->add_do_method(this, "update_translations"); undo_redo->add_undo_method(this, "update_translations"); undo_redo->add_do_method(this, "emit_signal", localization_changed); @@ -416,15 +416,15 @@ void LocalizationEditor::_pot_delete(Object *p_item, int p_column, int p_button) int idx = ti->get_metadata(0); - PackedStringArray pot_translations = ProjectSettings::get_singleton()->get("locale/translations_pot_files"); + PackedStringArray pot_translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations_pot_files"); ERR_FAIL_INDEX(idx, pot_translations.size()); pot_translations.remove(idx); undo_redo->create_action(TTR("Remove file from POT generation")); - undo_redo->add_do_property(ProjectSettings::get_singleton(), "locale/translations_pot_files", pot_translations); - undo_redo->add_undo_property(ProjectSettings::get_singleton(), "locale/translations_pot_files", ProjectSettings::get_singleton()->get("locale/translations_pot_files")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translations_pot_files", pot_translations); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translations_pot_files", ProjectSettings::get_singleton()->get("internationalization/locale/translations_pot_files")); undo_redo->add_do_method(this, "update_translations"); undo_redo->add_undo_method(this, "update_translations"); undo_redo->add_do_method(this, "emit_signal", localization_changed); @@ -463,8 +463,8 @@ void LocalizationEditor::update_translations() { translation_list->clear(); TreeItem *root = translation_list->create_item(nullptr); translation_list->set_hide_root(true); - if (ProjectSettings::get_singleton()->has_setting("locale/translations")) { - PackedStringArray translations = ProjectSettings::get_singleton()->get("locale/translations"); + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/translations")) { + PackedStringArray translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations"); for (int i = 0; i < translations.size(); i++) { TreeItem *t = translation_list->create_item(root); t->set_editable(0, false); @@ -482,8 +482,8 @@ void LocalizationEditor::update_translations() { Array l_filter_all; bool is_arr_empty = true; - if (ProjectSettings::get_singleton()->has_setting("locale/locale_filter")) { - l_filter_all = ProjectSettings::get_singleton()->get("locale/locale_filter"); + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter")) { + l_filter_all = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter"); if (l_filter_all.size() == 2) { translation_locale_filter_mode->select(l_filter_all[0]); @@ -573,8 +573,8 @@ void LocalizationEditor::update_translations() { } } - if (ProjectSettings::get_singleton()->has_setting("locale/translation_remaps")) { - Dictionary remaps = ProjectSettings::get_singleton()->get("locale/translation_remaps"); + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/translation_remaps")) { + Dictionary remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps"); List<Variant> rk; remaps.get_key_list(&rk); Vector<String> keys; @@ -631,8 +631,8 @@ void LocalizationEditor::update_translations() { translation_pot_list->clear(); root = translation_pot_list->create_item(nullptr); translation_pot_list->set_hide_root(true); - if (ProjectSettings::get_singleton()->has_setting("locale/translations_pot_files")) { - PackedStringArray pot_translations = ProjectSettings::get_singleton()->get("locale/translations_pot_files"); + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/translations_pot_files")) { + PackedStringArray pot_translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations_pot_files"); for (int i = 0; i < pot_translations.size(); i++) { TreeItem *t = translation_pot_list->create_item(root); t->set_editable(0, false); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index cda88c00f3..7c623505b5 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -1397,7 +1397,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { // Render every past/future step with the capture shader. RS::get_singleton()->canvas_item_set_material(onion.capture.canvas_item, onion.capture.material->get_rid()); - onion.capture.material->set_shader_param("bkg_color", GLOBAL_GET("rendering/environment/default_clear_color")); + onion.capture.material->set_shader_param("bkg_color", GLOBAL_GET("rendering/environment/defaults/default_clear_color")); onion.capture.material->set_shader_param("differences_only", onion.differences_only); onion.capture.material->set_shader_param("present", onion.differences_only ? RS::get_singleton()->viewport_get_texture(present_rid) : RID()); diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 63bb785c5e..eb3c06fba1 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -301,7 +301,7 @@ EditorPackedScenePreviewPlugin::EditorPackedScenePreviewPlugin() { ////////////////////////////////////////////////////////////////// void EditorMaterialPreviewPlugin::_preview_done(const Variant &p_udata) { - preview_done = true; + preview_done.set(); } void EditorMaterialPreviewPlugin::_bind_methods() { @@ -325,10 +325,10 @@ Ref<Texture2D> EditorMaterialPreviewPlugin::generate(const RES &p_from, const Si RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture - preview_done = false; + preview_done.clear(); RS::get_singleton()->request_frame_drawn_callback(const_cast<EditorMaterialPreviewPlugin *>(this), "_preview_done", Variant()); - while (!preview_done) { + while (!preview_done.is_set()) { OS::get_singleton()->delay_usec(10); } @@ -677,7 +677,7 @@ EditorAudioStreamPreviewPlugin::EditorAudioStreamPreviewPlugin() { /////////////////////////////////////////////////////////////////////////// void EditorMeshPreviewPlugin::_preview_done(const Variant &p_udata) { - preview_done = true; + preview_done.set(); } void EditorMeshPreviewPlugin::_bind_methods() { @@ -714,10 +714,10 @@ Ref<Texture2D> EditorMeshPreviewPlugin::generate(const RES &p_from, const Size2 RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture - preview_done = false; + preview_done.clear(); RS::get_singleton()->request_frame_drawn_callback(const_cast<EditorMeshPreviewPlugin *>(this), "_preview_done", Variant()); - while (!preview_done) { + while (!preview_done.is_set()) { OS::get_singleton()->delay_usec(10); } @@ -792,7 +792,7 @@ EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() { /////////////////////////////////////////////////////////////////////////// void EditorFontPreviewPlugin::_preview_done(const Variant &p_udata) { - preview_done = true; + preview_done.set(); } void EditorFontPreviewPlugin::_bind_methods() { @@ -883,11 +883,11 @@ Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path, font->draw_string(canvas_item, pos, sample, HALIGN_LEFT, -1.f, 50, Color(1, 1, 1)); - preview_done = false; + preview_done.clear(); RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture RS::get_singleton()->request_frame_drawn_callback(const_cast<EditorFontPreviewPlugin *>(this), "_preview_done", Variant()); - while (!preview_done) { + while (!preview_done.is_set()) { OS::get_singleton()->delay_usec(10); } diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index 57e2911c89..6e8b9a34cf 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -33,6 +33,8 @@ #include "editor/editor_resource_preview.h" +#include "core/templates/safe_refcount.h" + void post_process_preview(Ref<Image> p_image); class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator { @@ -90,7 +92,7 @@ class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator { RID light2; RID light_instance2; RID camera; - mutable volatile bool preview_done = false; + mutable SafeFlag preview_done; void _preview_done(const Variant &p_udata); @@ -134,7 +136,7 @@ class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator { RID light2; RID light_instance2; RID camera; - mutable volatile bool preview_done = false; + mutable SafeFlag preview_done; void _preview_done(const Variant &p_udata); @@ -156,7 +158,7 @@ class EditorFontPreviewPlugin : public EditorResourcePreviewGenerator { RID viewport_texture; RID canvas; RID canvas_item; - mutable volatile bool preview_done = false; + mutable SafeFlag preview_done; void _preview_done(const Variant &p_udata); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index c591e7f42d..610ef0c601 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -2337,12 +2337,12 @@ void Node3DEditorPlugin::edited_scene_changed() { void Node3DEditorViewport::_project_settings_changed() { //update shadow atlas if changed - int shadowmap_size = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/size"); - bool shadowmap_16_bits = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/16_bits"); - int atlas_q0 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_0_subdiv"); - int atlas_q1 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_1_subdiv"); - int atlas_q2 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_2_subdiv"); - int atlas_q3 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_3_subdiv"); + int shadowmap_size = ProjectSettings::get_singleton()->get("rendering/shadows/shadow_atlas/size"); + bool shadowmap_16_bits = ProjectSettings::get_singleton()->get("rendering/shadows/shadow_atlas/16_bits"); + int atlas_q0 = ProjectSettings::get_singleton()->get("rendering/shadows/shadow_atlas/quadrant_0_subdiv"); + int atlas_q1 = ProjectSettings::get_singleton()->get("rendering/shadows/shadow_atlas/quadrant_1_subdiv"); + int atlas_q2 = ProjectSettings::get_singleton()->get("rendering/shadows/shadow_atlas/quadrant_2_subdiv"); + int atlas_q3 = ProjectSettings::get_singleton()->get("rendering/shadows/shadow_atlas/quadrant_3_subdiv"); viewport->set_shadow_atlas_size(shadowmap_size); viewport->set_shadow_atlas_16_bits(shadowmap_16_bits); @@ -2359,11 +2359,11 @@ void Node3DEditorViewport::_project_settings_changed() { // Update MSAA, screen-space AA and debanding if changed - const int msaa_mode = ProjectSettings::get_singleton()->get("rendering/quality/screen_filters/msaa"); + const int msaa_mode = ProjectSettings::get_singleton()->get("rendering/anti_aliasing/quality/msaa"); viewport->set_msaa(Viewport::MSAA(msaa_mode)); - const int ssaa_mode = GLOBAL_GET("rendering/quality/screen_filters/screen_space_aa"); + const int ssaa_mode = GLOBAL_GET("rendering/anti_aliasing/quality/screen_space_aa"); viewport->set_screen_space_aa(Viewport::ScreenSpaceAA(ssaa_mode)); - const bool use_debanding = GLOBAL_GET("rendering/quality/screen_filters/use_debanding"); + const bool use_debanding = GLOBAL_GET("rendering/anti_aliasing/quality/use_debanding"); viewport->set_use_debanding(use_debanding); } diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 71b18497b0..b6df66b8af 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1066,7 +1066,7 @@ void ScriptTextEditor::_edit_option(int p_op) { return; } - tx->indent_left(); + tx->indent_selected_lines_left(); } break; case EDIT_INDENT_RIGHT: { Ref<Script> scr = script; @@ -1074,7 +1074,7 @@ void ScriptTextEditor::_edit_option(int p_op) { return; } - tx->indent_right(); + tx->indent_selected_lines_right(); } break; case EDIT_DELETE_LINE: { code_editor->delete_lines(); @@ -1632,16 +1632,16 @@ void ScriptTextEditor::_color_changed(const Color &p_color) { void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition, Vector2 p_pos) { context_menu->clear(); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); context_menu->add_separator(); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); context_menu->add_separator(); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); context_menu->add_separator(); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); @@ -1743,14 +1743,14 @@ void ScriptTextEditor::_enable_code_editor() { search_menu->get_popup()->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option)); edit_hb->add_child(edit_menu); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN); @@ -1763,7 +1763,7 @@ void ScriptTextEditor::_enable_code_editor() { edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_all_lines"), EDIT_UNFOLD_ALL_LINES); edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/complete_symbol"), EDIT_COMPLETE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/evaluate_selection"), EDIT_EVALUATE); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/trim_trailing_whitespace"), EDIT_TRIM_TRAILING_WHITESAPCE); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_spaces"), EDIT_CONVERT_INDENT_TO_SPACES); @@ -1915,12 +1915,6 @@ static ScriptEditorBase *create_editor(const RES &p_resource) { } void ScriptTextEditor::register_editor() { - ED_SHORTCUT("script_text_editor/undo", TTR("Undo"), KEY_MASK_CMD | KEY_Z); - ED_SHORTCUT("script_text_editor/redo", TTR("Redo"), KEY_MASK_CMD | KEY_Y); - ED_SHORTCUT("script_text_editor/cut", TTR("Cut"), KEY_MASK_CMD | KEY_X); - ED_SHORTCUT("script_text_editor/copy", TTR("Copy"), KEY_MASK_CMD | KEY_C); - ED_SHORTCUT("script_text_editor/paste", TTR("Paste"), KEY_MASK_CMD | KEY_V); - ED_SHORTCUT("script_text_editor/select_all", TTR("Select All"), KEY_MASK_CMD | KEY_A); ED_SHORTCUT("script_text_editor/move_up", TTR("Move Up"), KEY_MASK_ALT | KEY_UP); ED_SHORTCUT("script_text_editor/move_down", TTR("Move Down"), KEY_MASK_ALT | KEY_DOWN); ED_SHORTCUT("script_text_editor/delete_line", TTR("Delete Line"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_K); @@ -1936,10 +1930,8 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), 0); #ifdef OSX_ENABLED ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_C); - ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CTRL | KEY_SPACE); #else ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_D); - ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CMD | KEY_SPACE); #endif ED_SHORTCUT("script_text_editor/evaluate_selection", TTR("Evaluate Selection"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_E); ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_T); diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 8be82628cb..c8a46715ad 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -282,7 +282,7 @@ void ShaderEditor::_menu_option(int p_option) { } CodeEdit *tx = shader_editor->get_text_editor(); - tx->indent_left(); + tx->indent_selected_lines_left(); } break; case EDIT_INDENT_RIGHT: { @@ -291,7 +291,7 @@ void ShaderEditor::_menu_option(int p_option) { } CodeEdit *tx = shader_editor->get_text_editor(); - tx->indent_right(); + tx->indent_selected_lines_right(); } break; case EDIT_DELETE_LINE: { @@ -533,15 +533,15 @@ void ShaderEditor::_bookmark_item_pressed(int p_idx) { void ShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position) { context_menu->clear(); if (p_selection) { - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); } - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); context_menu->add_separator(); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); context_menu->add_separator(); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); @@ -585,14 +585,14 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { edit_menu->set_text(TTR("Edit")); edit_menu->set_switch_on_hover(true); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN); @@ -602,7 +602,7 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/complete_symbol"), EDIT_COMPLETE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE); edit_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditor::_menu_option)); search_menu = memnew(MenuButton); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index d45011c8aa..b88f1c91e6 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -363,10 +363,10 @@ void TextEditor::_edit_option(int p_op) { code_editor->move_lines_down(); } break; case EDIT_INDENT_LEFT: { - tx->indent_left(); + tx->indent_selected_lines_left(); } break; case EDIT_INDENT_RIGHT: { - tx->indent_right(); + tx->indent_selected_lines_right(); } break; case EDIT_DELETE_LINE: { code_editor->delete_lines(); @@ -514,15 +514,15 @@ void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { void TextEditor::_make_context_menu(bool p_selection, bool p_can_fold, bool p_is_folded, Vector2 p_position) { context_menu->clear(); if (p_selection) { - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); } - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); context_menu->add_separator(); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); - context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO); context_menu->add_separator(); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT); @@ -584,14 +584,14 @@ TextEditor::TextEditor() { edit_menu->set_switch_on_hover(true); edit_menu->get_popup()->connect("id_pressed", callable_mp(this, &TextEditor::_edit_option)); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("un_redo"), EDIT_REDO); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 5061067ded..a63e641c2b 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -1023,7 +1023,7 @@ void VisualShaderEditor::_update_options_menu() { Color unsupported_color = get_theme_color("error_color", "Editor"); Color supported_color = get_theme_color("warning_color", "Editor"); - static bool low_driver = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name") == "GLES2"; + static bool low_driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "GLES2"; Map<String, TreeItem *> folders; diff --git a/editor/pot_generator.cpp b/editor/pot_generator.cpp index 2d65c00a89..497cc0cbdc 100644 --- a/editor/pot_generator.cpp +++ b/editor/pot_generator.cpp @@ -55,7 +55,7 @@ void POTGenerator::_print_all_translation_strings() { #endif void POTGenerator::generate_pot(const String &p_file) { - if (!ProjectSettings::get_singleton()->has_setting("locale/translations_pot_files")) { + if (!ProjectSettings::get_singleton()->has_setting("internationalization/locale/translations_pot_files")) { WARN_PRINT("No files selected for POT generation."); return; } @@ -63,7 +63,7 @@ void POTGenerator::generate_pot(const String &p_file) { // Clear all_translation_strings of the previous round. all_translation_strings.clear(); - Vector<String> files = ProjectSettings::get_singleton()->get("locale/translations_pot_files"); + Vector<String> files = ProjectSettings::get_singleton()->get("internationalization/locale/translations_pot_files"); // Collect all translatable strings according to files order in "POT Generation" setting. for (int i = 0; i < files.size(); i++) { @@ -100,7 +100,7 @@ void POTGenerator::_write_to_pot(const String &p_file) { } String project_name = ProjectSettings::get_singleton()->get("application/config/name"); - Vector<String> files = ProjectSettings::get_singleton()->get("locale/translations_pot_files"); + Vector<String> files = ProjectSettings::get_singleton()->get("internationalization/locale/translations_pot_files"); String extracted_files = ""; for (int i = 0; i < files.size(); i++) { extracted_files += "# " + files[i] + "\n"; diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 7b00c688fa..7d421bdf81 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -474,15 +474,15 @@ private: } ProjectSettings::CustomMap initial_settings; if (rasterizer_button_group->get_pressed_button()->get_meta("driver_name") == "Vulkan") { - initial_settings["rendering/quality/driver/driver_name"] = "Vulkan"; + initial_settings["rendering/driver/driver_name"] = "Vulkan"; } else { - initial_settings["rendering/quality/driver/driver_name"] = "GLES2"; - initial_settings["rendering/vram_compression/import_etc2"] = false; - initial_settings["rendering/vram_compression/import_etc"] = true; + initial_settings["rendering/driver/driver_name"] = "GLES2"; + initial_settings["rendering/textures/vram_compression/import_etc2"] = false; + initial_settings["rendering/textures/vram_compression/import_etc"] = true; } initial_settings["application/config/name"] = project_name->get_text(); initial_settings["application/config/icon"] = "res://icon.png"; - initial_settings["rendering/environment/default_environment"] = "res://default_env.tres"; + initial_settings["rendering/environment/defaults/default_environment"] = "res://default_env.tres"; if (ProjectSettings::get_singleton()->save_custom(dir.plus_file("project.godot"), initial_settings, Vector<String>(), false) != OK) { set_message(TTR("Couldn't create project.godot in project path."), MESSAGE_ERROR); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 76fdf47eb6..6e2cd72796 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -75,8 +75,12 @@ void ProjectSettingsEditor::_advanced_pressed() { if (advanced->is_pressed()) { _update_advanced_bar(); advanced_bar->show(); + EditorSettings::get_singleton()->set_project_metadata("project_settings", "advanced_mode", true); + inspector->set_restrict_to_basic_settings(false); } else { advanced_bar->hide(); + EditorSettings::get_singleton()->set_project_metadata("project_settings", "advanced_mode", false); + inspector->set_restrict_to_basic_settings(true); } } @@ -191,9 +195,6 @@ void ProjectSettingsEditor::_update_advanced_bar() { add_button->set_disabled(disable_add); del_button->set_disabled(disable_del); - - error_label->set_text(error_msg); - error_label->set_visible(error_msg != ""); } String ProjectSettingsEditor::_get_setting_name() const { @@ -268,21 +269,216 @@ void ProjectSettingsEditor::_editor_restart_close() { restart_container->hide(); } +void ProjectSettingsEditor::_action_added(const String &p_name) { + String name = "input/" + p_name; + + if (ProjectSettings::get_singleton()->has_setting(name)) { + action_map->show_message(vformat(TTR("An action with the name '%s' already exists."), name)); + return; + } + + Dictionary action; + action["events"] = Array(); + action["deadzone"] = 0.5f; + + undo_redo->create_action(TTR("Add Input Action")); + undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, action); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", name); + + undo_redo->add_do_method(this, "_update_action_map_editor"); + undo_redo->add_undo_method(this, "_update_action_map_editor"); + undo_redo->add_do_method(this, "queue_save"); + undo_redo->add_undo_method(this, "queue_save"); + undo_redo->commit_action(); +} + +void ProjectSettingsEditor::_action_edited(const String &p_name, const Dictionary &p_action) { + const String property_name = "input/" + p_name; + Dictionary old_val = ProjectSettings::get_singleton()->get(property_name); + + if (old_val["deadzone"] != p_action["deadzone"]) { + // Deadzone Changed + undo_redo->create_action(TTR("Change Action deadzone")); + undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", property_name, p_action); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", property_name, old_val); + + } else { + // Events changed + int event_count = ((Array)p_action["events"]).size(); + int old_event_count = ((Array)old_val["events"]).size(); + + if (event_count == old_event_count) { + undo_redo->create_action(TTR("Edit Input Action Event")); + } else if (event_count > old_event_count) { + undo_redo->create_action(TTR("Add Input Action Event")); + } else if (event_count < old_event_count) { + undo_redo->create_action(TTR("Remove Input Action Event")); + } + + undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", property_name, p_action); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", property_name, old_val); + } + + undo_redo->add_do_method(this, "_update_action_map_editor"); + undo_redo->add_undo_method(this, "_update_action_map_editor"); + undo_redo->add_do_method(this, "queue_save"); + undo_redo->add_undo_method(this, "queue_save"); + undo_redo->commit_action(); +} + +void ProjectSettingsEditor::_action_removed(const String &p_name) { + const String property_name = "input/" + p_name; + + Dictionary old_val = ProjectSettings::get_singleton()->get(property_name); + int order = ProjectSettings::get_singleton()->get_order(property_name); + + undo_redo->create_action(TTR("Erase Input Action")); + undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", property_name); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", property_name, old_val); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", property_name, order); + + undo_redo->add_do_method(this, "_update_action_map_editor"); + undo_redo->add_undo_method(this, "_update_action_map_editor"); + undo_redo->add_do_method(this, "queue_save"); + undo_redo->add_undo_method(this, "queue_save"); + undo_redo->commit_action(); +} + +void ProjectSettingsEditor::_action_renamed(const String &p_old_name, const String &p_new_name) { + const String old_property_name = "input/" + p_old_name; + const String new_property_name = "input/" + p_new_name; + + if (ProjectSettings::get_singleton()->has_setting(new_property_name)) { + action_map->show_message(vformat(TTR("An action with the name '%s' already exists."), new_property_name)); + return; + } + + int order = ProjectSettings::get_singleton()->get_order(old_property_name); + Dictionary action = ProjectSettings::get_singleton()->get(old_property_name); + + undo_redo->create_action(TTR("Rename Input Action Event")); + // Do: clear old, set new + undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", old_property_name); + undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", new_property_name, action); + undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", new_property_name, order); + // Undo: clear new, set old + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", new_property_name); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", old_property_name, action); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", old_property_name, order); + + undo_redo->add_do_method(this, "_update_action_map_editor"); + undo_redo->add_undo_method(this, "_update_action_map_editor"); + undo_redo->add_do_method(this, "queue_save"); + undo_redo->add_undo_method(this, "queue_save"); + undo_redo->commit_action(); +} + +void ProjectSettingsEditor::_action_reordered(const String &p_action_name, const String &p_relative_to, bool p_before) { + const String action_name = "input/" + p_action_name; + const String target_name = "input/" + p_relative_to; + + // It is much easier to rebuild the custom "input" properties rather than messing around with the "order" values of them. + Variant action_value = ps->get(action_name); + Variant target_value = ps->get(target_name); + + List<PropertyInfo> props; + OrderedHashMap<String, Variant> action_values; + ProjectSettings::get_singleton()->get_property_list(&props); + + undo_redo->create_action(TTR("Update Input Action Order")); + + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + PropertyInfo prop = E->get(); + // Skip builtins and non-inputs + if (ProjectSettings::get_singleton()->is_builtin_setting(prop.name) || !prop.name.begins_with("input/")) { + continue; + } + + action_values.insert(prop.name, ps->get(prop.name)); + + undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", prop.name); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", prop.name); + } + + for (OrderedHashMap<String, Variant>::Element E = action_values.front(); E; E = E.next()) { + String name = E.key(); + Variant value = E.get(); + + if (name == target_name) { + if (p_before) { + // Insert before target + undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", action_name, action_value); + undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", target_name, target_value); + + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", target_name, target_value); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", action_name, action_value); + } else { + // Insert after target + undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", target_name, target_value); + undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", action_name, action_value); + + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", action_name, action_value); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", target_name, target_value); + } + + } else if (name != action_name) { + undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, value); + undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", name, value); + } + } + + undo_redo->add_do_method(this, "_update_action_map_editor"); + undo_redo->add_undo_method(this, "_update_action_map_editor"); + undo_redo->add_do_method(this, "queue_save"); + undo_redo->add_undo_method(this, "queue_save"); + undo_redo->commit_action(); +} + +void ProjectSettingsEditor::_update_action_map_editor() { + Vector<ActionMapEditor::ActionInfo> actions; + + List<PropertyInfo> props; + ProjectSettings::get_singleton()->get_property_list(&props); + + const Ref<Texture2D> builtin_icon = get_theme_icon("PinPressed", "EditorIcons"); + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + const String property_name = E->get().name; + + if (!property_name.begins_with("input/")) { + continue; + } + + // Strip the "input/" from the left. + String display_name = property_name.substr(String("input/").size() - 1); + Dictionary action = ProjectSettings::get_singleton()->get(property_name); + + ActionMapEditor::ActionInfo action_info; + action_info.action = action; + action_info.editable = true; + action_info.name = display_name; + + const bool is_builtin_input = ProjectSettings::get_singleton()->get_input_presets().find(property_name) != nullptr; + if (is_builtin_input) { + action_info.editable = false; + action_info.icon = builtin_icon; + } + + actions.push_back(action_info); + } + + action_map->update_action_list(actions); +} + void ProjectSettingsEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { if (!is_visible()) { EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "project_settings", Rect2(get_position(), get_size())); - if (advanced->is_pressed()) { - advanced->set_pressed(false); - advanced_bar->hide(); - } } } break; case NOTIFICATION_ENTER_TREE: { inspector->edit(ps); - error_label->add_theme_color_override("font_color", error_label->get_theme_color("error_color", "Editor")); add_button->set_icon(get_theme_icon("Add", "EditorIcons")); del_button->set_icon(get_theme_icon("Remove", "EditorIcons")); @@ -293,6 +489,8 @@ void ProjectSettingsEditor::_notification(int p_what) { restart_container->add_theme_style_override("panel", get_theme_stylebox("bg", "Tree")); restart_icon->set_texture(get_theme_icon("StatusWarning", "EditorIcons")); restart_label->add_theme_color_override("font_color", get_theme_color("warning_color", "Editor")); + + _update_action_map_editor(); } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { search_box->set_right_icon(get_theme_icon("Search", "EditorIcons")); @@ -303,6 +501,8 @@ void ProjectSettingsEditor::_notification(int p_what) { void ProjectSettingsEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("queue_save"), &ProjectSettingsEditor::queue_save); + + ClassDB::bind_method(D_METHOD("_update_action_map_editor"), &ProjectSettingsEditor::_update_action_map_editor); } ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { @@ -340,23 +540,19 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { search_bar->add_child(search_box); advanced = memnew(CheckButton); - advanced->set_text(TTR("Advanced")); + advanced->set_text(TTR("Advanced Settings")); advanced->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_advanced_pressed)); search_bar->add_child(advanced); } { // Advanced bar. - advanced_bar = memnew(VBoxContainer); - advanced_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL); + advanced_bar = memnew(HBoxContainer); advanced_bar->hide(); header->add_child(advanced_bar); - advanced_bar->add_child(memnew(HSeparator)); - - HBoxContainer *hbc = memnew(HBoxContainer); + HBoxContainer *hbc = advanced_bar; hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); - advanced_bar->add_margin_child(TTR("Add or Remove Custom Project Settings:"), hbc, true); category_box = memnew(LineEdit); category_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -365,7 +561,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { hbc->add_child(category_box); Label *l = memnew(Label); - l->set_text("/"); + l->set_text(" / "); hbc->add_child(l); property_box = memnew(LineEdit); @@ -408,9 +604,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { del_button->set_flat(true); del_button->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_delete_setting), varray(false)); hbc->add_child(del_button); - - error_label = memnew(Label); - advanced_bar->add_child(error_label); } inspector = memnew(SectionedInspector); @@ -448,10 +641,16 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { restart_close_button->connect("pressed", callable_mp(this, &ProjectSettingsEditor::_editor_restart_close)); restart_hb->add_child(restart_close_button); - inputmap_editor = memnew(InputMapEditor); - inputmap_editor->set_name(TTR("Input Map")); - inputmap_editor->connect("inputmap_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); - tab_container->add_child(inputmap_editor); + action_map = memnew(ActionMapEditor); + action_map->set_name(TTR("Input Map")); + action_map->connect("action_added", callable_mp(this, &ProjectSettingsEditor::_action_added)); + action_map->connect("action_edited", callable_mp(this, &ProjectSettingsEditor::_action_edited)); + action_map->connect("action_removed", callable_mp(this, &ProjectSettingsEditor::_action_removed)); + action_map->connect("action_renamed", callable_mp(this, &ProjectSettingsEditor::_action_renamed)); + action_map->connect("action_reordered", callable_mp(this, &ProjectSettingsEditor::_action_reordered)); + action_map->set_toggle_editable_label(TTR("Show built-in Actions")); + action_map->set_show_uneditable(false); + tab_container->add_child(action_map); localization_editor = memnew(LocalizationEditor); localization_editor->set_name(TTR("Localization")); @@ -484,4 +683,13 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { get_ok_button()->set_text(TTR("Close")); set_hide_on_ok(true); + + bool use_advanced = EditorSettings::get_singleton()->get_project_metadata("project_settings", "advanced_mode", false); + + if (use_advanced) { + advanced->set_pressed(true); + advanced_bar->show(); + } + + inspector->set_restrict_to_basic_settings(!use_advanced); } diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index 88c96540ff..c28785bb27 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -32,10 +32,10 @@ #define PROJECT_SETTINGS_EDITOR_H #include "core/object/undo_redo.h" +#include "editor/action_map_editor.h" #include "editor/editor_data.h" #include "editor/editor_plugin_settings.h" #include "editor/editor_sectioned_inspector.h" -#include "editor/input_map_editor.h" #include "editor/localization_editor.h" #include "editor/shader_globals_editor.h" #include "editor_autoload_settings.h" @@ -44,38 +44,29 @@ class ProjectSettingsEditor : public AcceptDialog { GDCLASS(ProjectSettingsEditor, AcceptDialog); - enum InputType { - INPUT_KEY, - INPUT_KEY_PHYSICAL, - INPUT_JOY_BUTTON, - INPUT_JOY_MOTION, - INPUT_MOUSE_BUTTON - }; - static ProjectSettingsEditor *singleton; ProjectSettings *ps; Timer *timer; TabContainer *tab_container; SectionedInspector *inspector; - InputMapEditor *inputmap_editor; LocalizationEditor *localization_editor; EditorAutoloadSettings *autoload_settings; ShaderGlobalsEditor *shaders_global_variables_editor; EditorPluginSettings *plugin_settings; + ActionMapEditor *action_map; HBoxContainer *search_bar; LineEdit *search_box; CheckButton *advanced; - VBoxContainer *advanced_bar; + HBoxContainer *advanced_bar; LineEdit *category_box; LineEdit *property_box; Button *add_button; Button *del_button; OptionButton *type; OptionButton *feature_override; - Label *error_label; ConfirmationDialog *del_confirmation; @@ -103,6 +94,14 @@ class ProjectSettingsEditor : public AcceptDialog { void _editor_restart_close(); void _add_feature_overrides(); + + void _action_added(const String &p_name); + void _action_edited(const String &p_name, const Dictionary &p_action); + void _action_removed(const String &p_name); + void _action_renamed(const String &p_old_name, const String &p_new_name); + void _action_reordered(const String &p_action_name, const String &p_relative_to, bool p_before); + void _update_action_map_editor(); + ProjectSettingsEditor(); protected: diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index c1edeeeb0e..48c4d33184 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -107,8 +107,6 @@ void SceneTreeDock::_unhandled_key_input(Ref<InputEvent> p_event) { _tool_selected(TOOL_MOVE_DOWN); } else if (ED_IS_SHORTCUT("scene_tree/reparent", p_event)) { _tool_selected(TOOL_REPARENT); - } else if (ED_IS_SHORTCUT("scene_tree/merge_from_scene", p_event)) { - _tool_selected(TOOL_MERGE_FROM_SCENE); } else if (ED_IS_SHORTCUT("scene_tree/save_branch_as_scene", p_event)) { _tool_selected(TOOL_NEW_SCENE_FROM); } else if (ED_IS_SHORTCUT("scene_tree/delete_no_confirm", p_event)) { @@ -880,13 +878,6 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } } break; - case TOOL_MERGE_FROM_SCENE: { - if (!profile_allow_editing) { - break; - } - - EditorNode::get_singleton()->merge_from_scene(); - } break; case TOOL_NEW_SCENE_FROM: { if (!profile_allow_editing) { break; @@ -2291,21 +2282,6 @@ void SceneTreeDock::set_selected(Node *p_node, bool p_emit_selected) { scene_tree->set_selected(p_node, p_emit_selected); } -void SceneTreeDock::import_subscene() { - import_subscene_dialog->popup_centered_clamped(Size2(500, 800) * EDSCALE, 0.8); -} - -void SceneTreeDock::_import_subscene() { - Node *parent = scene_tree->get_selected(); - if (!parent) { - parent = editor_data->get_edited_scene_root(); - ERR_FAIL_COND(!parent); - } - - import_subscene_dialog->move(parent, edited_scene); - editor_data->get_undo_redo().clear_history(); //no undo for now.. -} - void SceneTreeDock::_new_scene_from(String p_file) { List<Node *> selection = editor_selection->get_selected_node_list(); @@ -2622,7 +2598,6 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { if (selection.size() == 1) { if (profile_allow_editing) { menu->add_separator(); - menu->add_icon_shortcut(get_theme_icon("Blend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/merge_from_scene"), TOOL_MERGE_FROM_SCENE); menu->add_icon_shortcut(get_theme_icon("CreateNewSceneFrom", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/save_branch_as_scene"), TOOL_NEW_SCENE_FROM); } if (full_selection.size() == 1) { @@ -2921,7 +2896,7 @@ void SceneTreeDock::_create_remap_for_node(Node *p_node, Map<RES, RES> &r_remap) if (v.is_ref()) { RES res = v; if (res.is_valid()) { - if (res->get_path() == "" && !r_remap.has(res)) { + if ((res->get_path() == "" || res->get_path().find("::") > -1) && !r_remap.has(res)) { _create_remap_for_resource(res, r_remap); } } @@ -2948,7 +2923,7 @@ void SceneTreeDock::_create_remap_for_resource(RES p_resource, Map<RES, RES> &r_ if (v.is_ref()) { RES res = v; if (res.is_valid()) { - if (res->get_path() == "" && !r_remap.has(res)) { + if ((res->get_path() == "" || res->get_path().find("::") > -1) && !r_remap.has(res)) { _create_remap_for_resource(res, r_remap); } } @@ -3000,7 +2975,6 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel ED_SHORTCUT("scene_tree/reparent", TTR("Reparent")); ED_SHORTCUT("scene_tree/reparent_to_new_node", TTR("Reparent to New Node")); ED_SHORTCUT("scene_tree/make_root", TTR("Make Scene Root")); - ED_SHORTCUT("scene_tree/merge_from_scene", TTR("Merge From Scene")); ED_SHORTCUT("scene_tree/save_branch_as_scene", TTR("Save Branch as Scene")); ED_SHORTCUT("scene_tree/copy_node_path", TTR("Copy Node Path"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_C); ED_SHORTCUT("scene_tree/delete_no_confirm", TTR("Delete (No Confirm)"), KEY_MASK_SHIFT | KEY_DELETE); @@ -3130,10 +3104,6 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel add_child(placeholder_editable_instance_remove_dialog); placeholder_editable_instance_remove_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_toggle_placeholder_from_selection)); - import_subscene_dialog = memnew(EditorSubScene); - add_child(import_subscene_dialog); - import_subscene_dialog->connect("subscene_selected", callable_mp(this, &SceneTreeDock::_import_subscene)); - new_scene_from_dialog = memnew(EditorFileDialog); new_scene_from_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); add_child(new_scene_from_dialog); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index a042188be6..9bc281c7fb 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -34,7 +34,6 @@ #include "editor/connections_dialog.h" #include "editor/create_dialog.h" #include "editor/editor_data.h" -#include "editor/editor_sub_scene.h" #include "editor/groups_editor.h" #include "editor/quick_open.h" #include "editor/rename_dialog.h" @@ -74,7 +73,6 @@ class SceneTreeDock : public VBoxContainer { TOOL_REPARENT_TO_NEW_NODE, TOOL_MAKE_ROOT, TOOL_NEW_SCENE_FROM, - TOOL_MERGE_FROM_SCENE, TOOL_MULTI_EDIT, TOOL_ERASE, TOOL_COPY_NODE_PATH, @@ -141,7 +139,6 @@ class SceneTreeDock : public VBoxContainer { ReparentDialog *reparent_dialog; EditorQuickOpen *quick_open; - EditorSubScene *import_subscene_dialog; EditorFileDialog *new_scene_from_dialog; LineEdit *filter; diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index 1dad3c091d..3852c389c7 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -31,6 +31,7 @@ #include "settings_config_dialog.h" #include "core/config/project_settings.h" +#include "core/input/input_map.h" #include "core/os/keyboard.h" #include "editor/debugger/editor_debugger_node.h" #include "editor_file_system.h" @@ -143,7 +144,7 @@ void EditorSettingsDialog::_unhandled_input(const Ref<InputEvent> &p_event) { if (k.is_valid() && k->is_pressed()) { bool handled = false; - if (ED_IS_SHORTCUT("editor/undo", p_event)) { + if (ED_IS_SHORTCUT("ui_undo", p_event)) { String action = undo_redo->get_current_action_name(); if (action != "") { EditorNode::get_log()->add_message("Undo: " + action, EditorLog::MSG_TYPE_EDITOR); @@ -152,7 +153,7 @@ void EditorSettingsDialog::_unhandled_input(const Ref<InputEvent> &p_event) { handled = true; } - if (ED_IS_SHORTCUT("editor/redo", p_event)) { + if (ED_IS_SHORTCUT("ui_redo", p_event)) { undo_redo->redo(); String action = undo_redo->get_current_action_name(); if (action != "") { @@ -184,7 +185,52 @@ void EditorSettingsDialog::_update_icons() { restart_label->add_theme_color_override("font_color", shortcuts->get_theme_color("warning_color", "Editor")); } +void EditorSettingsDialog::_event_config_confirmed() { + Ref<InputEventKey> k = shortcut_editor->get_event(); + if (k.is_null()) { + return; + } + + if (editing_action) { + if (current_action_event_index == -1) { + // Add new event + current_action_events.push_back(k); + } else { + // Edit existing event + current_action_events[current_action_event_index] = k; + } + + _update_builtin_action(current_action, current_action_events); + } else { + k = k->duplicate(); + Ref<Shortcut> current_sc = EditorSettings::get_singleton()->get_shortcut(shortcut_being_edited); + + undo_redo->create_action(TTR("Change Shortcut") + " '" + shortcut_being_edited + "'"); + undo_redo->add_do_method(current_sc.ptr(), "set_shortcut", k); + undo_redo->add_undo_method(current_sc.ptr(), "set_shortcut", current_sc->get_shortcut()); + undo_redo->add_do_method(this, "_update_shortcuts"); + undo_redo->add_undo_method(this, "_update_shortcuts"); + undo_redo->add_do_method(this, "_settings_changed"); + undo_redo->add_undo_method(this, "_settings_changed"); + undo_redo->commit_action(); + } +} + +void EditorSettingsDialog::_update_builtin_action(const String &p_name, const Array &p_events) { + Array old_input_array = EditorSettings::get_singleton()->get_builtin_action_overrides(current_action); + + undo_redo->create_action(TTR("Edit Built-in Action")); + undo_redo->add_do_method(EditorSettings::get_singleton(), "set_builtin_action_override", p_name, p_events); + undo_redo->add_undo_method(EditorSettings::get_singleton(), "set_builtin_action_override", p_name, old_input_array); + undo_redo->add_do_method(this, "_settings_changed"); + undo_redo->add_undo_method(this, "_settings_changed"); + undo_redo->commit_action(); + + _update_shortcuts(); +} + void EditorSettingsDialog::_update_shortcuts() { + // Before clearing the tree, take note of which categories are collapsed so that this state can be maintained when the tree is repopulated. Map<String, bool> collapsed; if (shortcuts->get_root() && shortcuts->get_root()->get_children()) { @@ -192,15 +238,93 @@ void EditorSettingsDialog::_update_shortcuts() { collapsed[item->get_text(0)] = item->is_collapsed(); } } - shortcuts->clear(); - List<String> slist; - EditorSettings::get_singleton()->get_shortcut_list(&slist); TreeItem *root = shortcuts->create_item(); - Map<String, TreeItem *> sections; + // Set up section for Common/Built-in actions + TreeItem *common_section = shortcuts->create_item(root); + + sections["Common"] = common_section; + common_section->set_text(0, TTR("Common")); + if (collapsed.has("Common")) { + common_section->set_collapsed(collapsed["Common"]); + } + common_section->set_custom_bg_color(0, shortcuts->get_theme_color("prop_subsection", "Editor")); + common_section->set_custom_bg_color(1, shortcuts->get_theme_color("prop_subsection", "Editor")); + + // Get the action map for the editor, and add each item to the "Common" section. + OrderedHashMap<StringName, InputMap::Action> action_map = InputMap::get_singleton()->get_action_map(); + for (OrderedHashMap<StringName, InputMap::Action>::Element E = action_map.front(); E; E = E.next()) { + String action_name = E.key(); + + if (!shortcut_filter.is_subsequence_ofi(action_name)) { + continue; + } + + InputMap::Action action = E.get(); + + Array events; // Need to get the list of events into an array so it can be set as metadata on the item. + Vector<String> event_strings; + + List<Ref<InputEvent>> defaults = InputMap::get_singleton()->get_builtins().find(action_name).value(); + // Remove all non-key events from the defaults. + for (List<Ref<InputEvent>>::Element *I = defaults.front(); I; I = I->next()) { + Ref<InputEventKey> k = I->get(); + if (k.is_null()) { + I->erase(); + } + } + + bool same_as_defaults = defaults.size() == action.inputs.size(); // Initially this is set to just whether the arrays are equal. Later we check the events if needed. + + int count = 0; + for (List<Ref<InputEvent>>::Element *I = action.inputs.front(); I; I = I->next()) { + // Add event and event text to respective arrays. + events.push_back(I->get()); + event_strings.push_back(I->get()->as_text()); + + // Only check if the events have been the same so far - once one fails, we don't need to check any more. + if (same_as_defaults) { + Ref<InputEventKey> k = defaults[count]; + // Only check keys, since we are in the editor. + if (k.is_valid() && !defaults[count]->shortcut_match(I->get())) { + same_as_defaults = false; + } + } + count++; + } + + // Join the text of the events with a delimiter so they can all be displayed in one cell. + String events_display_string = event_strings.is_empty() ? "None" : String("; ").join(event_strings); + + TreeItem *item = shortcuts->create_item(common_section); + item->set_text(0, action_name); + item->set_text(1, events_display_string); + + if (!same_as_defaults) { + item->add_button(1, shortcuts->get_theme_icon("Reload", "EditorIcons"), 2); + } + + if (events_display_string == "None") { + // Fade out unassigned shortcut labels for easier visual grepping. + item->set_custom_color(1, shortcuts->get_theme_color("font_color", "Label") * Color(1, 1, 1, 0.5)); + } + + item->add_button(1, shortcuts->get_theme_icon("Edit", "EditorIcons"), 0); + item->add_button(1, shortcuts->get_theme_icon("Close", "EditorIcons"), 1); + item->set_tooltip(0, action_name); + item->set_tooltip(1, events_display_string); + item->set_metadata(0, "Common"); + item->set_metadata(1, events); + } + + // Editor Shortcuts + + List<String> slist; + EditorSettings::get_singleton()->get_shortcut_list(&slist); + for (List<String>::Element *E = slist.front(); E; E = E->next()) { Ref<Shortcut> sc = EditorSettings::get_singleton()->get_shortcut(E->get()); if (!sc->has_meta("original")) { @@ -267,84 +391,119 @@ void EditorSettingsDialog::_shortcut_button_pressed(Object *p_item, int p_column TreeItem *ti = Object::cast_to<TreeItem>(p_item); ERR_FAIL_COND(!ti); - String item = ti->get_metadata(0); - Ref<Shortcut> sc = EditorSettings::get_singleton()->get_shortcut(item); - - if (p_idx == 0) { - press_a_key_label->set_text(TTR("Press a Key...")); - last_wait_for_key = Ref<InputEventKey>(); - press_a_key->popup_centered(Size2(250, 80) * EDSCALE); - //press_a_key->grab_focus(); - press_a_key->get_ok_button()->set_focus_mode(Control::FOCUS_NONE); - press_a_key->get_cancel_button()->set_focus_mode(Control::FOCUS_NONE); - shortcut_configured = item; - - } else if (p_idx == 1) { //erase - if (!sc.is_valid()) { - return; //pointless, there is nothing + if (ti->get_metadata(0) == "Common") { + // Editing a Built-in action, which can have multiple bindings. + button_idx = p_idx; + editing_action = true; + current_action = ti->get_text(0); + + switch (button_idx) { + case SHORTCUT_REVERT: { + Array events; + List<Ref<InputEvent>> defaults = InputMap::get_singleton()->get_builtins()[current_action]; + + // Convert the list to an array, and only keep key events as this is for the editor. + for (List<Ref<InputEvent>>::Element *E = defaults.front(); E; E = E->next()) { + Ref<InputEventKey> k = E->get(); + if (k.is_valid()) { + events.append(E->get()); + } + } + + _update_builtin_action(current_action, events); + } break; + case SHORTCUT_EDIT: + case SHORTCUT_ERASE: { + // For Edit end Delete, we will show a popup which displays each event so the user can select which one to edit/delete. + current_action_events = ti->get_metadata(1); + action_popup->clear(); + + for (int i = 0; i < current_action_events.size(); i++) { + Ref<InputEvent> ie = current_action_events[i]; + action_popup->add_item(ie->as_text()); + action_popup->set_item_metadata(i, ie); + } + + if (button_idx == SHORTCUT_EDIT) { + // If editing, add a button which can be used to add an additional event. + action_popup->add_icon_item(get_theme_icon("Add", "EditorIcons"), TTR("Add")); + } + + action_popup->set_position(get_position() + get_mouse_position()); + action_popup->take_mouse_focus(); + action_popup->popup(); + action_popup->set_as_minsize(); + } break; + default: + break; } - - undo_redo->create_action(TTR("Erase Shortcut")); - undo_redo->add_do_method(sc.ptr(), "set_shortcut", Ref<InputEvent>()); - undo_redo->add_undo_method(sc.ptr(), "set_shortcut", sc->get_shortcut()); - undo_redo->add_do_method(this, "_update_shortcuts"); - undo_redo->add_undo_method(this, "_update_shortcuts"); - undo_redo->add_do_method(this, "_settings_changed"); - undo_redo->add_undo_method(this, "_settings_changed"); - undo_redo->commit_action(); - } else if (p_idx == 2) { //revert to original - if (!sc.is_valid()) { - return; //pointless, there is nothing + } else { + // Editing an Editor Shortcut, which can only have 1 binding. + String item = ti->get_metadata(0); + Ref<Shortcut> sc = EditorSettings::get_singleton()->get_shortcut(item); + editing_action = false; + + switch (button_idx) { + case EditorSettingsDialog::SHORTCUT_EDIT: + shortcut_editor->popup_and_configure(sc->get_shortcut()); + shortcut_being_edited = item; + break; + case EditorSettingsDialog::SHORTCUT_ERASE: { + if (!sc.is_valid()) { + return; //pointless, there is nothing + } + + undo_redo->create_action(TTR("Erase Shortcut")); + undo_redo->add_do_method(sc.ptr(), "set_shortcut", Ref<InputEvent>()); + undo_redo->add_undo_method(sc.ptr(), "set_shortcut", sc->get_shortcut()); + undo_redo->add_do_method(this, "_update_shortcuts"); + undo_redo->add_undo_method(this, "_update_shortcuts"); + undo_redo->add_do_method(this, "_settings_changed"); + undo_redo->add_undo_method(this, "_settings_changed"); + undo_redo->commit_action(); + } break; + case EditorSettingsDialog::SHORTCUT_REVERT: { + if (!sc.is_valid()) { + return; //pointless, there is nothing + } + + Ref<InputEvent> original = sc->get_meta("original"); + + undo_redo->create_action(TTR("Restore Shortcut")); + undo_redo->add_do_method(sc.ptr(), "set_shortcut", original); + undo_redo->add_undo_method(sc.ptr(), "set_shortcut", sc->get_shortcut()); + undo_redo->add_do_method(this, "_update_shortcuts"); + undo_redo->add_undo_method(this, "_update_shortcuts"); + undo_redo->add_do_method(this, "_settings_changed"); + undo_redo->add_undo_method(this, "_settings_changed"); + undo_redo->commit_action(); + } break; + default: + break; } - - Ref<InputEvent> original = sc->get_meta("original"); - - undo_redo->create_action(TTR("Restore Shortcut")); - undo_redo->add_do_method(sc.ptr(), "set_shortcut", original); - undo_redo->add_undo_method(sc.ptr(), "set_shortcut", sc->get_shortcut()); - undo_redo->add_do_method(this, "_update_shortcuts"); - undo_redo->add_undo_method(this, "_update_shortcuts"); - undo_redo->add_do_method(this, "_settings_changed"); - undo_redo->add_undo_method(this, "_settings_changed"); - undo_redo->commit_action(); } } -void EditorSettingsDialog::_wait_for_key(const Ref<InputEvent> &p_event) { - Ref<InputEventKey> k = p_event; - - if (k.is_valid() && k->is_pressed() && k->get_keycode() != 0) { - last_wait_for_key = k; - const String str = keycode_get_string(k->get_keycode_with_modifiers()); - - press_a_key_label->set_text(str); - press_a_key->set_input_as_handled(); - } -} - -void EditorSettingsDialog::_press_a_key_confirm() { - if (last_wait_for_key.is_null()) { - return; +void EditorSettingsDialog::_builtin_action_popup_index_pressed(int p_index) { + switch (button_idx) { + case SHORTCUT_EDIT: { + if (p_index == action_popup->get_item_count() - 1) { + // Selected last item in list (Add button), therefore add new + current_action_event_index = -1; + shortcut_editor->popup_and_configure(); + } else { + // Configure existing + current_action_event_index = p_index; + shortcut_editor->popup_and_configure(action_popup->get_item_metadata(p_index)); + } + } break; + case SHORTCUT_ERASE: { + current_action_events.remove(p_index); + _update_builtin_action(current_action, current_action_events); + } break; + default: + break; } - - Ref<InputEventKey> ie; - ie.instance(); - ie->set_keycode(last_wait_for_key->get_keycode()); - ie->set_shift(last_wait_for_key->get_shift()); - ie->set_control(last_wait_for_key->get_control()); - ie->set_alt(last_wait_for_key->get_alt()); - ie->set_metakey(last_wait_for_key->get_metakey()); - - Ref<Shortcut> sc = EditorSettings::get_singleton()->get_shortcut(shortcut_configured); - - undo_redo->create_action(TTR("Change Shortcut") + " '" + shortcut_configured + "'"); - undo_redo->add_do_method(sc.ptr(), "set_shortcut", ie); - undo_redo->add_undo_method(sc.ptr(), "set_shortcut", sc->get_shortcut()); - undo_redo->add_do_method(this, "_update_shortcuts"); - undo_redo->add_undo_method(this, "_update_shortcuts"); - undo_redo->add_do_method(this, "_settings_changed"); - undo_redo->add_undo_method(this, "_settings_changed"); - undo_redo->commit_action(); } void EditorSettingsDialog::_tabs_tab_changed(int p_tab) { @@ -382,9 +541,14 @@ void EditorSettingsDialog::_editor_restart_close() { void EditorSettingsDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_unhandled_input"), &EditorSettingsDialog::_unhandled_input); ClassDB::bind_method(D_METHOD("_update_shortcuts"), &EditorSettingsDialog::_update_shortcuts); + ClassDB::bind_method(D_METHOD("_settings_changed"), &EditorSettingsDialog::_settings_changed); } EditorSettingsDialog::EditorSettingsDialog() { + action_popup = memnew(PopupMenu); + action_popup->connect("index_pressed", callable_mp(this, &EditorSettingsDialog::_builtin_action_popup_index_pressed)); + add_child(action_popup); + set_title(TTR("Editor Settings")); undo_redo = memnew(UndoRedo); @@ -442,21 +606,17 @@ EditorSettingsDialog::EditorSettingsDialog() { // Shortcuts Tab tab_shortcuts = memnew(VBoxContainer); + tabs->add_child(tab_shortcuts); tab_shortcuts->set_name(TTR("Shortcuts")); - hbc = memnew(HBoxContainer); - hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); - tab_shortcuts->add_child(hbc); - shortcut_search_box = memnew(LineEdit); shortcut_search_box->set_placeholder(TTR("Search")); shortcut_search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); - hbc->add_child(shortcut_search_box); + tab_shortcuts->add_child(shortcut_search_box); shortcut_search_box->connect("text_changed", callable_mp(this, &EditorSettingsDialog::_filter_shortcuts)); shortcuts = memnew(Tree); - tab_shortcuts->add_child(shortcuts, true); shortcuts->set_v_size_flags(Control::SIZE_EXPAND_FILL); shortcuts->set_columns(2); shortcuts->set_hide_root(true); @@ -464,21 +624,13 @@ EditorSettingsDialog::EditorSettingsDialog() { shortcuts->set_column_title(0, TTR("Name")); shortcuts->set_column_title(1, TTR("Binding")); shortcuts->connect("button_pressed", callable_mp(this, &EditorSettingsDialog::_shortcut_button_pressed)); + tab_shortcuts->add_child(shortcuts); - press_a_key = memnew(ConfirmationDialog); - //press_a_key->set_focus_mode(Control::FOCUS_ALL); - add_child(press_a_key); - - Label *l = memnew(Label); - l->set_text(TTR("Press a Key...")); - l->set_anchors_and_offsets_preset(Control::PRESET_WIDE); - l->set_align(Label::ALIGN_CENTER); - l->set_offset(SIDE_TOP, 20); - l->set_anchor_and_offset(SIDE_BOTTOM, Control::ANCHOR_BEGIN, 30); - press_a_key_label = l; - press_a_key->add_child(l); - press_a_key->connect("window_input", callable_mp(this, &EditorSettingsDialog::_wait_for_key)); - press_a_key->connect("confirmed", callable_mp(this, &EditorSettingsDialog::_press_a_key_confirm)); + // Adding event dialog + shortcut_editor = memnew(InputEventConfigurationDialog); + shortcut_editor->connect("confirmed", callable_mp(this, &EditorSettingsDialog::_event_config_confirmed)); + shortcut_editor->set_allowed_input_types(InputEventConfigurationDialog::InputType::INPUT_KEY); + add_child(shortcut_editor); set_hide_on_ok(true); diff --git a/editor/settings_config_dialog.h b/editor/settings_config_dialog.h index b1ee58ae8f..c38fceedf1 100644 --- a/editor/settings_config_dialog.h +++ b/editor/settings_config_dialog.h @@ -31,6 +31,7 @@ #ifndef SETTINGS_CONFIG_DIALOG_H #define SETTINGS_CONFIG_DIALOG_H +#include "editor/action_map_editor.h" #include "editor/editor_sectioned_inspector.h" #include "editor_inspector.h" #include "scene/gui/dialogs.h" @@ -52,16 +53,28 @@ class EditorSettingsDialog : public AcceptDialog { LineEdit *shortcut_search_box; SectionedInspector *inspector; + enum ShortcutButton { + SHORTCUT_EDIT, + SHORTCUT_ERASE, + SHORTCUT_REVERT + }; + + int button_idx; + int current_action_event_index = -1; + bool editing_action = false; + String current_action; + Array current_action_events; + PopupMenu *action_popup; + Timer *timer; UndoRedo *undo_redo; - Tree *shortcuts; - ConfirmationDialog *press_a_key; - Label *press_a_key_label; - Ref<InputEventKey> last_wait_for_key; - String shortcut_configured; + // Shortcuts String shortcut_filter; + Tree *shortcuts; + InputEventConfigurationDialog *shortcut_editor; + String shortcut_being_edited; virtual void cancel_pressed() override; virtual void ok_pressed() override; @@ -74,20 +87,20 @@ class EditorSettingsDialog : public AcceptDialog { void _notification(int p_what); void _update_icons(); - void _press_a_key_confirm(); - void _wait_for_key(const Ref<InputEvent> &p_event); + void _event_config_confirmed(); + + void _update_builtin_action(const String &p_name, const Array &p_events); void _tabs_tab_changed(int p_tab); void _focus_current_search_box(); - void _clear_shortcut_search_box(); - void _clear_search_box(); - void _filter_shortcuts(const String &p_filter); void _update_shortcuts(); void _shortcut_button_pressed(Object *p_item, int p_column, int p_idx); + void _builtin_action_popup_index_pressed(int p_index); + static void _undo_redo_callback(void *p_self, const String &p_name); Label *restart_label; |