Skip to content

Commit

Permalink
Added Built-in Action editor to Editor Settings dialog.
Browse files Browse the repository at this point in the history
EditorSettings will save and serialize to editor_settings.tres what the overrides are for each builtin action so they persist throughout sessions. Shortcuts are also created for each built in action as needed, however they are not serialised as they are auto-generated from the builtin action overrides.
  • Loading branch information
EricEzaM committed Oct 7, 2020
1 parent 4865226 commit bba3369
Show file tree
Hide file tree
Showing 7 changed files with 373 additions and 91 deletions.
228 changes: 159 additions & 69 deletions core/input/input_map.cpp

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion core/input/input_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ class InputMap : public Object {
void load_from_project_settings();
void load_default();

String get_builtin_display_name(const String &p_name) const;
// Use an Ordered Map so insertion order is preserved. We want the elements to be 'grouped' somewhat.
OrderedHashMap<StringName, List<Ref<InputEvent>>> get_builtins();
const OrderedHashMap<String, List<Ref<InputEvent>>> &get_builtins();

InputMap();
};
Expand Down
152 changes: 141 additions & 11 deletions editor/editor_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include "editor_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"
Expand Down Expand Up @@ -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) {
Expand All @@ -83,6 +84,24 @@ bool EditorSettings::_set_only(const StringName &p_name, const Variant &p_value)
add_shortcut(name, sc);
}

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;
}

Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -1492,12 +1538,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) {
Expand All @@ -1518,11 +1591,6 @@ Ref<Shortcut> ED_GET_SHORTCUT(const String &p_path) {
return sc;
}

struct ShortcutMapping {
const char *path;
uint32_t keycode;
};

Ref<Shortcut> ED_SHORTCUT(const String &p_path, const String &p_name, uint32_t p_keycode) {
#ifdef OSX_ENABLED
// Use Cmd+Backspace as a general replacement for Delete shortcuts on macOS
Expand Down Expand Up @@ -1568,6 +1636,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->next()) {
if (E->get() != 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_

Expand Down Expand Up @@ -1606,6 +1734,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);
Expand Down
6 changes: 5 additions & 1 deletion editor/editor_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ class EditorSettings : public Resource {
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;
Expand Down Expand Up @@ -186,6 +187,9 @@ class EditorSettings : public Resource {
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();
Expand Down
12 changes: 6 additions & 6 deletions editor/plugins/script_text_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1739,14 +1739,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);
Expand Down
55 changes: 55 additions & 0 deletions editor/settings_config_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include "settings_config_dialog.h"

#include "core/input/input_map.h"
#include "core/os/keyboard.h"
#include "core/project_settings.h"
#include "editor/debugger/editor_debugger_node.h"
Expand Down Expand Up @@ -127,6 +128,7 @@ void EditorSettingsDialog::_notification(int p_what) {
} break;
case NOTIFICATION_ENTER_TREE: {
_update_icons();
_update_action_map_editor();
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
_update_icons();
Expand Down Expand Up @@ -358,6 +360,8 @@ void EditorSettingsDialog::_focus_current_search_box() {
current_search_box = search_box;
} else if (tab == tab_shortcuts) {
current_search_box = shortcut_search_box;
} else if (tab == builtin_action_editor) {
current_search_box = builtin_action_editor->get_search_box();
}

if (current_search_box) {
Expand All @@ -382,6 +386,8 @@ 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);
ClassDB::bind_method(D_METHOD("_update_action_map_editor"), &EditorSettingsDialog::_update_action_map_editor);
}

EditorSettingsDialog::EditorSettingsDialog() {
Expand Down Expand Up @@ -480,6 +486,16 @@ EditorSettingsDialog::EditorSettingsDialog() {
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));

// Builtin Actions
builtin_action_editor = memnew(ActionMapEditor);
builtin_action_editor->set_name(TTR("Built-in Actions"));
builtin_action_editor->connect("action_edited", callable_mp(this, &EditorSettingsDialog::_action_edited));
builtin_action_editor->set_toggle_editable_label(TTR("Show built-in Actions"));
builtin_action_editor->set_allow_editing_actions(false);
builtin_action_editor->set_show_uneditable(true);
builtin_action_editor->get_configuration_dialog()->set_allowed_input_types(InputEventConfigurationDialog::INPUT_KEY);
tabs->add_child(builtin_action_editor);

set_hide_on_ok(true);

timer = memnew(Timer);
Expand All @@ -493,6 +509,45 @@ EditorSettingsDialog::EditorSettingsDialog() {
updating = false;
}

void EditorSettingsDialog::_action_edited(const String &p_name, const Dictionary &p_action) {
Array new_input_array = p_action["events"];
Array old_input_array = EditorSettings::get_singleton()->get_builtin_action_overrides(p_name);

undo_redo->create_action(TTR("Edit Built-in Action"));
undo_redo->add_do_method(EditorSettings::get_singleton(), "set_builtin_action_override", p_name, new_input_array);
undo_redo->add_undo_method(EditorSettings::get_singleton(), "set_builtin_action_override", p_name, old_input_array);
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, "_settings_changed");
undo_redo->add_undo_method(this, "_settings_changed");
undo_redo->commit_action();
}

void EditorSettingsDialog::_update_action_map_editor() {
OrderedHashMap<StringName, InputMap::Action> action_map = InputMap::get_singleton()->get_action_map();

Vector<ActionMapEditor::ActionInfo> actions;
for (OrderedHashMap<StringName, InputMap::Action>::Element E = action_map.front(); E; E = E.next()) {
Array events;
for (List<Ref<InputEvent>>::Element *I = E.get().inputs.front(); I; I = I->next()) {
events.push_back(I->get());
}

Dictionary action;
action["deadzone"] = Variant(E.get().deadzone);
action["events"] = events;

ActionMapEditor::ActionInfo action_info;
action_info.action = action;
action_info.editable = false;
action_info.name = E.key();

actions.push_back(action_info);
}

builtin_action_editor->update_action_list(actions);
}

EditorSettingsDialog::~EditorSettingsDialog() {
memdelete(undo_redo);
}
Loading

0 comments on commit bba3369

Please sign in to comment.