diff options
author | Eric M <itsjusteza@gmail.com> | 2020-10-02 23:12:20 +1000 |
---|---|---|
committer | Eric M <itsjusteza@gmail.com> | 2021-02-19 19:36:42 +1000 |
commit | 8d9256e13c0289d625cb5c5867ffb7e190dd604c (patch) | |
tree | 799aaa08ce3a845c9bbf8afb17b96e2c2e7786ac | |
parent | 3db45ff1988a0ff685f9988a02f706d3932a9ced (diff) |
Added Built-in Action editor to Editor Settings dialog.
Built-in actions can now be edited for the Editor too.
Also added usage of the new Event confifiguration dialog to for better UX.
-rw-r--r-- | editor/settings_config_dialog.cpp | 344 | ||||
-rw-r--r-- | editor/settings_config_dialog.h | 33 |
2 files changed, 271 insertions, 106 deletions
diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index ffd5716364..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" @@ -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; |