Path: blob/master/editor/settings/input_event_configuration_dialog.cpp
9898 views
/**************************************************************************/1/* input_event_configuration_dialog.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "input_event_configuration_dialog.h"3132#include "core/input/input_map.h"33#include "editor/editor_string_names.h"34#include "editor/settings/event_listener_line_edit.h"35#include "editor/themes/editor_scale.h"36#include "scene/gui/check_box.h"37#include "scene/gui/line_edit.h"38#include "scene/gui/option_button.h"39#include "scene/gui/separator.h"40#include "scene/gui/tree.h"4142void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, const Ref<InputEvent> &p_original_event, bool p_update_input_list_selection) {43if (p_event.is_valid()) {44event = p_event;45original_event = p_original_event;4647// If the event is changed to something which is not the same as the listener,48// clear out the event from the listener text box to avoid confusion.49const Ref<InputEvent> listener_event = event_listener->get_event();50if (listener_event.is_valid() && !listener_event->is_match(p_event)) {51event_listener->clear_event();52}5354// Update Label55event_as_text->set_text(EventListenerLineEdit::get_event_text(event, true));5657Ref<InputEventKey> k = p_event;58Ref<InputEventMouseButton> mb = p_event;59Ref<InputEventJoypadButton> joyb = p_event;60Ref<InputEventJoypadMotion> joym = p_event;61Ref<InputEventWithModifiers> mod = p_event;6263// Update option values and visibility64bool show_mods = false;65bool show_device = false;66bool show_key = false;67bool show_location = false;6869if (mod.is_valid()) {70show_mods = true;71mod_checkboxes[MOD_ALT]->set_pressed(mod->is_alt_pressed());72mod_checkboxes[MOD_SHIFT]->set_pressed(mod->is_shift_pressed());73mod_checkboxes[MOD_CTRL]->set_pressed(mod->is_ctrl_pressed());74mod_checkboxes[MOD_META]->set_pressed(mod->is_meta_pressed());7576autoremap_command_or_control_checkbox->set_pressed(mod->is_command_or_control_autoremap());77}7879if (k.is_valid()) {80show_key = true;81Key phys_key = k->get_physical_keycode();82if (k->get_keycode() == Key::NONE && phys_key == Key::NONE && k->get_key_label() != Key::NONE) {83key_mode->select(KEYMODE_UNICODE);84} else if (k->get_keycode() != Key::NONE) {85key_mode->select(KEYMODE_KEYCODE);86} else if (phys_key != Key::NONE) {87key_mode->select(KEYMODE_PHY_KEYCODE);88if (phys_key == Key::SHIFT || phys_key == Key::CTRL || phys_key == Key::ALT || phys_key == Key::META) {89key_location->select((int)k->get_location());90show_location = true;91}92} else {93// Invalid key.94event = Ref<InputEvent>();95original_event = Ref<InputEvent>();96event_listener->clear_event();97event_as_text->set_text(TTRC("No Event Configured"));9899additional_options_container->hide();100input_list_tree->deselect_all();101_update_input_list();102return;103}104} else if (joyb.is_valid() || joym.is_valid() || mb.is_valid()) {105show_device = true;106_set_current_device(event->get_device());107}108109mod_container->set_visible(show_mods);110device_container->set_visible(show_device);111key_mode->set_visible(show_key);112location_container->set_visible(show_location);113additional_options_container->show();114115// Update mode selector based on original key event.116Ref<InputEventKey> ko = p_original_event;117if (ko.is_valid()) {118if (ko->get_keycode() == Key::NONE) {119if (ko->get_physical_keycode() != Key::NONE) {120ko->set_keycode(ko->get_physical_keycode());121}122if (ko->get_key_label() != Key::NONE) {123ko->set_keycode(fix_keycode((char32_t)ko->get_key_label(), Key::NONE));124}125}126127if (ko->get_physical_keycode() == Key::NONE) {128if (ko->get_keycode() != Key::NONE) {129ko->set_physical_keycode(ko->get_keycode());130}131if (ko->get_key_label() != Key::NONE) {132ko->set_physical_keycode(fix_keycode((char32_t)ko->get_key_label(), Key::NONE));133}134}135136if (ko->get_key_label() == Key::NONE) {137if (ko->get_keycode() != Key::NONE) {138ko->set_key_label(fix_key_label((char32_t)ko->get_keycode(), Key::NONE));139}140if (ko->get_physical_keycode() != Key::NONE) {141ko->set_key_label(fix_key_label((char32_t)ko->get_physical_keycode(), Key::NONE));142}143}144145key_mode->set_item_disabled(KEYMODE_KEYCODE, ko->get_keycode() == Key::NONE);146key_mode->set_item_disabled(KEYMODE_PHY_KEYCODE, ko->get_physical_keycode() == Key::NONE);147key_mode->set_item_disabled(KEYMODE_UNICODE, ko->get_key_label() == Key::NONE);148}149150// Update selected item in input list.151if (p_update_input_list_selection && (k.is_valid() || joyb.is_valid() || joym.is_valid() || mb.is_valid())) {152in_tree_update = true;153TreeItem *category = input_list_tree->get_root()->get_first_child();154while (category) {155TreeItem *input_item = category->get_first_child();156157if (input_item != nullptr) {158// input_type should always be > 0, unless the tree structure has been misconfigured.159int input_type = input_item->get_parent()->get_meta("__type", 0);160if (input_type == 0) {161in_tree_update = false;162return;163}164165// If event type matches input types of this category.166if ((k.is_valid() && input_type == INPUT_KEY) || (joyb.is_valid() && input_type == INPUT_JOY_BUTTON) || (joym.is_valid() && input_type == INPUT_JOY_MOTION) || (mb.is_valid() && input_type == INPUT_MOUSE_BUTTON)) {167// Loop through all items of this category until one matches.168while (input_item) {169bool key_match = k.is_valid() && (Variant(k->get_keycode()) == input_item->get_meta("__keycode") || Variant(k->get_physical_keycode()) == input_item->get_meta("__keycode"));170bool joyb_match = joyb.is_valid() && Variant(joyb->get_button_index()) == input_item->get_meta("__index");171bool joym_match = joym.is_valid() && Variant(joym->get_axis()) == input_item->get_meta("__axis") && joym->get_axis_value() == (float)input_item->get_meta("__value");172bool mb_match = mb.is_valid() && Variant(mb->get_button_index()) == input_item->get_meta("__index");173if (key_match || joyb_match || joym_match || mb_match) {174category->set_collapsed(false);175input_item->select(0);176input_list_tree->ensure_cursor_is_visible();177in_tree_update = false;178return;179}180input_item = input_item->get_next();181}182}183}184185category->set_collapsed(true); // Event not in this category, so collapse;186category = category->get_next();187}188in_tree_update = false;189}190} else {191// Event is not valid, reset dialog192event = Ref<InputEvent>();193original_event = Ref<InputEvent>();194event_listener->clear_event();195event_as_text->set_text(TTRC("No Event Configured"));196197additional_options_container->hide();198input_list_tree->deselect_all();199_update_input_list();200}201}202203void InputEventConfigurationDialog::_on_listen_input_changed(const Ref<InputEvent> &p_event) {204// Ignore if invalid, echo or not pressed205if (p_event.is_null() || p_event->is_echo() || !p_event->is_pressed()) {206return;207}208209// Create an editable reference and a copy of full event.210Ref<InputEvent> received_event = p_event;211Ref<InputEvent> received_original_event = received_event->duplicate();212213// Check what the type is and if it is allowed.214Ref<InputEventKey> k = received_event;215Ref<InputEventJoypadButton> joyb = received_event;216Ref<InputEventJoypadMotion> joym = received_event;217Ref<InputEventMouseButton> mb = received_event;218219int type = 0;220if (k.is_valid()) {221type = INPUT_KEY;222} else if (joyb.is_valid()) {223type = INPUT_JOY_BUTTON;224} else if (joym.is_valid()) {225type = INPUT_JOY_MOTION;226} else if (mb.is_valid()) {227type = INPUT_MOUSE_BUTTON;228}229230if (!(allowed_input_types & type)) {231return;232}233234if (joym.is_valid()) {235joym->set_axis_value(SIGN(joym->get_axis_value()));236}237238if (k.is_valid()) {239k->set_pressed(false); // To avoid serialization of 'pressed' property - doesn't matter for actions anyway.240if (key_mode->get_selected_id() == KEYMODE_KEYCODE) {241k->set_physical_keycode(Key::NONE);242k->set_key_label(Key::NONE);243} else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) {244k->set_keycode(Key::NONE);245k->set_key_label(Key::NONE);246} else if (key_mode->get_selected_id() == KEYMODE_UNICODE) {247k->set_physical_keycode(Key::NONE);248k->set_keycode(Key::NONE);249}250if (key_location->get_selected_id() == (int)KeyLocation::UNSPECIFIED) {251k->set_location(KeyLocation::UNSPECIFIED);252}253}254255Ref<InputEventWithModifiers> mod = received_event;256if (mod.is_valid()) {257mod->set_window_id(0);258}259260// Maintain device selection.261received_event->set_device(_get_current_device());262263_set_event(received_event, received_original_event);264}265266void InputEventConfigurationDialog::_search_term_updated(const String &) {267_update_input_list();268}269270void InputEventConfigurationDialog::_update_input_list() {271input_list_tree->clear();272273TreeItem *root = input_list_tree->create_item();274String search_term = input_list_search->get_text();275276bool collapse = input_list_search->get_text().is_empty();277278if (allowed_input_types & INPUT_KEY) {279TreeItem *kb_root = input_list_tree->create_item(root);280kb_root->set_text(0, TTR("Keyboard Keys"));281kb_root->set_icon(0, icon_cache.keyboard);282kb_root->set_collapsed(collapse);283kb_root->set_meta("__type", INPUT_KEY);284285for (int i = 0; i < keycode_get_count(); i++) {286String name = keycode_get_name_by_index(i);287288if (!search_term.is_empty() && !name.containsn(search_term)) {289continue;290}291292TreeItem *item = input_list_tree->create_item(kb_root);293item->set_text(0, name);294item->set_meta("__keycode", keycode_get_value_by_index(i));295}296}297298if (allowed_input_types & INPUT_MOUSE_BUTTON) {299TreeItem *mouse_root = input_list_tree->create_item(root);300mouse_root->set_text(0, TTR("Mouse Buttons"));301mouse_root->set_icon(0, icon_cache.mouse);302mouse_root->set_collapsed(collapse);303mouse_root->set_meta("__type", INPUT_MOUSE_BUTTON);304305MouseButton mouse_buttons[9] = { MouseButton::LEFT, MouseButton::RIGHT, MouseButton::MIDDLE, MouseButton::WHEEL_UP, MouseButton::WHEEL_DOWN, MouseButton::WHEEL_LEFT, MouseButton::WHEEL_RIGHT, MouseButton::MB_XBUTTON1, MouseButton::MB_XBUTTON2 };306for (int i = 0; i < 9; i++) {307Ref<InputEventMouseButton> mb;308mb.instantiate();309mb->set_button_index(mouse_buttons[i]);310String desc = EventListenerLineEdit::get_event_text(mb, false);311312if (!search_term.is_empty() && !desc.containsn(search_term)) {313continue;314}315316TreeItem *item = input_list_tree->create_item(mouse_root);317item->set_text(0, desc);318item->set_meta("__index", mouse_buttons[i]);319}320}321322if (allowed_input_types & INPUT_JOY_BUTTON) {323TreeItem *joyb_root = input_list_tree->create_item(root);324joyb_root->set_text(0, TTR("Joypad Buttons"));325joyb_root->set_icon(0, icon_cache.joypad_button);326joyb_root->set_collapsed(collapse);327joyb_root->set_meta("__type", INPUT_JOY_BUTTON);328329for (int i = 0; i < (int)JoyButton::MAX; i++) {330Ref<InputEventJoypadButton> joyb;331joyb.instantiate();332joyb->set_button_index((JoyButton)i);333String desc = EventListenerLineEdit::get_event_text(joyb, false);334335if (!search_term.is_empty() && !desc.containsn(search_term)) {336continue;337}338339TreeItem *item = input_list_tree->create_item(joyb_root);340item->set_text(0, desc);341item->set_meta("__index", i);342}343}344345if (allowed_input_types & INPUT_JOY_MOTION) {346TreeItem *joya_root = input_list_tree->create_item(root);347joya_root->set_text(0, TTR("Joypad Axes"));348joya_root->set_icon(0, icon_cache.joypad_axis);349joya_root->set_collapsed(collapse);350joya_root->set_meta("__type", INPUT_JOY_MOTION);351352for (int i = 0; i < (int)JoyAxis::MAX * 2; i++) {353int axis = i / 2;354int direction = (i & 1) ? 1 : -1;355Ref<InputEventJoypadMotion> joym;356joym.instantiate();357joym->set_axis((JoyAxis)axis);358joym->set_axis_value(direction);359String desc = EventListenerLineEdit::get_event_text(joym, false);360361if (!search_term.is_empty() && !desc.containsn(search_term)) {362continue;363}364365TreeItem *item = input_list_tree->create_item(joya_root);366item->set_text(0, desc);367item->set_meta("__axis", i >> 1);368item->set_meta("__value", (i & 1) ? 1 : -1);369}370}371}372373void InputEventConfigurationDialog::_mod_toggled(bool p_checked, int p_index) {374Ref<InputEventWithModifiers> ie = event;375376// Not event with modifiers377if (ie.is_null()) {378return;379}380381if (p_index == 0) {382ie->set_alt_pressed(p_checked);383} else if (p_index == 1) {384ie->set_shift_pressed(p_checked);385} else if (p_index == 2) {386if (!autoremap_command_or_control_checkbox->is_pressed()) {387ie->set_ctrl_pressed(p_checked);388}389} else if (p_index == 3) {390if (!autoremap_command_or_control_checkbox->is_pressed()) {391ie->set_meta_pressed(p_checked);392}393}394395_set_event(ie, original_event);396}397398void InputEventConfigurationDialog::_autoremap_command_or_control_toggled(bool p_checked) {399Ref<InputEventWithModifiers> ie = event;400if (ie.is_valid()) {401ie->set_command_or_control_autoremap(p_checked);402_set_event(ie, original_event);403}404405if (p_checked) {406mod_checkboxes[MOD_META]->hide();407mod_checkboxes[MOD_CTRL]->hide();408} else {409mod_checkboxes[MOD_META]->show();410mod_checkboxes[MOD_CTRL]->show();411}412}413414void InputEventConfigurationDialog::_key_mode_selected(int p_mode) {415Ref<InputEventKey> k = event;416Ref<InputEventKey> ko = original_event;417if (k.is_null() || ko.is_null()) {418return;419}420421if (key_mode->get_selected_id() == KEYMODE_KEYCODE) {422k->set_keycode(ko->get_keycode());423k->set_physical_keycode(Key::NONE);424k->set_key_label(Key::NONE);425} else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) {426k->set_keycode(Key::NONE);427k->set_physical_keycode(ko->get_physical_keycode());428k->set_key_label(Key::NONE);429} else if (key_mode->get_selected_id() == KEYMODE_UNICODE) {430k->set_physical_keycode(Key::NONE);431k->set_keycode(Key::NONE);432k->set_key_label(ko->get_key_label());433}434435_set_event(k, original_event);436}437438void InputEventConfigurationDialog::_key_location_selected(int p_location) {439Ref<InputEventKey> k = event;440if (k.is_null()) {441return;442}443444k->set_location((KeyLocation)p_location);445446_set_event(k, original_event);447}448449void InputEventConfigurationDialog::_input_list_item_activated() {450TreeItem *selected = input_list_tree->get_selected();451selected->set_collapsed(!selected->is_collapsed());452}453454void InputEventConfigurationDialog::_input_list_item_selected() {455TreeItem *selected = input_list_tree->get_selected();456457// Called form _set_event, do not update for a second time.458if (in_tree_update) {459return;460}461462// Invalid tree selection - type only exists on the "category" items, which are not a valid selection.463if (selected->has_meta("__type")) {464return;465}466467InputType input_type = (InputType)(int)selected->get_parent()->get_meta("__type");468469switch (input_type) {470case INPUT_KEY: {471Key keycode = (Key)(int)selected->get_meta("__keycode");472Ref<InputEventKey> k;473k.instantiate();474475k->set_physical_keycode(keycode);476k->set_keycode(keycode);477k->set_key_label(keycode);478479// Maintain modifier state from checkboxes.480k->set_alt_pressed(mod_checkboxes[MOD_ALT]->is_pressed());481k->set_shift_pressed(mod_checkboxes[MOD_SHIFT]->is_pressed());482if (autoremap_command_or_control_checkbox->is_pressed()) {483k->set_command_or_control_autoremap(true);484} else {485k->set_ctrl_pressed(mod_checkboxes[MOD_CTRL]->is_pressed());486k->set_meta_pressed(mod_checkboxes[MOD_META]->is_pressed());487}488489Ref<InputEventKey> ko = k->duplicate();490491if (key_mode->get_selected_id() == KEYMODE_UNICODE) {492key_mode->select(KEYMODE_PHY_KEYCODE);493}494495if (key_mode->get_selected_id() == KEYMODE_KEYCODE) {496k->set_physical_keycode(Key::NONE);497k->set_keycode(keycode);498k->set_key_label(Key::NONE);499} else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) {500k->set_physical_keycode(keycode);501k->set_keycode(Key::NONE);502k->set_key_label(Key::NONE);503}504505_set_event(k, ko, false);506} break;507case INPUT_MOUSE_BUTTON: {508MouseButton idx = (MouseButton)(int)selected->get_meta("__index");509Ref<InputEventMouseButton> mb;510mb.instantiate();511mb->set_button_index(idx);512// Maintain modifier state from checkboxes513mb->set_alt_pressed(mod_checkboxes[MOD_ALT]->is_pressed());514mb->set_shift_pressed(mod_checkboxes[MOD_SHIFT]->is_pressed());515if (autoremap_command_or_control_checkbox->is_pressed()) {516mb->set_command_or_control_autoremap(true);517} else {518mb->set_ctrl_pressed(mod_checkboxes[MOD_CTRL]->is_pressed());519mb->set_meta_pressed(mod_checkboxes[MOD_META]->is_pressed());520}521522// Maintain selected device523mb->set_device(_get_current_device());524525_set_event(mb, mb, false);526} break;527case INPUT_JOY_BUTTON: {528JoyButton idx = (JoyButton)(int)selected->get_meta("__index");529Ref<InputEventJoypadButton> jb = InputEventJoypadButton::create_reference(idx);530531// Maintain selected device532jb->set_device(_get_current_device());533534_set_event(jb, jb, false);535} break;536case INPUT_JOY_MOTION: {537JoyAxis axis = (JoyAxis)(int)selected->get_meta("__axis");538int value = selected->get_meta("__value");539540Ref<InputEventJoypadMotion> jm;541jm.instantiate();542jm->set_axis(axis);543jm->set_axis_value(value);544545// Maintain selected device546jm->set_device(_get_current_device());547548_set_event(jm, jm, false);549} break;550}551}552553void InputEventConfigurationDialog::_device_selection_changed(int p_option_button_index) {554// Subtract 1 as option index 0 corresponds to "All Devices" (value of -1)555// and option index 1 corresponds to device 0, etc...556event->set_device(p_option_button_index - 1);557event_as_text->set_text(EventListenerLineEdit::get_event_text(event, true));558}559560void InputEventConfigurationDialog::_set_current_device(int p_device) {561device_id_option->select(p_device + 1);562}563564int InputEventConfigurationDialog::_get_current_device() const {565return device_id_option->get_selected() - 1;566}567568void InputEventConfigurationDialog::_notification(int p_what) {569switch (p_what) {570case NOTIFICATION_VISIBILITY_CHANGED: {571event_listener->grab_focus();572} break;573574case NOTIFICATION_THEME_CHANGED: {575input_list_search->set_right_icon(get_editor_theme_icon(SNAME("Search")));576577key_mode->set_item_icon(KEYMODE_KEYCODE, get_editor_theme_icon(SNAME("Keyboard")));578key_mode->set_item_icon(KEYMODE_PHY_KEYCODE, get_editor_theme_icon(SNAME("KeyboardPhysical")));579key_mode->set_item_icon(KEYMODE_UNICODE, get_editor_theme_icon(SNAME("KeyboardLabel")));580581icon_cache.keyboard = get_editor_theme_icon(SNAME("Keyboard"));582icon_cache.mouse = get_editor_theme_icon(SNAME("Mouse"));583icon_cache.joypad_button = get_editor_theme_icon(SNAME("JoyButton"));584icon_cache.joypad_axis = get_editor_theme_icon(SNAME("JoyAxis"));585586event_as_text->add_theme_font_override(SceneStringName(font), get_theme_font(SNAME("bold"), EditorStringName(EditorFonts)));587588_update_input_list();589} break;590591case NOTIFICATION_TRANSLATION_CHANGED: {592key_location->set_item_text(key_location->get_item_index((int)KeyLocation::UNSPECIFIED), TTR("Unspecified", "Key Location"));593key_location->set_item_text(key_location->get_item_index((int)KeyLocation::LEFT), TTR("Left", "Key Location"));594key_location->set_item_text(key_location->get_item_index((int)KeyLocation::RIGHT), TTR("Right", "Key Location"));595} break;596}597}598599void InputEventConfigurationDialog::popup_and_configure(const Ref<InputEvent> &p_event, const String &p_current_action_name) {600if (p_event.is_valid()) {601_set_event(p_event->duplicate(), p_event->duplicate());602} else {603// Clear Event604_set_event(Ref<InputEvent>(), Ref<InputEvent>());605606// Clear Checkbox Values607for (int i = 0; i < MOD_MAX; i++) {608mod_checkboxes[i]->set_pressed(false);609}610611// Enable the Physical Key by default to encourage its use.612// Physical Key should be used for most game inputs as it allows keys to work613// on non-QWERTY layouts out of the box.614// This is especially important for WASD movement layouts.615616key_mode->select(KEYMODE_PHY_KEYCODE);617autoremap_command_or_control_checkbox->set_pressed(false);618619// Select "All Devices" by default.620device_id_option->select(0);621// Also "all locations".622key_location->select(0);623}624625if (!p_current_action_name.is_empty()) {626set_title(vformat(TTR("Event Configuration for \"%s\""), p_current_action_name));627} else {628set_title(TTR("Event Configuration"));629}630631popup_centered(Size2(0, 400) * EDSCALE);632}633634Ref<InputEvent> InputEventConfigurationDialog::get_event() const {635return event;636}637638void InputEventConfigurationDialog::set_allowed_input_types(int p_type_masks) {639allowed_input_types = p_type_masks;640event_listener->set_allowed_input_types(p_type_masks);641}642643InputEventConfigurationDialog::InputEventConfigurationDialog() {644allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION;645646set_min_size(Size2i(800, 0) * EDSCALE);647648VBoxContainer *main_vbox = memnew(VBoxContainer);649add_child(main_vbox);650651event_as_text = memnew(Label);652event_as_text->set_focus_mode(Control::FOCUS_ACCESSIBILITY);653event_as_text->set_custom_minimum_size(Size2(500, 0) * EDSCALE);654event_as_text->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);655event_as_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);656event_as_text->add_theme_font_size_override(SceneStringName(font_size), 18 * EDSCALE);657main_vbox->add_child(event_as_text);658659event_listener = memnew(EventListenerLineEdit);660event_listener->set_h_size_flags(Control::SIZE_EXPAND_FILL);661event_listener->set_stretch_ratio(0.75);662event_listener->connect("event_changed", callable_mp(this, &InputEventConfigurationDialog::_on_listen_input_changed));663main_vbox->add_child(event_listener);664665main_vbox->add_child(memnew(HSeparator));666667// List of all input options to manually select from.668VBoxContainer *manual_vbox = memnew(VBoxContainer);669manual_vbox->set_name("Manual Selection");670manual_vbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);671main_vbox->add_child(manual_vbox);672673input_list_search = memnew(LineEdit);674input_list_search->set_h_size_flags(Control::SIZE_EXPAND_FILL);675input_list_search->set_placeholder(TTRC("Filter Inputs"));676input_list_search->set_accessibility_name(TTRC("Filter Inputs"));677input_list_search->set_clear_button_enabled(true);678input_list_search->connect(SceneStringName(text_changed), callable_mp(this, &InputEventConfigurationDialog::_search_term_updated));679manual_vbox->add_child(input_list_search);680681input_list_tree = memnew(Tree);682input_list_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);683input_list_tree->connect("item_activated", callable_mp(this, &InputEventConfigurationDialog::_input_list_item_activated));684input_list_tree->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_input_list_item_selected));685input_list_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);686manual_vbox->add_child(input_list_tree);687688input_list_tree->set_hide_root(true);689input_list_tree->set_columns(1);690691_update_input_list();692693// Additional Options694additional_options_container = memnew(VBoxContainer);695additional_options_container->hide();696697Label *opts_label = memnew(Label(TTRC("Additional Options")));698opts_label->set_theme_type_variation("HeaderSmall");699additional_options_container->add_child(opts_label);700701// Device Selection702device_container = memnew(HBoxContainer);703device_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);704705Label *device_label = memnew(Label(TTRC("Device:")));706device_label->set_theme_type_variation("HeaderSmall");707device_container->add_child(device_label);708709device_id_option = memnew(OptionButton);710device_id_option->set_h_size_flags(Control::SIZE_EXPAND_FILL);711for (int i = -1; i < 8; i++) {712device_id_option->add_item(EventListenerLineEdit::get_device_string(i));713}714device_id_option->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_device_selection_changed));715device_id_option->set_accessibility_name(TTRC("Device:"));716_set_current_device(InputMap::ALL_DEVICES);717device_container->add_child(device_id_option);718719device_container->hide();720additional_options_container->add_child(device_container);721722// Modifier Selection723mod_container = memnew(HBoxContainer);724for (int i = 0; i < MOD_MAX; i++) {725String name = mods[i];726mod_checkboxes[i] = memnew(CheckBox(name));727mod_checkboxes[i]->connect(SceneStringName(toggled), callable_mp(this, &InputEventConfigurationDialog::_mod_toggled).bind(i));728mod_checkboxes[i]->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);729mod_checkboxes[i]->set_tooltip_auto_translate_mode(AUTO_TRANSLATE_MODE_ALWAYS);730mod_checkboxes[i]->set_tooltip_text(mods_tip[i]);731mod_container->add_child(mod_checkboxes[i]);732}733734mod_container->add_child(memnew(VSeparator));735736autoremap_command_or_control_checkbox = memnew(CheckBox(TTRC("Command / Control (auto)")));737autoremap_command_or_control_checkbox->connect(SceneStringName(toggled), callable_mp(this, &InputEventConfigurationDialog::_autoremap_command_or_control_toggled));738autoremap_command_or_control_checkbox->set_pressed(false);739autoremap_command_or_control_checkbox->set_tooltip_text(TTRC("Automatically remaps between 'Meta' ('Command') and 'Control' depending on current platform."));740mod_container->add_child(autoremap_command_or_control_checkbox);741742mod_container->hide();743additional_options_container->add_child(mod_container);744745// Key Mode Selection746747key_mode = memnew(OptionButton);748key_mode->add_item(TTRC("Keycode (Latin Equivalent)"), KEYMODE_KEYCODE);749key_mode->add_item(TTRC("Physical Keycode (Position on US QWERTY Keyboard)"), KEYMODE_PHY_KEYCODE);750key_mode->add_item(TTRC("Key Label (Unicode, Case-Insensitive)"), KEYMODE_UNICODE);751key_mode->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_key_mode_selected));752key_mode->hide();753additional_options_container->add_child(key_mode);754755// Key Location Selection756757location_container = memnew(HBoxContainer);758location_container->hide();759760location_container->add_child(memnew(Label(TTRC("Physical location"))));761762key_location = memnew(OptionButton);763key_location->set_h_size_flags(Control::SIZE_EXPAND_FILL);764key_location->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);765// Item texts will be set in `NOTIFICATION_TRANSLATION_CHANGED`.766key_location->add_item(String(), (int)KeyLocation::UNSPECIFIED);767key_location->add_item(String(), (int)KeyLocation::LEFT);768key_location->add_item(String(), (int)KeyLocation::RIGHT);769key_location->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_key_location_selected));770key_location->set_accessibility_name(TTRC("Physical location"));771772location_container->add_child(key_location);773additional_options_container->add_child(location_container);774775main_vbox->add_child(additional_options_container);776}777778779