Path: blob/master/editor/settings/input_event_configuration_dialog.cpp
20942 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/margin_container.h"39#include "scene/gui/option_button.h"40#include "scene/gui/separator.h"41#include "scene/gui/tree.h"4243void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, const Ref<InputEvent> &p_original_event, bool p_update_input_list_selection) {44if (p_event.is_valid()) {45event = p_event;46original_event = p_original_event;4748// If the event is changed to something which is not the same as the listener,49// clear out the event from the listener text box to avoid confusion.50const Ref<InputEvent> listener_event = event_listener->get_event();51if (listener_event.is_valid() && !listener_event->is_match(p_event)) {52event_listener->clear_event();53}5455// Update Label56event_as_text->set_text(EventListenerLineEdit::get_event_text(event, true));5758Ref<InputEventKey> k = p_event;59Ref<InputEventMouseButton> mb = p_event;60Ref<InputEventJoypadButton> joyb = p_event;61Ref<InputEventJoypadMotion> joym = p_event;62Ref<InputEventWithModifiers> mod = p_event;6364// Update option values and visibility65bool show_mods = false;66bool show_device = false;67bool show_key = false;68bool show_location = false;6970if (mod.is_valid()) {71show_mods = true;72mod_checkboxes[MOD_ALT]->set_pressed(mod->is_alt_pressed());73mod_checkboxes[MOD_SHIFT]->set_pressed(mod->is_shift_pressed());74mod_checkboxes[MOD_CTRL]->set_pressed(mod->is_ctrl_pressed());75mod_checkboxes[MOD_META]->set_pressed(mod->is_meta_pressed());7677autoremap_command_or_control_checkbox->set_pressed(mod->is_command_or_control_autoremap());78}7980if (k.is_valid()) {81show_key = true;82Key phys_key = k->get_physical_keycode();83if (k->get_keycode() == Key::NONE && phys_key == Key::NONE && k->get_key_label() != Key::NONE) {84key_mode->select(KEYMODE_UNICODE);85} else if (k->get_keycode() != Key::NONE) {86key_mode->select(KEYMODE_KEYCODE);87} else if (phys_key != Key::NONE) {88key_mode->select(KEYMODE_PHY_KEYCODE);89if (phys_key == Key::SHIFT || phys_key == Key::CTRL || phys_key == Key::ALT || phys_key == Key::META) {90key_location->select((int)k->get_location());91show_location = true;92}93} else {94// Invalid key.95event = Ref<InputEvent>();96original_event = Ref<InputEvent>();97event_listener->clear_event();98event_as_text->set_text(TTRC("No Event Configured"));99100additional_options_container->hide();101input_list_tree->deselect_all();102_update_input_list();103return;104}105} else if (joyb.is_valid() || joym.is_valid() || mb.is_valid()) {106show_device = true;107_set_current_device(event->get_device());108}109110mod_container->set_visible(show_mods);111device_container->set_visible(show_device);112key_mode->set_visible(show_key);113location_container->set_visible(show_location);114additional_options_container->show();115116// Update mode selector based on original key event.117Ref<InputEventKey> ko = p_original_event;118if (ko.is_valid()) {119if (ko->get_keycode() == Key::NONE) {120if (ko->get_physical_keycode() != Key::NONE) {121ko->set_keycode(ko->get_physical_keycode());122}123if (ko->get_key_label() != Key::NONE) {124ko->set_keycode(fix_keycode((char32_t)ko->get_key_label(), Key::NONE));125}126}127128if (ko->get_physical_keycode() == Key::NONE) {129if (ko->get_keycode() != Key::NONE) {130ko->set_physical_keycode(ko->get_keycode());131}132if (ko->get_key_label() != Key::NONE) {133ko->set_physical_keycode(fix_keycode((char32_t)ko->get_key_label(), Key::NONE));134}135}136137if (ko->get_key_label() == Key::NONE) {138if (ko->get_keycode() != Key::NONE) {139ko->set_key_label(fix_key_label((char32_t)ko->get_keycode(), Key::NONE));140}141if (ko->get_physical_keycode() != Key::NONE) {142ko->set_key_label(fix_key_label((char32_t)ko->get_physical_keycode(), Key::NONE));143}144}145146key_mode->set_item_disabled(KEYMODE_KEYCODE, ko->get_keycode() == Key::NONE);147key_mode->set_item_disabled(KEYMODE_PHY_KEYCODE, ko->get_physical_keycode() == Key::NONE);148key_mode->set_item_disabled(KEYMODE_UNICODE, ko->get_key_label() == Key::NONE);149}150151// Update selected item in input list.152if (p_update_input_list_selection && (k.is_valid() || joyb.is_valid() || joym.is_valid() || mb.is_valid())) {153in_tree_update = true;154TreeItem *category = input_list_tree->get_root()->get_first_child();155while (category) {156TreeItem *input_item = category->get_first_child();157158if (input_item != nullptr) {159// input_type should always be > 0, unless the tree structure has been misconfigured.160int input_type = input_item->get_parent()->get_meta("__type", 0);161if (input_type == 0) {162in_tree_update = false;163return;164}165166// If event type matches input types of this category.167if ((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)) {168// Loop through all items of this category until one matches.169while (input_item) {170bool key_match = k.is_valid() && (Variant(k->get_keycode()) == input_item->get_meta("__keycode") || Variant(k->get_physical_keycode()) == input_item->get_meta("__keycode"));171bool joyb_match = joyb.is_valid() && Variant(joyb->get_button_index()) == input_item->get_meta("__index");172bool joym_match = joym.is_valid() && Variant(joym->get_axis()) == input_item->get_meta("__axis") && joym->get_axis_value() == (float)input_item->get_meta("__value");173bool mb_match = mb.is_valid() && Variant(mb->get_button_index()) == input_item->get_meta("__index");174if (key_match || joyb_match || joym_match || mb_match) {175category->set_collapsed(false);176input_item->select(0);177input_list_tree->ensure_cursor_is_visible();178in_tree_update = false;179return;180}181input_item = input_item->get_next();182}183}184}185186category->set_collapsed(true); // Event not in this category, so collapse;187category = category->get_next();188}189in_tree_update = false;190}191} else {192// Event is not valid, reset dialog193event = Ref<InputEvent>();194original_event = Ref<InputEvent>();195event_listener->clear_event();196event_as_text->set_text(TTRC("No Event Configured"));197198additional_options_container->hide();199input_list_tree->deselect_all();200_update_input_list();201}202}203204void InputEventConfigurationDialog::_on_listen_input_changed(const Ref<InputEvent> &p_event) {205// Ignore if invalid, echo or not pressed206if (p_event.is_null() || p_event->is_echo() || !p_event->is_pressed()) {207return;208}209210// Create an editable reference and a copy of full event.211Ref<InputEvent> received_event = p_event;212Ref<InputEvent> received_original_event = received_event->duplicate();213214// Check what the type is and if it is allowed.215Ref<InputEventKey> k = received_event;216Ref<InputEventJoypadButton> joyb = received_event;217Ref<InputEventJoypadMotion> joym = received_event;218Ref<InputEventMouseButton> mb = received_event;219220int type = 0;221if (k.is_valid()) {222type = INPUT_KEY;223} else if (joyb.is_valid()) {224type = INPUT_JOY_BUTTON;225} else if (joym.is_valid()) {226type = INPUT_JOY_MOTION;227} else if (mb.is_valid()) {228type = INPUT_MOUSE_BUTTON;229}230231if (!(allowed_input_types & type)) {232return;233}234235if (joym.is_valid()) {236joym->set_axis_value(SIGN(joym->get_axis_value()));237}238239if (k.is_valid()) {240k->set_pressed(false); // To avoid serialization of 'pressed' property - doesn't matter for actions anyway.241if (key_mode->get_selected_id() == KEYMODE_KEYCODE) {242k->set_physical_keycode(Key::NONE);243k->set_key_label(Key::NONE);244} else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) {245k->set_keycode(Key::NONE);246k->set_key_label(Key::NONE);247} else if (key_mode->get_selected_id() == KEYMODE_UNICODE) {248k->set_physical_keycode(Key::NONE);249k->set_keycode(Key::NONE);250}251if (key_location->get_selected_id() == (int)KeyLocation::UNSPECIFIED) {252k->set_location(KeyLocation::UNSPECIFIED);253}254}255256Ref<InputEventWithModifiers> mod = received_event;257if (mod.is_valid()) {258mod->set_window_id(0);259}260261// Maintain device selection.262received_event->set_device(_get_current_device());263264_set_event(received_event, received_original_event);265}266267void InputEventConfigurationDialog::_search_term_updated(const String &) {268_update_input_list();269}270271void InputEventConfigurationDialog::_update_input_list() {272input_list_tree->clear();273274TreeItem *root = input_list_tree->create_item();275String search_term = input_list_search->get_text();276277bool collapse = input_list_search->get_text().is_empty();278279if (allowed_input_types & INPUT_KEY) {280TreeItem *kb_root = input_list_tree->create_item(root);281kb_root->set_text(0, TTR("Keyboard Keys"));282kb_root->set_icon(0, icon_cache.keyboard);283kb_root->set_collapsed(collapse);284kb_root->set_meta("__type", INPUT_KEY);285286for (int i = 0; i < keycode_get_count(); i++) {287String name = keycode_get_name_by_index(i);288289if (!search_term.is_empty() && !name.containsn(search_term)) {290continue;291}292293TreeItem *item = input_list_tree->create_item(kb_root);294item->set_text(0, name);295item->set_meta("__keycode", keycode_get_value_by_index(i));296}297}298299if (allowed_input_types & INPUT_MOUSE_BUTTON) {300TreeItem *mouse_root = input_list_tree->create_item(root);301mouse_root->set_text(0, TTR("Mouse Buttons"));302mouse_root->set_icon(0, icon_cache.mouse);303mouse_root->set_collapsed(collapse);304mouse_root->set_meta("__type", INPUT_MOUSE_BUTTON);305306MouseButton 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 };307for (int i = 0; i < 9; i++) {308Ref<InputEventMouseButton> mb;309mb.instantiate();310mb->set_button_index(mouse_buttons[i]);311String desc = EventListenerLineEdit::get_event_text(mb, false);312313if (!search_term.is_empty() && !desc.containsn(search_term)) {314continue;315}316317TreeItem *item = input_list_tree->create_item(mouse_root);318item->set_text(0, desc);319item->set_meta("__index", mouse_buttons[i]);320}321}322323if (allowed_input_types & INPUT_JOY_BUTTON) {324TreeItem *joyb_root = input_list_tree->create_item(root);325joyb_root->set_text(0, TTR("Joypad Buttons"));326joyb_root->set_icon(0, icon_cache.joypad_button);327joyb_root->set_collapsed(collapse);328joyb_root->set_meta("__type", INPUT_JOY_BUTTON);329330for (int i = 0; i < (int)JoyButton::MAX; i++) {331Ref<InputEventJoypadButton> joyb;332joyb.instantiate();333joyb->set_button_index((JoyButton)i);334String desc = EventListenerLineEdit::get_event_text(joyb, false);335336if (!search_term.is_empty() && !desc.containsn(search_term)) {337continue;338}339340TreeItem *item = input_list_tree->create_item(joyb_root);341item->set_text(0, desc);342item->set_meta("__index", i);343}344}345346if (allowed_input_types & INPUT_JOY_MOTION) {347TreeItem *joya_root = input_list_tree->create_item(root);348joya_root->set_text(0, TTR("Joypad Axes"));349joya_root->set_icon(0, icon_cache.joypad_axis);350joya_root->set_collapsed(collapse);351joya_root->set_meta("__type", INPUT_JOY_MOTION);352353for (int i = 0; i < (int)JoyAxis::MAX * 2; i++) {354int axis = i / 2;355int direction = (i & 1) ? 1 : -1;356Ref<InputEventJoypadMotion> joym;357joym.instantiate();358joym->set_axis((JoyAxis)axis);359joym->set_axis_value(direction);360String desc = EventListenerLineEdit::get_event_text(joym, false);361362if (!search_term.is_empty() && !desc.containsn(search_term)) {363continue;364}365366TreeItem *item = input_list_tree->create_item(joya_root);367item->set_text(0, desc);368item->set_meta("__axis", i >> 1);369item->set_meta("__value", (i & 1) ? 1 : -1);370}371}372}373374void InputEventConfigurationDialog::_mod_toggled(bool p_checked, int p_index) {375Ref<InputEventWithModifiers> ie = event;376377// Not event with modifiers378if (ie.is_null()) {379return;380}381382if (p_index == 0) {383ie->set_alt_pressed(p_checked);384} else if (p_index == 1) {385ie->set_shift_pressed(p_checked);386} else if (p_index == 2) {387if (!autoremap_command_or_control_checkbox->is_pressed()) {388ie->set_ctrl_pressed(p_checked);389}390} else if (p_index == 3) {391if (!autoremap_command_or_control_checkbox->is_pressed()) {392ie->set_meta_pressed(p_checked);393}394}395396_set_event(ie, original_event);397}398399void InputEventConfigurationDialog::_autoremap_command_or_control_toggled(bool p_checked) {400Ref<InputEventWithModifiers> ie = event;401if (ie.is_valid()) {402ie->set_command_or_control_autoremap(p_checked);403_set_event(ie, original_event);404}405406if (p_checked) {407mod_checkboxes[MOD_META]->hide();408mod_checkboxes[MOD_CTRL]->hide();409} else {410mod_checkboxes[MOD_META]->show();411mod_checkboxes[MOD_CTRL]->show();412}413}414415void InputEventConfigurationDialog::_key_mode_selected(int p_mode) {416Ref<InputEventKey> k = event;417Ref<InputEventKey> ko = original_event;418if (k.is_null() || ko.is_null()) {419return;420}421422if (key_mode->get_selected_id() == KEYMODE_KEYCODE) {423k->set_keycode(ko->get_keycode());424k->set_physical_keycode(Key::NONE);425k->set_key_label(Key::NONE);426} else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) {427k->set_keycode(Key::NONE);428k->set_physical_keycode(ko->get_physical_keycode());429k->set_key_label(Key::NONE);430} else if (key_mode->get_selected_id() == KEYMODE_UNICODE) {431k->set_physical_keycode(Key::NONE);432k->set_keycode(Key::NONE);433k->set_key_label(ko->get_key_label());434}435436_set_event(k, original_event);437}438439void InputEventConfigurationDialog::_key_location_selected(int p_location) {440Ref<InputEventKey> k = event;441if (k.is_null()) {442return;443}444445k->set_location((KeyLocation)p_location);446447_set_event(k, original_event);448}449450void InputEventConfigurationDialog::_input_list_item_activated() {451TreeItem *selected = input_list_tree->get_selected();452selected->set_collapsed(!selected->is_collapsed());453}454455void InputEventConfigurationDialog::_input_list_item_selected() {456TreeItem *selected = input_list_tree->get_selected();457458// Called form _set_event, do not update for a second time.459if (in_tree_update) {460return;461}462463// Invalid tree selection - type only exists on the "category" items, which are not a valid selection.464if (selected->has_meta("__type")) {465return;466}467468InputType input_type = (InputType)(int)selected->get_parent()->get_meta("__type");469470switch (input_type) {471case INPUT_KEY: {472Key keycode = (Key)(int)selected->get_meta("__keycode");473Ref<InputEventKey> k;474k.instantiate();475476k->set_physical_keycode(keycode);477k->set_keycode(keycode);478k->set_key_label(keycode);479480// Maintain modifier state from checkboxes.481k->set_alt_pressed(mod_checkboxes[MOD_ALT]->is_pressed());482k->set_shift_pressed(mod_checkboxes[MOD_SHIFT]->is_pressed());483if (autoremap_command_or_control_checkbox->is_pressed()) {484k->set_command_or_control_autoremap(true);485} else {486k->set_ctrl_pressed(mod_checkboxes[MOD_CTRL]->is_pressed());487k->set_meta_pressed(mod_checkboxes[MOD_META]->is_pressed());488}489490Ref<InputEventKey> ko = k->duplicate();491492if (key_mode->get_selected_id() == KEYMODE_UNICODE) {493key_mode->select(KEYMODE_PHY_KEYCODE);494}495496if (key_mode->get_selected_id() == KEYMODE_KEYCODE) {497k->set_physical_keycode(Key::NONE);498k->set_keycode(keycode);499k->set_key_label(Key::NONE);500} else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) {501k->set_physical_keycode(keycode);502k->set_keycode(Key::NONE);503k->set_key_label(Key::NONE);504}505506_set_event(k, ko, false);507} break;508case INPUT_MOUSE_BUTTON: {509MouseButton idx = (MouseButton)(int)selected->get_meta("__index");510Ref<InputEventMouseButton> mb;511mb.instantiate();512mb->set_button_index(idx);513// Maintain modifier state from checkboxes514mb->set_alt_pressed(mod_checkboxes[MOD_ALT]->is_pressed());515mb->set_shift_pressed(mod_checkboxes[MOD_SHIFT]->is_pressed());516if (autoremap_command_or_control_checkbox->is_pressed()) {517mb->set_command_or_control_autoremap(true);518} else {519mb->set_ctrl_pressed(mod_checkboxes[MOD_CTRL]->is_pressed());520mb->set_meta_pressed(mod_checkboxes[MOD_META]->is_pressed());521}522523// Maintain selected device524mb->set_device(_get_current_device());525526_set_event(mb, mb, false);527} break;528case INPUT_JOY_BUTTON: {529JoyButton idx = (JoyButton)(int)selected->get_meta("__index");530// Maintain selected device531Ref<InputEventJoypadButton> jb = InputEventJoypadButton::create_reference(idx, _get_current_device());532533_set_event(jb, jb, false);534} break;535case INPUT_JOY_MOTION: {536JoyAxis axis = (JoyAxis)(int)selected->get_meta("__axis");537int value = selected->get_meta("__value");538539Ref<InputEventJoypadMotion> jm;540jm.instantiate();541jm->set_axis(axis);542jm->set_axis_value(value);543544// Maintain selected device545jm->set_device(_get_current_device());546547_set_event(jm, jm, false);548} break;549}550}551552void InputEventConfigurationDialog::_device_selection_changed(int p_option_button_index) {553// Subtract 1 as option index 0 corresponds to "All Devices" (value of -1)554// and option index 1 corresponds to device 0, etc...555event->set_device(p_option_button_index - 1);556event_as_text->set_text(EventListenerLineEdit::get_event_text(event, true));557}558559void InputEventConfigurationDialog::_set_current_device(int p_device) {560device_id_option->select(p_device + 1);561}562563int InputEventConfigurationDialog::_get_current_device() const {564return device_id_option->get_selected() - 1;565}566567void InputEventConfigurationDialog::_notification(int p_what) {568switch (p_what) {569case NOTIFICATION_VISIBILITY_CHANGED: {570event_listener->grab_focus();571} break;572573case NOTIFICATION_THEME_CHANGED: {574input_list_search->set_right_icon(get_editor_theme_icon(SNAME("Search")));575576key_mode->set_item_icon(KEYMODE_KEYCODE, get_editor_theme_icon(SNAME("Keyboard")));577key_mode->set_item_icon(KEYMODE_PHY_KEYCODE, get_editor_theme_icon(SNAME("KeyboardPhysical")));578key_mode->set_item_icon(KEYMODE_UNICODE, get_editor_theme_icon(SNAME("KeyboardLabel")));579580icon_cache.keyboard = get_editor_theme_icon(SNAME("Keyboard"));581icon_cache.mouse = get_editor_theme_icon(SNAME("Mouse"));582icon_cache.joypad_button = get_editor_theme_icon(SNAME("JoyButton"));583icon_cache.joypad_axis = get_editor_theme_icon(SNAME("JoyAxis"));584585event_as_text->add_theme_font_override(SceneStringName(font), get_theme_font(SNAME("bold"), EditorStringName(EditorFonts)));586587_update_input_list();588} break;589590case NOTIFICATION_TRANSLATION_CHANGED: {591key_location->set_item_text(key_location->get_item_index((int)KeyLocation::UNSPECIFIED), TTR("Unspecified", "Key Location"));592key_location->set_item_text(key_location->get_item_index((int)KeyLocation::LEFT), TTR("Left", "Key Location"));593key_location->set_item_text(key_location->get_item_index((int)KeyLocation::RIGHT), TTR("Right", "Key Location"));594} break;595}596}597598void InputEventConfigurationDialog::popup_and_configure(const Ref<InputEvent> &p_event, const String &p_current_action_name) {599if (p_event.is_valid()) {600_set_event(p_event->duplicate(), p_event->duplicate());601} else {602// Clear Event603_set_event(Ref<InputEvent>(), Ref<InputEvent>());604605// Clear Checkbox Values606for (int i = 0; i < MOD_MAX; i++) {607mod_checkboxes[i]->set_pressed(false);608}609610// Enable the Physical Key by default to encourage its use.611// Physical Key should be used for most game inputs as it allows keys to work612// on non-QWERTY layouts out of the box.613// This is especially important for WASD movement layouts.614615key_mode->select(KEYMODE_PHY_KEYCODE);616autoremap_command_or_control_checkbox->set_pressed(false);617618// Select "All Devices" by default.619device_id_option->select(0);620// Also "all locations".621key_location->select(0);622}623624if (!p_current_action_name.is_empty()) {625set_title(vformat(TTR("Event Configuration for \"%s\""), p_current_action_name));626} else {627set_title(TTR("Event Configuration"));628}629630popup_centered(Size2(0, 400) * EDSCALE);631}632633Ref<InputEvent> InputEventConfigurationDialog::get_event() const {634return event;635}636637void InputEventConfigurationDialog::set_allowed_input_types(int p_type_masks) {638allowed_input_types = p_type_masks;639event_listener->set_allowed_input_types(p_type_masks);640}641642InputEventConfigurationDialog::InputEventConfigurationDialog() {643allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION;644645set_min_size(Size2i(800, 0) * EDSCALE);646647VBoxContainer *main_vbox = memnew(VBoxContainer);648add_child(main_vbox);649650event_as_text = memnew(Label);651event_as_text->set_focus_mode(Control::FOCUS_ACCESSIBILITY);652event_as_text->set_custom_minimum_size(Size2(500, 0) * EDSCALE);653event_as_text->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);654event_as_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);655event_as_text->add_theme_font_size_override(SceneStringName(font_size), 18 * EDSCALE);656main_vbox->add_child(event_as_text);657658event_listener = memnew(EventListenerLineEdit);659event_listener->set_h_size_flags(Control::SIZE_EXPAND_FILL);660event_listener->set_stretch_ratio(0.75);661event_listener->connect("event_changed", callable_mp(this, &InputEventConfigurationDialog::_on_listen_input_changed));662main_vbox->add_child(event_listener);663664main_vbox->add_child(memnew(HSeparator));665666// List of all input options to manually select from.667VBoxContainer *manual_vbox = memnew(VBoxContainer);668manual_vbox->set_name("Manual Selection");669manual_vbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);670main_vbox->add_child(manual_vbox);671672input_list_search = memnew(LineEdit);673input_list_search->set_h_size_flags(Control::SIZE_EXPAND_FILL);674input_list_search->set_placeholder(TTRC("Filter Inputs"));675input_list_search->set_accessibility_name(TTRC("Filter Inputs"));676input_list_search->set_clear_button_enabled(true);677input_list_search->connect(SceneStringName(text_changed), callable_mp(this, &InputEventConfigurationDialog::_search_term_updated));678manual_vbox->add_child(input_list_search);679680MarginContainer *mc = memnew(MarginContainer);681mc->set_v_size_flags(Control::SIZE_EXPAND_FILL);682mc->set_theme_type_variation("NoBorderHorizontalWindow");683manual_vbox->add_child(mc);684685input_list_tree = memnew(Tree);686input_list_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);687input_list_tree->set_scroll_hint_mode(Tree::SCROLL_HINT_MODE_BOTH);688input_list_tree->connect("item_activated", callable_mp(this, &InputEventConfigurationDialog::_input_list_item_activated));689input_list_tree->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_input_list_item_selected));690mc->add_child(input_list_tree);691692input_list_tree->set_hide_root(true);693input_list_tree->set_columns(1);694695_update_input_list();696697// Additional Options698additional_options_container = memnew(VBoxContainer);699additional_options_container->hide();700701Label *opts_label = memnew(Label(TTRC("Additional Options")));702opts_label->set_theme_type_variation("HeaderSmall");703additional_options_container->add_child(opts_label);704705// Device Selection706device_container = memnew(HBoxContainer);707device_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);708709Label *device_label = memnew(Label(TTRC("Device:")));710device_label->set_theme_type_variation("HeaderSmall");711device_container->add_child(device_label);712713device_id_option = memnew(OptionButton);714device_id_option->set_h_size_flags(Control::SIZE_EXPAND_FILL);715for (int i = -1; i < 8; i++) {716device_id_option->add_item(EventListenerLineEdit::get_device_string(i));717}718device_id_option->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_device_selection_changed));719device_id_option->set_accessibility_name(TTRC("Device:"));720_set_current_device(InputMap::ALL_DEVICES);721device_container->add_child(device_id_option);722723device_container->hide();724additional_options_container->add_child(device_container);725726// Modifier Selection727mod_container = memnew(HBoxContainer);728for (int i = 0; i < MOD_MAX; i++) {729String name = mods[i];730mod_checkboxes[i] = memnew(CheckBox(name));731mod_checkboxes[i]->connect(SceneStringName(toggled), callable_mp(this, &InputEventConfigurationDialog::_mod_toggled).bind(i));732mod_checkboxes[i]->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);733mod_checkboxes[i]->set_tooltip_auto_translate_mode(AUTO_TRANSLATE_MODE_ALWAYS);734mod_checkboxes[i]->set_tooltip_text(mods_tip[i]);735mod_container->add_child(mod_checkboxes[i]);736}737738mod_container->add_child(memnew(VSeparator));739740autoremap_command_or_control_checkbox = memnew(CheckBox(TTRC("Command / Control (auto)")));741autoremap_command_or_control_checkbox->connect(SceneStringName(toggled), callable_mp(this, &InputEventConfigurationDialog::_autoremap_command_or_control_toggled));742autoremap_command_or_control_checkbox->set_pressed(false);743autoremap_command_or_control_checkbox->set_tooltip_text(TTRC("Automatically remaps between 'Meta' ('Command') and 'Control' depending on current platform."));744mod_container->add_child(autoremap_command_or_control_checkbox);745746mod_container->hide();747additional_options_container->add_child(mod_container);748749// Key Mode Selection750751key_mode = memnew(OptionButton);752key_mode->add_item(TTRC("Keycode (Latin Equivalent)"), KEYMODE_KEYCODE);753key_mode->add_item(TTRC("Physical Keycode (Position on US QWERTY Keyboard)"), KEYMODE_PHY_KEYCODE);754key_mode->add_item(TTRC("Key Label (Unicode, Case-Insensitive)"), KEYMODE_UNICODE);755key_mode->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_key_mode_selected));756key_mode->hide();757additional_options_container->add_child(key_mode);758759// Key Location Selection760761location_container = memnew(HBoxContainer);762location_container->hide();763764location_container->add_child(memnew(Label(TTRC("Physical location"))));765766key_location = memnew(OptionButton);767key_location->set_h_size_flags(Control::SIZE_EXPAND_FILL);768key_location->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);769// Item texts will be set in `NOTIFICATION_TRANSLATION_CHANGED`.770key_location->add_item(String(), (int)KeyLocation::UNSPECIFIED);771key_location->add_item(String(), (int)KeyLocation::LEFT);772key_location->add_item(String(), (int)KeyLocation::RIGHT);773key_location->connect(SceneStringName(item_selected), callable_mp(this, &InputEventConfigurationDialog::_key_location_selected));774key_location->set_accessibility_name(TTRC("Physical location"));775776location_container->add_child(key_location);777additional_options_container->add_child(location_container);778779main_vbox->add_child(additional_options_container);780}781782783