2020-10-01 15:04:57 +02:00
/*************************************************************************/
/* action_map_editor.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2020-10-01 15:04:57 +02:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
# include "action_map_editor.h"
2022-02-14 14:00:03 +01:00
2020-10-01 15:04:57 +02:00
# include "core/input/input_map.h"
# include "core/os/keyboard.h"
# include "editor/editor_scale.h"
2022-02-12 02:46:22 +01:00
# include "scene/gui/separator.h"
2020-10-01 15:04:57 +02:00
/////////////////////////////////////////
2021-08-13 23:31:57 +02:00
// Maps to 2*axis if value is neg, or 2*axis+1 if value is pos.
static const char * _joy_axis_descriptions [ ( size_t ) JoyAxis : : MAX * 2 ] = {
2020-10-01 15:04:57 +02:00
TTRC ( " Left Stick Left, Joystick 0 Left " ) ,
TTRC ( " Left Stick Right, Joystick 0 Right " ) ,
TTRC ( " Left Stick Up, Joystick 0 Up " ) ,
TTRC ( " Left Stick Down, Joystick 0 Down " ) ,
TTRC ( " Right Stick Left, Joystick 1 Left " ) ,
TTRC ( " Right Stick Right, Joystick 1 Right " ) ,
TTRC ( " Right Stick Up, Joystick 1 Up " ) ,
TTRC ( " Right Stick Down, Joystick 1 Down " ) ,
TTRC ( " Joystick 2 Left " ) ,
TTRC ( " Left Trigger, Sony L2, Xbox LT, Joystick 2 Right " ) ,
TTRC ( " Joystick 2 Up " ) ,
TTRC ( " Right Trigger, Sony R2, Xbox RT, Joystick 2 Down " ) ,
TTRC ( " Joystick 3 Left " ) ,
TTRC ( " Joystick 3 Right " ) ,
TTRC ( " Joystick 3 Up " ) ,
TTRC ( " Joystick 3 Down " ) ,
TTRC ( " Joystick 4 Left " ) ,
TTRC ( " Joystick 4 Right " ) ,
TTRC ( " Joystick 4 Up " ) ,
TTRC ( " Joystick 4 Down " ) ,
} ;
2022-03-25 14:56:25 +01:00
String InputEventConfigurationDialog : : get_event_text ( const Ref < InputEvent > & p_event , bool p_include_device ) const {
2020-10-01 15:04:57 +02:00
ERR_FAIL_COND_V_MSG ( p_event . is_null ( ) , String ( ) , " Provided event is not a valid instance of InputEvent " ) ;
2022-03-25 14:56:25 +01:00
String text = p_event - > as_text ( ) ;
2022-09-02 11:37:48 +02:00
Ref < InputEventKey > key = p_event ;
if ( key . is_valid ( ) & & key - > is_command_or_control_autoremap ( ) ) {
# ifdef MACOS_ENABLED
text = text . replace ( " Command " , " Command/Ctrl " ) ;
# else
text = text . replace ( " Ctrl " , " Command/Ctrl " ) ;
# endif
}
2022-03-25 14:56:25 +01:00
Ref < InputEventMouse > mouse = p_event ;
Ref < InputEventJoypadMotion > jp_motion = p_event ;
Ref < InputEventJoypadButton > jp_button = p_event ;
if ( jp_motion . is_valid ( ) ) {
// Joypad motion events will display slightly differently than what the event->as_text() provides. See #43660.
2020-10-01 15:04:57 +02:00
String desc = TTR ( " Unknown Joypad Axis " ) ;
2022-03-25 14:56:25 +01:00
if ( jp_motion - > get_axis ( ) < JoyAxis : : MAX ) {
desc = RTR ( _joy_axis_descriptions [ 2 * ( size_t ) jp_motion - > get_axis ( ) + ( jp_motion - > get_axis_value ( ) < 0 ? 0 : 1 ) ] ) ;
2020-10-01 15:04:57 +02:00
}
2022-03-25 14:56:25 +01:00
text = vformat ( " Joypad Axis %s %s (%s) " , itos ( ( int64_t ) jp_motion - > get_axis ( ) ) , jp_motion - > get_axis_value ( ) < 0 ? " - " : " + " , desc ) ;
}
if ( p_include_device & & ( mouse . is_valid ( ) | | jp_button . is_valid ( ) | | jp_motion . is_valid ( ) ) ) {
String device_string = _get_device_string ( p_event - > get_device ( ) ) ;
text + = vformat ( " - %s " , device_string ) ;
2020-10-01 15:04:57 +02:00
}
2022-03-25 14:56:25 +01:00
return text ;
2020-10-01 15:04:57 +02:00
}
2022-03-25 14:56:25 +01:00
void InputEventConfigurationDialog : : _set_event ( const Ref < InputEvent > & p_event , bool p_update_input_list_selection ) {
2020-10-01 15:04:57 +02:00
if ( p_event . is_valid ( ) ) {
event = p_event ;
// Update Label
2022-03-25 14:56:25 +01:00
event_as_text - > set_text ( get_event_text ( event , true ) ) ;
2020-10-01 15:04:57 +02:00
Ref < InputEventKey > k = p_event ;
Ref < InputEventMouseButton > mb = p_event ;
Ref < InputEventJoypadButton > joyb = p_event ;
Ref < InputEventJoypadMotion > joym = p_event ;
Ref < InputEventWithModifiers > mod = p_event ;
// Update option values and visibility
bool show_mods = false ;
bool show_device = false ;
bool show_phys_key = false ;
if ( mod . is_valid ( ) ) {
show_mods = true ;
2021-04-24 22:33:50 +02:00
mod_checkboxes [ MOD_ALT ] - > set_pressed ( mod - > is_alt_pressed ( ) ) ;
mod_checkboxes [ MOD_SHIFT ] - > set_pressed ( mod - > is_shift_pressed ( ) ) ;
mod_checkboxes [ MOD_CTRL ] - > set_pressed ( mod - > is_ctrl_pressed ( ) ) ;
mod_checkboxes [ MOD_META ] - > set_pressed ( mod - > is_meta_pressed ( ) ) ;
2020-10-01 15:04:57 +02:00
2022-09-02 11:37:48 +02:00
autoremap_command_or_control_checkbox - > set_pressed ( mod - > is_command_or_control_autoremap ( ) ) ;
2020-10-01 15:04:57 +02:00
}
if ( k . is_valid ( ) ) {
show_phys_key = true ;
2021-08-13 23:31:57 +02:00
physical_key_checkbox - > set_pressed ( k - > get_physical_keycode ( ) ! = Key : : NONE & & k - > get_keycode ( ) = = Key : : NONE ) ;
2020-10-01 15:04:57 +02:00
} else if ( joyb . is_valid ( ) | | joym . is_valid ( ) | | mb . is_valid ( ) ) {
show_device = true ;
_set_current_device ( event - > get_device ( ) ) ;
}
mod_container - > set_visible ( show_mods ) ;
device_container - > set_visible ( show_device ) ;
physical_key_checkbox - > set_visible ( show_phys_key ) ;
additional_options_container - > show ( ) ;
2021-09-20 12:16:38 +02:00
// Update selected item in input list.
2022-03-25 14:56:25 +01:00
if ( p_update_input_list_selection & & ( k . is_valid ( ) | | joyb . is_valid ( ) | | joym . is_valid ( ) | | mb . is_valid ( ) ) ) {
2021-03-07 21:07:30 +01:00
TreeItem * category = input_list_tree - > get_root ( ) - > get_first_child ( ) ;
2020-10-01 15:04:57 +02:00
while ( category ) {
2021-03-07 21:07:30 +01:00
TreeItem * input_item = category - > get_first_child ( ) ;
2020-10-01 15:04:57 +02:00
2021-10-09 15:01:14 +02:00
if ( input_item ! = nullptr ) {
2022-04-01 20:30:23 +02:00
// input_type should always be > 0, unless the tree structure has been misconfigured.
int input_type = input_item - > get_parent ( ) - > get_meta ( " __type " , 0 ) ;
if ( input_type = = 0 ) {
2021-10-09 15:01:14 +02:00
return ;
}
2020-10-01 15:04:57 +02:00
2021-10-09 15:01:14 +02:00
// If event type matches input types of this category.
if ( ( k . is_valid ( ) & & input_type = = INPUT_KEY ) | | ( joyb . is_valid ( ) & & input_type = = INPUT_JOY_BUTTON ) | | ( joym . is_valid ( ) & & input_type = = INPUT_JOY_MOTION ) | | ( mb . is_valid ( ) & & input_type = = INPUT_MOUSE_BUTTON ) ) {
// Loop through all items of this category until one matches.
while ( input_item ) {
bool key_match = k . is_valid ( ) & & ( Variant ( k - > get_keycode ( ) ) = = input_item - > get_meta ( " __keycode " ) | | Variant ( k - > get_physical_keycode ( ) ) = = input_item - > get_meta ( " __keycode " ) ) ;
bool joyb_match = joyb . is_valid ( ) & & Variant ( joyb - > get_button_index ( ) ) = = input_item - > get_meta ( " __index " ) ;
bool joym_match = joym . is_valid ( ) & & Variant ( joym - > get_axis ( ) ) = = input_item - > get_meta ( " __axis " ) & & joym - > get_axis_value ( ) = = ( float ) input_item - > get_meta ( " __value " ) ;
bool mb_match = mb . is_valid ( ) & & Variant ( mb - > get_button_index ( ) ) = = input_item - > get_meta ( " __index " ) ;
if ( key_match | | joyb_match | | joym_match | | mb_match ) {
category - > set_collapsed ( false ) ;
input_item - > select ( 0 ) ;
input_list_tree - > ensure_cursor_is_visible ( ) ;
return ;
}
input_item = input_item - > get_next ( ) ;
2020-10-01 15:04:57 +02:00
}
}
}
category - > set_collapsed ( true ) ; // Event not in this category, so collapse;
category = category - > get_next ( ) ;
}
}
} else {
// Event is not valid, reset dialog
event = p_event ;
Vector < String > strings ;
// Reset message, promp for input according to which input types are allowed.
String text = TTR ( " Perform an Input (%s). " ) ;
if ( allowed_input_types & INPUT_KEY ) {
strings . append ( TTR ( " Key " ) ) ;
}
if ( allowed_input_types & INPUT_JOY_BUTTON ) {
strings . append ( TTR ( " Joypad Button " ) ) ;
}
if ( allowed_input_types & INPUT_JOY_MOTION ) {
strings . append ( TTR ( " Joypad Axis " ) ) ;
}
2021-09-20 12:16:38 +02:00
if ( allowed_input_types & INPUT_MOUSE_BUTTON ) {
strings . append ( TTR ( " Mouse Button in area below " ) ) ;
}
2020-10-01 15:04:57 +02:00
if ( strings . size ( ) = = 0 ) {
text = TTR ( " Input Event dialog has been misconfigured: No input types are allowed. " ) ;
event_as_text - > set_text ( text ) ;
} else {
String insert_text = String ( " , " ) . join ( strings ) ;
event_as_text - > set_text ( vformat ( text , insert_text ) ) ;
}
additional_options_container - > hide ( ) ;
input_list_tree - > deselect_all ( ) ;
_update_input_list ( ) ;
}
}
void InputEventConfigurationDialog : : _tab_selected ( int p_tab ) {
Callable signal_method = callable_mp ( this , & InputEventConfigurationDialog : : _listen_window_input ) ;
if ( p_tab = = 0 ) {
// Start Listening.
if ( ! is_connected ( " window_input " , signal_method ) ) {
connect ( " window_input " , signal_method ) ;
}
} else {
// Stop Listening.
if ( is_connected ( " window_input " , signal_method ) ) {
disconnect ( " window_input " , signal_method ) ;
}
2021-07-17 23:22:52 +02:00
input_list_tree - > call_deferred ( SNAME ( " ensure_cursor_is_visible " ) ) ;
2020-10-01 15:04:57 +02:00
if ( input_list_tree - > get_selected ( ) = = nullptr ) {
// If nothing selected, scroll to top.
input_list_tree - > scroll_to_item ( input_list_tree - > get_root ( ) ) ;
}
}
}
void InputEventConfigurationDialog : : _listen_window_input ( const Ref < InputEvent > & p_event ) {
// Ignore if echo or not pressed
if ( p_event - > is_echo ( ) | | ! p_event - > is_pressed ( ) ) {
return ;
}
2021-09-20 12:16:38 +02:00
// Ignore mouse motion
Ref < InputEventMouseMotion > mm = p_event ;
if ( mm . is_valid ( ) ) {
2020-10-01 15:04:57 +02:00
return ;
}
2021-09-20 12:16:38 +02:00
// Ignore mouse button if not in the detection rect
Ref < InputEventMouseButton > mb = p_event ;
if ( mb . is_valid ( ) ) {
Rect2 r = mouse_detection_rect - > get_rect ( ) ;
if ( ! r . has_point ( mouse_detection_rect - > get_local_mouse_position ( ) + r . get_position ( ) ) ) {
return ;
}
}
2022-03-25 14:56:25 +01:00
// Create an editable reference
Ref < InputEvent > received_event = p_event ;
2020-10-01 15:04:57 +02:00
// Check what the type is and if it is allowed.
2022-03-25 14:56:25 +01:00
Ref < InputEventKey > k = received_event ;
Ref < InputEventJoypadButton > joyb = received_event ;
Ref < InputEventJoypadMotion > joym = received_event ;
2020-10-01 15:04:57 +02:00
2021-10-28 15:19:35 +02:00
int type = 0 ;
if ( k . is_valid ( ) ) {
type = INPUT_KEY ;
} else if ( joyb . is_valid ( ) ) {
type = INPUT_JOY_BUTTON ;
} else if ( joym . is_valid ( ) ) {
type = INPUT_JOY_MOTION ;
} else if ( mb . is_valid ( ) ) {
type = INPUT_MOUSE_BUTTON ;
}
2020-10-01 15:04:57 +02:00
if ( ! ( allowed_input_types & type ) ) {
return ;
}
if ( joym . is_valid ( ) ) {
float axis_value = joym - > get_axis_value ( ) ;
if ( ABS ( axis_value ) < 0.9 ) {
// Ignore motion below 0.9 magnitude to avoid accidental touches
return ;
} else {
// Always make the value 1 or -1 for display consistency
2021-10-16 01:22:57 +02:00
joym - > set_axis_value ( SIGN ( axis_value ) ) ;
2020-10-01 15:04:57 +02:00
}
}
if ( k . is_valid ( ) ) {
2022-03-25 14:56:25 +01:00
k - > set_pressed ( false ) ; // To avoid serialisation of 'pressed' property - doesn't matter for actions anyway.
2020-10-01 15:04:57 +02:00
// Maintain physical keycode option state
if ( physical_key_checkbox - > is_pressed ( ) ) {
2021-08-13 23:31:57 +02:00
k - > set_keycode ( Key : : NONE ) ;
2020-10-01 15:04:57 +02:00
} else {
2021-08-13 23:31:57 +02:00
k - > set_physical_keycode ( Key : : NONE ) ;
2020-10-01 15:04:57 +02:00
}
}
2022-03-25 14:56:25 +01:00
Ref < InputEventWithModifiers > mod = received_event ;
2020-10-01 15:04:57 +02:00
if ( mod . is_valid ( ) ) {
mod - > set_window_id ( 0 ) ;
}
2022-03-25 14:56:25 +01:00
// Maintain device selection.
received_event - > set_device ( _get_current_device ( ) ) ;
_set_event ( received_event ) ;
2020-10-01 15:04:57 +02:00
set_input_as_handled ( ) ;
}
void InputEventConfigurationDialog : : _search_term_updated ( const String & ) {
_update_input_list ( ) ;
}
void InputEventConfigurationDialog : : _update_input_list ( ) {
input_list_tree - > clear ( ) ;
TreeItem * root = input_list_tree - > create_item ( ) ;
String search_term = input_list_search - > get_text ( ) ;
bool collapse = input_list_search - > get_text ( ) . is_empty ( ) ;
if ( allowed_input_types & INPUT_KEY ) {
TreeItem * kb_root = input_list_tree - > create_item ( root ) ;
kb_root - > set_text ( 0 , TTR ( " Keyboard Keys " ) ) ;
kb_root - > set_icon ( 0 , icon_cache . keyboard ) ;
kb_root - > set_collapsed ( collapse ) ;
kb_root - > set_meta ( " __type " , INPUT_KEY ) ;
for ( int i = 0 ; i < keycode_get_count ( ) ; i + + ) {
String name = keycode_get_name_by_index ( i ) ;
if ( ! search_term . is_empty ( ) & & name . findn ( search_term ) = = - 1 ) {
continue ;
}
TreeItem * item = input_list_tree - > create_item ( kb_root ) ;
item - > set_text ( 0 , name ) ;
item - > set_meta ( " __keycode " , keycode_get_value_by_index ( i ) ) ;
}
}
if ( allowed_input_types & INPUT_MOUSE_BUTTON ) {
TreeItem * mouse_root = input_list_tree - > create_item ( root ) ;
mouse_root - > set_text ( 0 , TTR ( " Mouse Buttons " ) ) ;
mouse_root - > set_icon ( 0 , icon_cache . mouse ) ;
mouse_root - > set_collapsed ( collapse ) ;
mouse_root - > set_meta ( " __type " , INPUT_MOUSE_BUTTON ) ;
2021-08-13 23:31:57 +02:00
MouseButton 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 } ;
2020-10-01 15:04:57 +02:00
for ( int i = 0 ; i < 9 ; i + + ) {
Ref < InputEventMouseButton > mb ;
2021-06-18 00:03:09 +02:00
mb . instantiate ( ) ;
2020-10-01 15:04:57 +02:00
mb - > set_button_index ( mouse_buttons [ i ] ) ;
2022-03-25 14:56:25 +01:00
String desc = get_event_text ( mb , false ) ;
2020-10-01 15:04:57 +02:00
if ( ! search_term . is_empty ( ) & & desc . findn ( search_term ) = = - 1 ) {
continue ;
}
TreeItem * item = input_list_tree - > create_item ( mouse_root ) ;
item - > set_text ( 0 , desc ) ;
item - > set_meta ( " __index " , mouse_buttons [ i ] ) ;
}
}
if ( allowed_input_types & INPUT_JOY_BUTTON ) {
TreeItem * joyb_root = input_list_tree - > create_item ( root ) ;
joyb_root - > set_text ( 0 , TTR ( " Joypad Buttons " ) ) ;
joyb_root - > set_icon ( 0 , icon_cache . joypad_button ) ;
joyb_root - > set_collapsed ( collapse ) ;
joyb_root - > set_meta ( " __type " , INPUT_JOY_BUTTON ) ;
2021-08-13 23:31:57 +02:00
for ( int i = 0 ; i < ( int ) JoyButton : : MAX ; i + + ) {
2020-10-01 15:04:57 +02:00
Ref < InputEventJoypadButton > joyb ;
2021-06-18 00:03:09 +02:00
joyb . instantiate ( ) ;
2021-03-25 21:56:12 +01:00
joyb - > set_button_index ( ( JoyButton ) i ) ;
2022-03-25 14:56:25 +01:00
String desc = get_event_text ( joyb , false ) ;
2020-10-01 15:04:57 +02:00
if ( ! search_term . is_empty ( ) & & desc . findn ( search_term ) = = - 1 ) {
continue ;
}
TreeItem * item = input_list_tree - > create_item ( joyb_root ) ;
item - > set_text ( 0 , desc ) ;
item - > set_meta ( " __index " , i ) ;
}
}
if ( allowed_input_types & INPUT_JOY_MOTION ) {
TreeItem * joya_root = input_list_tree - > create_item ( root ) ;
joya_root - > set_text ( 0 , TTR ( " Joypad Axes " ) ) ;
joya_root - > set_icon ( 0 , icon_cache . joypad_axis ) ;
joya_root - > set_collapsed ( collapse ) ;
joya_root - > set_meta ( " __type " , INPUT_JOY_MOTION ) ;
2021-08-13 23:31:57 +02:00
for ( int i = 0 ; i < ( int ) JoyAxis : : MAX * 2 ; i + + ) {
2020-10-01 15:04:57 +02:00
int axis = i / 2 ;
int direction = ( i & 1 ) ? 1 : - 1 ;
Ref < InputEventJoypadMotion > joym ;
2021-06-18 00:03:09 +02:00
joym . instantiate ( ) ;
2021-03-25 21:56:12 +01:00
joym - > set_axis ( ( JoyAxis ) axis ) ;
2020-10-01 15:04:57 +02:00
joym - > set_axis_value ( direction ) ;
2022-03-25 14:56:25 +01:00
String desc = get_event_text ( joym , false ) ;
2020-10-01 15:04:57 +02:00
if ( ! search_term . is_empty ( ) & & desc . findn ( search_term ) = = - 1 ) {
continue ;
}
TreeItem * item = input_list_tree - > create_item ( joya_root ) ;
item - > set_text ( 0 , desc ) ;
item - > set_meta ( " __axis " , i > > 1 ) ;
item - > set_meta ( " __value " , ( i & 1 ) ? 1 : - 1 ) ;
}
}
}
void InputEventConfigurationDialog : : _mod_toggled ( bool p_checked , int p_index ) {
Ref < InputEventWithModifiers > ie = event ;
// Not event with modifiers
if ( ie . is_null ( ) ) {
return ;
}
if ( p_index = = 0 ) {
2021-04-24 22:33:50 +02:00
ie - > set_alt_pressed ( p_checked ) ;
2020-10-01 15:04:57 +02:00
} else if ( p_index = = 1 ) {
2021-04-24 22:33:50 +02:00
ie - > set_shift_pressed ( p_checked ) ;
2020-10-01 15:04:57 +02:00
} else if ( p_index = = 2 ) {
2022-09-02 11:37:48 +02:00
if ( ! autoremap_command_or_control_checkbox - > is_pressed ( ) ) {
ie - > set_ctrl_pressed ( p_checked ) ;
}
2020-10-01 15:04:57 +02:00
} else if ( p_index = = 3 ) {
2022-09-02 11:37:48 +02:00
if ( ! autoremap_command_or_control_checkbox - > is_pressed ( ) ) {
ie - > set_meta_pressed ( p_checked ) ;
}
2020-10-01 15:04:57 +02:00
}
_set_event ( ie ) ;
}
2022-09-02 11:37:48 +02:00
void InputEventConfigurationDialog : : _autoremap_command_or_control_toggled ( bool p_checked ) {
2020-10-01 15:04:57 +02:00
Ref < InputEventWithModifiers > ie = event ;
if ( ie . is_valid ( ) ) {
2022-09-02 11:37:48 +02:00
ie - > set_command_or_control_autoremap ( p_checked ) ;
2020-10-01 15:04:57 +02:00
_set_event ( ie ) ;
}
if ( p_checked ) {
mod_checkboxes [ MOD_META ] - > hide ( ) ;
2021-04-24 22:33:50 +02:00
mod_checkboxes [ MOD_CTRL ] - > hide ( ) ;
2020-10-01 15:04:57 +02:00
} else {
mod_checkboxes [ MOD_META ] - > show ( ) ;
2022-09-02 11:37:48 +02:00
mod_checkboxes [ MOD_CTRL ] - > show ( ) ;
2020-10-01 15:04:57 +02:00
}
}
void InputEventConfigurationDialog : : _physical_keycode_toggled ( bool p_checked ) {
Ref < InputEventKey > k = event ;
if ( k . is_null ( ) ) {
return ;
}
if ( p_checked ) {
k - > set_physical_keycode ( k - > get_keycode ( ) ) ;
2021-08-13 23:31:57 +02:00
k - > set_keycode ( Key : : NONE ) ;
2020-10-01 15:04:57 +02:00
} else {
2021-06-20 19:12:33 +02:00
k - > set_keycode ( ( Key ) k - > get_physical_keycode ( ) ) ;
2021-08-13 23:31:57 +02:00
k - > set_physical_keycode ( Key : : NONE ) ;
2020-10-01 15:04:57 +02:00
}
_set_event ( k ) ;
}
void InputEventConfigurationDialog : : _input_list_item_selected ( ) {
TreeItem * selected = input_list_tree - > get_selected ( ) ;
// Invalid tree selection - type only exists on the "category" items, which are not a valid selection.
if ( selected - > has_meta ( " __type " ) ) {
return ;
}
2021-06-20 19:12:33 +02:00
InputEventConfigurationDialog : : InputType input_type = ( InputEventConfigurationDialog : : InputType ) ( int ) selected - > get_parent ( ) - > get_meta ( " __type " ) ;
2020-10-01 15:04:57 +02:00
switch ( input_type ) {
case InputEventConfigurationDialog : : INPUT_KEY : {
2021-06-20 19:12:33 +02:00
Key keycode = ( Key ) ( int ) selected - > get_meta ( " __keycode " ) ;
2020-10-01 15:04:57 +02:00
Ref < InputEventKey > k ;
2021-06-18 00:03:09 +02:00
k . instantiate ( ) ;
2020-10-01 15:04:57 +02:00
if ( physical_key_checkbox - > is_pressed ( ) ) {
2021-06-20 19:12:33 +02:00
k - > set_physical_keycode ( keycode ) ;
2021-08-13 23:31:57 +02:00
k - > set_keycode ( Key : : NONE ) ;
2020-10-01 15:04:57 +02:00
} else {
2021-08-13 23:31:57 +02:00
k - > set_physical_keycode ( Key : : NONE ) ;
2021-06-20 19:12:33 +02:00
k - > set_keycode ( keycode ) ;
2020-10-01 15:04:57 +02:00
}
// Maintain modifier state from checkboxes
2021-04-24 22:33:50 +02:00
k - > set_alt_pressed ( mod_checkboxes [ MOD_ALT ] - > is_pressed ( ) ) ;
k - > set_shift_pressed ( mod_checkboxes [ MOD_SHIFT ] - > is_pressed ( ) ) ;
2022-09-02 11:37:48 +02:00
if ( autoremap_command_or_control_checkbox - > is_pressed ( ) ) {
k - > set_command_or_control_autoremap ( true ) ;
} else {
k - > set_ctrl_pressed ( mod_checkboxes [ MOD_CTRL ] - > is_pressed ( ) ) ;
k - > set_meta_pressed ( mod_checkboxes [ MOD_META ] - > is_pressed ( ) ) ;
}
2020-10-01 15:04:57 +02:00
2022-03-25 14:56:25 +01:00
_set_event ( k , false ) ;
2020-10-01 15:04:57 +02:00
} break ;
case InputEventConfigurationDialog : : INPUT_MOUSE_BUTTON : {
2021-06-20 19:12:33 +02:00
MouseButton idx = ( MouseButton ) ( int ) selected - > get_meta ( " __index " ) ;
2020-10-01 15:04:57 +02:00
Ref < InputEventMouseButton > mb ;
2021-06-18 00:03:09 +02:00
mb . instantiate ( ) ;
2021-06-20 19:12:33 +02:00
mb - > set_button_index ( idx ) ;
2020-10-01 15:04:57 +02:00
// Maintain modifier state from checkboxes
2021-04-24 22:33:50 +02:00
mb - > set_alt_pressed ( mod_checkboxes [ MOD_ALT ] - > is_pressed ( ) ) ;
mb - > set_shift_pressed ( mod_checkboxes [ MOD_SHIFT ] - > is_pressed ( ) ) ;
2022-09-02 11:37:48 +02:00
if ( autoremap_command_or_control_checkbox - > is_pressed ( ) ) {
mb - > set_command_or_control_autoremap ( true ) ;
} else {
mb - > set_ctrl_pressed ( mod_checkboxes [ MOD_CTRL ] - > is_pressed ( ) ) ;
mb - > set_meta_pressed ( mod_checkboxes [ MOD_META ] - > is_pressed ( ) ) ;
}
2020-10-01 15:04:57 +02:00
2022-03-25 14:56:25 +01:00
// Maintain selected device
mb - > set_device ( _get_current_device ( ) ) ;
_set_event ( mb , false ) ;
2020-10-01 15:04:57 +02:00
} break ;
case InputEventConfigurationDialog : : INPUT_JOY_BUTTON : {
2021-06-20 19:12:33 +02:00
JoyButton idx = ( JoyButton ) ( int ) selected - > get_meta ( " __index " ) ;
Ref < InputEventJoypadButton > jb = InputEventJoypadButton : : create_reference ( idx ) ;
2022-03-25 14:56:25 +01:00
// Maintain selected device
jb - > set_device ( _get_current_device ( ) ) ;
_set_event ( jb , false ) ;
2020-10-01 15:04:57 +02:00
} break ;
case InputEventConfigurationDialog : : INPUT_JOY_MOTION : {
2021-06-20 19:12:33 +02:00
JoyAxis axis = ( JoyAxis ) ( int ) selected - > get_meta ( " __axis " ) ;
2020-10-01 15:04:57 +02:00
int value = selected - > get_meta ( " __value " ) ;
Ref < InputEventJoypadMotion > jm ;
2021-06-18 00:03:09 +02:00
jm . instantiate ( ) ;
2021-06-20 19:12:33 +02:00
jm - > set_axis ( axis ) ;
2020-10-01 15:04:57 +02:00
jm - > set_axis_value ( value ) ;
2022-03-25 14:56:25 +01:00
// Maintain selected device
jm - > set_device ( _get_current_device ( ) ) ;
_set_event ( jm , false ) ;
2020-10-01 15:04:57 +02:00
} break ;
}
}
2022-03-25 14:56:25 +01:00
void InputEventConfigurationDialog : : _device_selection_changed ( int p_option_button_index ) {
// Subtract 1 as option index 0 corresponds to "All Devices" (value of -1)
// and option index 1 corresponds to device 0, etc...
event - > set_device ( p_option_button_index - 1 ) ;
event_as_text - > set_text ( get_event_text ( event , true ) ) ;
}
void InputEventConfigurationDialog : : _set_current_device ( int p_device ) {
device_id_option - > select ( p_device + 1 ) ;
2020-10-01 15:04:57 +02:00
}
int InputEventConfigurationDialog : : _get_current_device ( ) const {
return device_id_option - > get_selected ( ) - 1 ;
}
2022-03-25 14:56:25 +01:00
String InputEventConfigurationDialog : : _get_device_string ( int p_device ) const {
if ( p_device = = InputMap : : ALL_DEVICES ) {
2020-10-01 15:04:57 +02:00
return TTR ( " All Devices " ) ;
}
2022-03-25 14:56:25 +01:00
return TTR ( " Device " ) + " " + itos ( p_device ) ;
2020-10-01 15:04:57 +02:00
}
void InputEventConfigurationDialog : : _notification ( int p_what ) {
switch ( p_what ) {
2022-08-29 11:04:31 +02:00
case NOTIFICATION_ENTER_TREE :
2020-10-01 15:04:57 +02:00
case NOTIFICATION_THEME_CHANGED : {
2021-07-17 23:22:52 +02:00
input_list_search - > set_right_icon ( input_list_search - > get_theme_icon ( SNAME ( " Search " ) , SNAME ( " EditorIcons " ) ) ) ;
2020-10-01 15:04:57 +02:00
2021-07-17 23:22:52 +02:00
physical_key_checkbox - > set_icon ( get_theme_icon ( SNAME ( " KeyboardPhysical " ) , SNAME ( " EditorIcons " ) ) ) ;
2020-10-01 15:04:57 +02:00
2021-07-17 23:22:52 +02:00
icon_cache . keyboard = get_theme_icon ( SNAME ( " Keyboard " ) , SNAME ( " EditorIcons " ) ) ;
icon_cache . mouse = get_theme_icon ( SNAME ( " Mouse " ) , SNAME ( " EditorIcons " ) ) ;
icon_cache . joypad_button = get_theme_icon ( SNAME ( " JoyButton " ) , SNAME ( " EditorIcons " ) ) ;
icon_cache . joypad_axis = get_theme_icon ( SNAME ( " JoyAxis " ) , SNAME ( " EditorIcons " ) ) ;
2020-10-01 15:04:57 +02:00
_update_input_list ( ) ;
} break ;
}
}
void InputEventConfigurationDialog : : popup_and_configure ( const Ref < InputEvent > & p_event ) {
if ( p_event . is_valid ( ) ) {
_set_event ( p_event ) ;
} else {
// Clear Event
_set_event ( p_event ) ;
// Clear Checkbox Values
for ( int i = 0 ; i < MOD_MAX ; i + + ) {
mod_checkboxes [ i ] - > set_pressed ( false ) ;
}
2021-10-12 12:05:29 +02:00
// Enable the Physical Key checkbox by default to encourage its use.
// Physical Key should be used for most game inputs as it allows keys to work
// on non-QWERTY layouts out of the box.
// This is especially important for WASD movement layouts.
physical_key_checkbox - > set_pressed ( true ) ;
2022-09-02 11:37:48 +02:00
autoremap_command_or_control_checkbox - > set_pressed ( false ) ;
2020-10-01 15:04:57 +02:00
_set_current_device ( 0 ) ;
// Switch to "Listen" tab
tab_container - > set_current_tab ( 0 ) ;
2022-03-25 14:56:25 +01:00
// Select "All Devices" by default.
device_id_option - > select ( 0 ) ;
2020-10-01 15:04:57 +02:00
}
2022-04-08 00:46:49 +02:00
popup_centered ( Size2 ( 0 , 400 ) * EDSCALE ) ;
2020-10-01 15:04:57 +02:00
}
Ref < InputEvent > InputEventConfigurationDialog : : get_event ( ) const {
return event ;
}
void InputEventConfigurationDialog : : set_allowed_input_types ( int p_type_masks ) {
allowed_input_types = p_type_masks ;
}
InputEventConfigurationDialog : : InputEventConfigurationDialog ( ) {
2021-09-20 12:16:38 +02:00
allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION | INPUT_MOUSE_BUTTON ;
2020-10-01 15:04:57 +02:00
2021-07-19 21:10:05 +02:00
set_title ( TTR ( " Event Configuration " ) ) ;
2020-10-01 15:04:57 +02:00
set_min_size ( Size2i ( 550 * EDSCALE , 0 ) ) ; // Min width
VBoxContainer * main_vbox = memnew ( VBoxContainer ) ;
add_child ( main_vbox ) ;
tab_container = memnew ( TabContainer ) ;
tab_container - > set_use_hidden_tabs_for_min_size ( true ) ;
tab_container - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2022-05-27 04:12:09 +02:00
tab_container - > set_theme_type_variation ( " TabContainerOdd " ) ;
2020-10-01 15:04:57 +02:00
tab_container - > connect ( " tab_selected " , callable_mp ( this , & InputEventConfigurationDialog : : _tab_selected ) ) ;
main_vbox - > add_child ( tab_container ) ;
2021-09-20 12:16:38 +02:00
// Listen to input tab
VBoxContainer * vb = memnew ( VBoxContainer ) ;
vb - > set_name ( TTR ( " Listen for Input " ) ) ;
2020-10-01 15:04:57 +02:00
event_as_text = memnew ( Label ) ;
2021-11-25 03:58:47 +01:00
event_as_text - > set_horizontal_alignment ( HORIZONTAL_ALIGNMENT_CENTER ) ;
2021-09-20 12:16:38 +02:00
vb - > add_child ( event_as_text ) ;
2022-04-08 00:46:49 +02:00
// Mouse button detection rect (Mouse button event outside this rect will be ignored)
mouse_detection_rect = memnew ( Panel ) ;
2021-09-20 12:16:38 +02:00
mouse_detection_rect - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
vb - > add_child ( mouse_detection_rect ) ;
tab_container - > add_child ( vb ) ;
2020-10-01 15:04:57 +02:00
// List of all input options to manually select from.
VBoxContainer * manual_vbox = memnew ( VBoxContainer ) ;
2021-07-19 21:10:05 +02:00
manual_vbox - > set_name ( TTR ( " Manual Selection " ) ) ;
2020-10-01 15:04:57 +02:00
manual_vbox - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
tab_container - > add_child ( manual_vbox ) ;
input_list_search = memnew ( LineEdit ) ;
input_list_search - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
input_list_search - > set_placeholder ( TTR ( " Filter Inputs " ) ) ;
input_list_search - > set_clear_button_enabled ( true ) ;
input_list_search - > connect ( " text_changed " , callable_mp ( this , & InputEventConfigurationDialog : : _search_term_updated ) ) ;
manual_vbox - > add_child ( input_list_search ) ;
input_list_tree = memnew ( Tree ) ;
input_list_tree - > set_custom_minimum_size ( Size2 ( 0 , 100 * EDSCALE ) ) ; // Min height for tree
input_list_tree - > connect ( " item_selected " , callable_mp ( this , & InputEventConfigurationDialog : : _input_list_item_selected ) ) ;
input_list_tree - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
manual_vbox - > add_child ( input_list_tree ) ;
input_list_tree - > set_hide_root ( true ) ;
input_list_tree - > set_columns ( 1 ) ;
_update_input_list ( ) ;
// Additional Options
additional_options_container = memnew ( VBoxContainer ) ;
additional_options_container - > hide ( ) ;
Label * opts_label = memnew ( Label ) ;
2021-07-08 15:29:15 +02:00
opts_label - > set_theme_type_variation ( " HeaderSmall " ) ;
2021-07-19 21:10:05 +02:00
opts_label - > set_text ( TTR ( " Additional Options " ) ) ;
2020-10-01 15:04:57 +02:00
additional_options_container - > add_child ( opts_label ) ;
// Device Selection
device_container = memnew ( HBoxContainer ) ;
device_container - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
Label * device_label = memnew ( Label ) ;
2021-07-08 15:29:15 +02:00
device_label - > set_theme_type_variation ( " HeaderSmall " ) ;
2021-07-19 21:10:05 +02:00
device_label - > set_text ( TTR ( " Device: " ) ) ;
2020-10-01 15:04:57 +02:00
device_container - > add_child ( device_label ) ;
device_id_option = memnew ( OptionButton ) ;
device_id_option - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
for ( int i = - 1 ; i < 8 ; i + + ) {
device_id_option - > add_item ( _get_device_string ( i ) ) ;
}
2022-03-25 14:56:25 +01:00
device_id_option - > connect ( " item_selected " , callable_mp ( this , & InputEventConfigurationDialog : : _device_selection_changed ) ) ;
_set_current_device ( InputMap : : ALL_DEVICES ) ;
device_container - > add_child ( device_id_option ) ;
2020-10-01 15:04:57 +02:00
device_container - > hide ( ) ;
additional_options_container - > add_child ( device_container ) ;
// Modifier Selection
mod_container = memnew ( HBoxContainer ) ;
for ( int i = 0 ; i < MOD_MAX ; i + + ) {
String name = mods [ i ] ;
mod_checkboxes [ i ] = memnew ( CheckBox ) ;
2022-07-28 22:56:41 +02:00
mod_checkboxes [ i ] - > connect ( " toggled " , callable_mp ( this , & InputEventConfigurationDialog : : _mod_toggled ) . bind ( i ) ) ;
2020-10-01 15:04:57 +02:00
mod_checkboxes [ i ] - > set_text ( name ) ;
2022-09-02 11:37:48 +02:00
mod_checkboxes [ i ] - > set_tooltip_text ( TTR ( mods_tip [ i ] ) ) ;
2020-10-01 15:04:57 +02:00
mod_container - > add_child ( mod_checkboxes [ i ] ) ;
}
mod_container - > add_child ( memnew ( VSeparator ) ) ;
2022-09-02 11:37:48 +02:00
autoremap_command_or_control_checkbox = memnew ( CheckBox ) ;
autoremap_command_or_control_checkbox - > connect ( " toggled " , callable_mp ( this , & InputEventConfigurationDialog : : _autoremap_command_or_control_toggled ) ) ;
autoremap_command_or_control_checkbox - > set_pressed ( false ) ;
autoremap_command_or_control_checkbox - > set_text ( TTR ( " Command / Control (auto) " ) ) ;
autoremap_command_or_control_checkbox - > set_tooltip_text ( TTR ( " Automatically remaps between 'Meta' ('Command') and 'Control' depending on current platform. " ) ) ;
mod_container - > add_child ( autoremap_command_or_control_checkbox ) ;
2020-10-01 15:04:57 +02:00
mod_container - > hide ( ) ;
additional_options_container - > add_child ( mod_container ) ;
// Physical Key Checkbox
physical_key_checkbox = memnew ( CheckBox ) ;
physical_key_checkbox - > set_text ( TTR ( " Use Physical Keycode " ) ) ;
2022-08-25 12:42:17 +02:00
physical_key_checkbox - > set_tooltip_text ( TTR ( " Stores the physical position of the key on the keyboard rather than the key's value. Used for compatibility with non-latin layouts. \n This should generally be enabled for most game shortcuts, but not in non-game applications. " ) ) ;
2020-10-01 15:04:57 +02:00
physical_key_checkbox - > connect ( " toggled " , callable_mp ( this , & InputEventConfigurationDialog : : _physical_keycode_toggled ) ) ;
physical_key_checkbox - > hide ( ) ;
additional_options_container - > add_child ( physical_key_checkbox ) ;
main_vbox - > add_child ( additional_options_container ) ;
// Default to first tab
tab_container - > set_current_tab ( 0 ) ;
}
/////////////////////////////////////////
static bool _is_action_name_valid ( const String & p_name ) {
const char32_t * cstr = p_name . get_data ( ) ;
for ( int i = 0 ; cstr [ i ] ; i + + ) {
if ( cstr [ i ] = = ' / ' | | cstr [ i ] = = ' : ' | | cstr [ i ] = = ' " ' | |
cstr [ i ] = = ' = ' | | cstr [ i ] = = ' \\ ' | | cstr [ i ] < 32 ) {
return false ;
}
}
return true ;
}
void ActionMapEditor : : _event_config_confirmed ( ) {
Ref < InputEvent > ev = event_config_dialog - > get_event ( ) ;
Dictionary new_action = current_action . duplicate ( ) ;
2021-12-24 05:19:24 +01:00
Array events = new_action [ " events " ] . duplicate ( ) ;
2020-10-01 15:04:57 +02:00
if ( current_action_event_index = = - 1 ) {
// Add new event
events . push_back ( ev ) ;
} else {
// Edit existing event
events [ current_action_event_index ] = ev ;
}
new_action [ " events " ] = events ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " action_edited " ) , current_action_name , new_action ) ;
2020-10-01 15:04:57 +02:00
}
void ActionMapEditor : : _add_action_pressed ( ) {
_add_action ( add_edit - > get_text ( ) ) ;
}
2022-05-04 01:47:29 +02:00
String ActionMapEditor : : _check_new_action_name ( const String & p_name ) {
if ( p_name . is_empty ( ) | | ! _is_action_name_valid ( p_name ) ) {
return TTR ( " Invalid action name. It cannot be empty nor contain '/', ':', '=', ' \\ ' or ' \" ' " ) ;
}
if ( _has_action ( p_name ) ) {
return vformat ( TTR ( " An action with the name '%s' already exists. " ) , p_name ) ;
}
return " " ;
}
void ActionMapEditor : : _add_edit_text_changed ( const String & p_name ) {
String error = _check_new_action_name ( p_name ) ;
2022-08-25 12:42:17 +02:00
add_button - > set_tooltip_text ( error ) ;
2022-05-04 01:47:29 +02:00
add_button - > set_disabled ( ! error . is_empty ( ) ) ;
}
2021-12-28 18:08:06 +01:00
bool ActionMapEditor : : _has_action ( const String & p_name ) const {
for ( const ActionInfo & action_info : actions_cache ) {
if ( p_name = = action_info . name ) {
return true ;
}
}
return false ;
}
2020-10-01 15:04:57 +02:00
void ActionMapEditor : : _add_action ( const String & p_name ) {
2022-05-04 01:47:29 +02:00
String error = _check_new_action_name ( p_name ) ;
if ( ! error . is_empty ( ) ) {
show_message ( error ) ;
2021-12-28 18:08:06 +01:00
return ;
}
2020-10-01 15:04:57 +02:00
add_edit - > clear ( ) ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " action_added " ) , p_name ) ;
2020-10-01 15:04:57 +02:00
}
void ActionMapEditor : : _action_edited ( ) {
TreeItem * ti = action_tree - > get_edited ( ) ;
if ( ! ti ) {
return ;
}
if ( action_tree - > get_selected_column ( ) = = 0 ) {
// Name Edited
String new_name = ti - > get_text ( 0 ) ;
String old_name = ti - > get_meta ( " __name " ) ;
if ( new_name = = old_name ) {
return ;
}
2021-12-09 10:42:46 +01:00
if ( new_name . is_empty ( ) | | ! _is_action_name_valid ( new_name ) ) {
2020-10-01 15:04:57 +02:00
ti - > set_text ( 0 , old_name ) ;
2021-05-20 12:07:26 +02:00
show_message ( TTR ( " Invalid action name. It cannot be empty nor contain '/', ':', '=', ' \\ ' or ' \" ' " ) ) ;
2020-10-01 15:04:57 +02:00
return ;
}
2021-12-28 18:08:06 +01:00
if ( _has_action ( new_name ) ) {
ti - > set_text ( 0 , old_name ) ;
show_message ( vformat ( TTR ( " An action with the name '%s' already exists. " ) , new_name ) ) ;
return ;
}
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " action_renamed " ) , old_name , new_name ) ;
2020-10-01 15:04:57 +02:00
} else if ( action_tree - > get_selected_column ( ) = = 1 ) {
// Deadzone Edited
String name = ti - > get_meta ( " __name " ) ;
Dictionary old_action = ti - > get_meta ( " __action " ) ;
Dictionary new_action = old_action . duplicate ( ) ;
new_action [ " deadzone " ] = ti - > get_range ( 1 ) ;
// Call deferred so that input can finish propagating through tree, allowing re-making of tree to occur.
2021-07-17 23:22:52 +02:00
call_deferred ( SNAME ( " emit_signal " ) , " action_edited " , name , new_action ) ;
2020-10-01 15:04:57 +02:00
}
}
2021-09-18 09:33:18 +02:00
void ActionMapEditor : : _tree_button_pressed ( Object * p_item , int p_column , int p_id , MouseButton p_button ) {
if ( p_button ! = MouseButton : : LEFT ) {
return ;
}
2020-10-01 15:04:57 +02:00
ItemButton option = ( ItemButton ) p_id ;
TreeItem * item = Object : : cast_to < TreeItem > ( p_item ) ;
if ( ! item ) {
return ;
}
switch ( option ) {
case ActionMapEditor : : BUTTON_ADD_EVENT : {
current_action = item - > get_meta ( " __action " ) ;
current_action_name = item - > get_meta ( " __name " ) ;
current_action_event_index = - 1 ;
event_config_dialog - > popup_and_configure ( ) ;
} break ;
case ActionMapEditor : : BUTTON_EDIT_EVENT : {
// Action and Action name is located on the parent of the event.
current_action = item - > get_parent ( ) - > get_meta ( " __action " ) ;
current_action_name = item - > get_parent ( ) - > get_meta ( " __name " ) ;
current_action_event_index = item - > get_meta ( " __index " ) ;
Ref < InputEvent > ie = item - > get_meta ( " __event " ) ;
if ( ie . is_valid ( ) ) {
event_config_dialog - > popup_and_configure ( ie ) ;
}
} break ;
case ActionMapEditor : : BUTTON_REMOVE_ACTION : {
// Send removed action name
String name = item - > get_meta ( " __name " ) ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " action_removed " ) , name ) ;
2020-10-01 15:04:57 +02:00
} break ;
case ActionMapEditor : : BUTTON_REMOVE_EVENT : {
// Remove event and send updated action
2021-12-24 05:19:24 +01:00
Dictionary action = item - > get_parent ( ) - > get_meta ( " __action " ) . duplicate ( ) ;
2020-10-01 15:04:57 +02:00
String action_name = item - > get_parent ( ) - > get_meta ( " __name " ) ;
int event_index = item - > get_meta ( " __index " ) ;
2021-12-24 05:19:24 +01:00
Array events = action [ " events " ] . duplicate ( ) ;
2021-07-04 00:17:03 +02:00
events . remove_at ( event_index ) ;
2020-10-01 15:04:57 +02:00
action [ " events " ] = events ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " action_edited " ) , action_name , action ) ;
2020-10-01 15:04:57 +02:00
} break ;
default :
break ;
}
}
void ActionMapEditor : : _tree_item_activated ( ) {
TreeItem * item = action_tree - > get_selected ( ) ;
if ( ! item | | ! item - > has_meta ( " __event " ) ) {
return ;
}
2021-09-18 09:33:18 +02:00
_tree_button_pressed ( item , 2 , BUTTON_EDIT_EVENT , MouseButton : : LEFT ) ;
2020-10-01 15:04:57 +02:00
}
2021-05-05 21:49:35 +02:00
void ActionMapEditor : : set_show_builtin_actions ( bool p_show ) {
show_builtin_actions = p_show ;
show_builtin_actions_checkbutton - > set_pressed ( p_show ) ;
2020-10-01 15:04:57 +02:00
2021-05-05 21:49:35 +02:00
// Prevent unnecessary updates of action list when cache is empty.
2020-10-01 15:04:57 +02:00
if ( ! actions_cache . is_empty ( ) ) {
update_action_list ( ) ;
}
}
void ActionMapEditor : : _search_term_updated ( const String & ) {
update_action_list ( ) ;
}
Variant ActionMapEditor : : get_drag_data_fw ( const Point2 & p_point , Control * p_from ) {
TreeItem * selected = action_tree - > get_selected ( ) ;
if ( ! selected ) {
return Variant ( ) ;
}
String name = selected - > get_text ( 0 ) ;
Label * label = memnew ( Label ( name ) ) ;
2021-07-08 15:29:15 +02:00
label - > set_theme_type_variation ( " HeaderSmall " ) ;
2020-10-01 15:04:57 +02:00
label - > set_modulate ( Color ( 1 , 1 , 1 , 1.0f ) ) ;
action_tree - > set_drag_preview ( label ) ;
Dictionary drag_data ;
if ( selected - > has_meta ( " __action " ) ) {
drag_data [ " input_type " ] = " action " ;
}
if ( selected - > has_meta ( " __event " ) ) {
drag_data [ " input_type " ] = " event " ;
}
action_tree - > set_drop_mode_flags ( Tree : : DROP_MODE_INBETWEEN ) ;
return drag_data ;
}
bool ActionMapEditor : : can_drop_data_fw ( const Point2 & p_point , const Variant & p_data , Control * p_from ) const {
Dictionary d = p_data ;
if ( ! d . has ( " input_type " ) ) {
return false ;
}
TreeItem * selected = action_tree - > get_selected ( ) ;
TreeItem * item = action_tree - > get_item_at_position ( p_point ) ;
if ( ! selected | | ! item | | item = = selected ) {
return false ;
}
// Don't allow moving an action in-between events.
if ( d [ " input_type " ] = = " action " & & item - > has_meta ( " __event " ) ) {
return false ;
}
// Don't allow moving an event to a different action.
if ( d [ " input_type " ] = = " event " & & item - > get_parent ( ) ! = selected - > get_parent ( ) ) {
return false ;
}
return true ;
}
void ActionMapEditor : : drop_data_fw ( const Point2 & p_point , const Variant & p_data , Control * p_from ) {
if ( ! can_drop_data_fw ( p_point , p_data , p_from ) ) {
return ;
}
TreeItem * selected = action_tree - > get_selected ( ) ;
TreeItem * target = action_tree - > get_item_at_position ( p_point ) ;
bool drop_above = action_tree - > get_drop_section_at_position ( p_point ) = = - 1 ;
if ( ! target ) {
return ;
}
Dictionary d = p_data ;
if ( d [ " input_type " ] = = " action " ) {
// Change action order.
String relative_to = target - > get_meta ( " __name " ) ;
String action_name = selected - > get_meta ( " __name " ) ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " action_reordered " ) , action_name , relative_to , drop_above ) ;
2020-10-01 15:04:57 +02:00
} else if ( d [ " input_type " ] = = " event " ) {
// Change event order
int current_index = selected - > get_meta ( " __index " ) ;
int target_index = target - > get_meta ( " __index " ) ;
// Construct new events array.
Dictionary new_action = selected - > get_parent ( ) - > get_meta ( " __action " ) ;
Array events = new_action [ " events " ] ;
Array new_events ;
// The following method was used to perform the array changes since `remove` followed by `insert` was not working properly at time of writing.
// Loop thought existing events
for ( int i = 0 ; i < events . size ( ) ; i + + ) {
// If you come across the current index, just skip it, as it has been moved.
if ( i = = current_index ) {
continue ;
} else if ( i = = target_index ) {
// We are at the target index. If drop above, add selected event there first, then target, so moved event goes on top.
if ( drop_above ) {
new_events . push_back ( events [ current_index ] ) ;
new_events . push_back ( events [ target_index ] ) ;
} else {
new_events . push_back ( events [ target_index ] ) ;
new_events . push_back ( events [ current_index ] ) ;
}
} else {
new_events . push_back ( events [ i ] ) ;
}
}
new_action [ " events " ] = new_events ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " action_edited " ) , selected - > get_parent ( ) - > get_meta ( " __name " ) , new_action ) ;
2020-10-01 15:04:57 +02:00
}
}
void ActionMapEditor : : _notification ( int p_what ) {
switch ( p_what ) {
2022-08-29 11:04:31 +02:00
case NOTIFICATION_ENTER_TREE :
2020-10-01 15:04:57 +02:00
case NOTIFICATION_THEME_CHANGED : {
2021-07-17 23:22:52 +02:00
action_list_search - > set_right_icon ( get_theme_icon ( SNAME ( " Search " ) , SNAME ( " EditorIcons " ) ) ) ;
2022-08-25 08:11:39 +02:00
if ( ! actions_cache . is_empty ( ) ) {
update_action_list ( ) ;
}
2020-10-01 15:04:57 +02:00
} break ;
}
}
void ActionMapEditor : : _bind_methods ( ) {
2021-05-15 23:48:59 +02:00
ClassDB : : bind_method ( D_METHOD ( " _get_drag_data_fw " ) , & ActionMapEditor : : get_drag_data_fw ) ;
ClassDB : : bind_method ( D_METHOD ( " _can_drop_data_fw " ) , & ActionMapEditor : : can_drop_data_fw ) ;
ClassDB : : bind_method ( D_METHOD ( " _drop_data_fw " ) , & ActionMapEditor : : drop_data_fw ) ;
2020-10-01 15:04:57 +02:00
ADD_SIGNAL ( MethodInfo ( " action_added " , PropertyInfo ( Variant : : STRING , " name " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " action_edited " , PropertyInfo ( Variant : : STRING , " name " ) , PropertyInfo ( Variant : : DICTIONARY , " new_action " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " action_removed " , PropertyInfo ( Variant : : STRING , " name " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " action_renamed " , PropertyInfo ( Variant : : STRING , " old_name " ) , PropertyInfo ( Variant : : STRING , " new_name " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " action_reordered " , PropertyInfo ( Variant : : STRING , " action_name " ) , PropertyInfo ( Variant : : STRING , " relative_to " ) , PropertyInfo ( Variant : : BOOL , " before " ) ) ) ;
}
LineEdit * ActionMapEditor : : get_search_box ( ) const {
return action_list_search ;
}
InputEventConfigurationDialog * ActionMapEditor : : get_configuration_dialog ( ) {
return event_config_dialog ;
}
void ActionMapEditor : : update_action_list ( const Vector < ActionInfo > & p_action_infos ) {
if ( ! p_action_infos . is_empty ( ) ) {
actions_cache = p_action_infos ;
}
action_tree - > clear ( ) ;
TreeItem * root = action_tree - > create_item ( ) ;
int uneditable_count = 0 ;
for ( int i = 0 ; i < actions_cache . size ( ) ; i + + ) {
ActionInfo action_info = actions_cache [ i ] ;
if ( ! action_info . editable ) {
uneditable_count + + ;
}
String search_term = action_list_search - > get_text ( ) ;
if ( ! search_term . is_empty ( ) & & action_info . name . findn ( search_term ) = = - 1 ) {
continue ;
}
2021-05-05 21:49:35 +02:00
if ( ! action_info . editable & & ! show_builtin_actions ) {
2020-10-01 15:04:57 +02:00
continue ;
}
const Array events = action_info . action [ " events " ] ;
const Variant deadzone = action_info . action [ " deadzone " ] ;
// Update Tree...
TreeItem * action_item = action_tree - > create_item ( root ) ;
action_item - > set_meta ( " __action " , action_info . action ) ;
action_item - > set_meta ( " __name " , action_info . name ) ;
// First Column - Action Name
action_item - > set_text ( 0 , action_info . name ) ;
action_item - > set_editable ( 0 , action_info . editable ) ;
action_item - > set_icon ( 0 , action_info . icon ) ;
// Second Column - Deadzone
action_item - > set_editable ( 1 , true ) ;
action_item - > set_cell_mode ( 1 , TreeItem : : CELL_MODE_RANGE ) ;
action_item - > set_range_config ( 1 , 0.0 , 1.0 , 0.01 ) ;
action_item - > set_range ( 1 , deadzone ) ;
// Third column - buttons
2021-07-17 23:22:52 +02:00
action_item - > add_button ( 2 , action_tree - > get_theme_icon ( SNAME ( " Add " ) , SNAME ( " EditorIcons " ) ) , BUTTON_ADD_EVENT , false , TTR ( " Add Event " ) ) ;
2021-07-20 20:07:42 +02:00
action_item - > add_button ( 2 , action_tree - > get_theme_icon ( SNAME ( " Remove " ) , SNAME ( " EditorIcons " ) ) , BUTTON_REMOVE_ACTION , ! action_info . editable , action_info . editable ? TTR ( " Remove Action " ) : TTR ( " Cannot Remove Action " ) ) ;
2020-10-01 15:04:57 +02:00
2021-07-17 23:22:52 +02:00
action_item - > set_custom_bg_color ( 0 , action_tree - > get_theme_color ( SNAME ( " prop_subsection " ) , SNAME ( " Editor " ) ) ) ;
action_item - > set_custom_bg_color ( 1 , action_tree - > get_theme_color ( SNAME ( " prop_subsection " ) , SNAME ( " Editor " ) ) ) ;
2020-10-01 15:04:57 +02:00
for ( int evnt_idx = 0 ; evnt_idx < events . size ( ) ; evnt_idx + + ) {
Ref < InputEvent > event = events [ evnt_idx ] ;
if ( event . is_null ( ) ) {
continue ;
}
TreeItem * event_item = action_tree - > create_item ( action_item ) ;
// First Column - Text
2022-03-25 14:56:25 +01:00
event_item - > set_text ( 0 , event_config_dialog - > get_event_text ( event , true ) ) ;
2020-10-01 15:04:57 +02:00
event_item - > set_meta ( " __event " , event ) ;
event_item - > set_meta ( " __index " , evnt_idx ) ;
2021-10-12 18:49:13 +02:00
// First Column - Icon
Ref < InputEventKey > k = event ;
if ( k . is_valid ( ) ) {
2022-03-25 15:22:53 +01:00
if ( k - > get_physical_keycode ( ) = = Key : : NONE ) {
2021-10-12 18:49:13 +02:00
event_item - > set_icon ( 0 , action_tree - > get_theme_icon ( SNAME ( " Keyboard " ) , SNAME ( " EditorIcons " ) ) ) ;
} else {
event_item - > set_icon ( 0 , action_tree - > get_theme_icon ( SNAME ( " KeyboardPhysical " ) , SNAME ( " EditorIcons " ) ) ) ;
}
}
Ref < InputEventMouseButton > mb = event ;
if ( mb . is_valid ( ) ) {
event_item - > set_icon ( 0 , action_tree - > get_theme_icon ( SNAME ( " Mouse " ) , SNAME ( " EditorIcons " ) ) ) ;
}
Ref < InputEventJoypadButton > jb = event ;
if ( jb . is_valid ( ) ) {
event_item - > set_icon ( 0 , action_tree - > get_theme_icon ( SNAME ( " JoyButton " ) , SNAME ( " EditorIcons " ) ) ) ;
}
Ref < InputEventJoypadMotion > jm = event ;
if ( jm . is_valid ( ) ) {
event_item - > set_icon ( 0 , action_tree - > get_theme_icon ( SNAME ( " JoyAxis " ) , SNAME ( " EditorIcons " ) ) ) ;
}
2020-10-01 15:04:57 +02:00
// Third Column - Buttons
2021-07-17 23:22:52 +02:00
event_item - > add_button ( 2 , action_tree - > get_theme_icon ( SNAME ( " Edit " ) , SNAME ( " EditorIcons " ) ) , BUTTON_EDIT_EVENT , false , TTR ( " Edit Event " ) ) ;
event_item - > add_button ( 2 , action_tree - > get_theme_icon ( SNAME ( " Remove " ) , SNAME ( " EditorIcons " ) ) , BUTTON_REMOVE_EVENT , false , TTR ( " Remove Event " ) ) ;
2020-10-01 15:04:57 +02:00
event_item - > set_button_color ( 2 , 0 , Color ( 1 , 1 , 1 , 0.75 ) ) ;
event_item - > set_button_color ( 2 , 1 , Color ( 1 , 1 , 1 , 0.75 ) ) ;
}
}
}
void ActionMapEditor : : show_message ( const String & p_message ) {
message - > set_text ( p_message ) ;
2022-04-08 00:46:49 +02:00
message - > popup_centered ( ) ;
2020-10-01 15:04:57 +02:00
}
void ActionMapEditor : : use_external_search_box ( LineEdit * p_searchbox ) {
memdelete ( action_list_search ) ;
action_list_search = p_searchbox ;
action_list_search - > connect ( " text_changed " , callable_mp ( this , & ActionMapEditor : : _search_term_updated ) ) ;
}
ActionMapEditor : : ActionMapEditor ( ) {
// Main Vbox Container
VBoxContainer * main_vbox = memnew ( VBoxContainer ) ;
2022-03-19 01:02:57 +01:00
main_vbox - > set_anchors_and_offsets_preset ( PRESET_FULL_RECT ) ;
2020-10-01 15:04:57 +02:00
add_child ( main_vbox ) ;
HBoxContainer * top_hbox = memnew ( HBoxContainer ) ;
main_vbox - > add_child ( top_hbox ) ;
action_list_search = memnew ( LineEdit ) ;
action_list_search - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
action_list_search - > set_placeholder ( TTR ( " Filter Actions " ) ) ;
action_list_search - > set_clear_button_enabled ( true ) ;
action_list_search - > connect ( " text_changed " , callable_mp ( this , & ActionMapEditor : : _search_term_updated ) ) ;
top_hbox - > add_child ( action_list_search ) ;
2021-05-05 21:49:35 +02:00
show_builtin_actions_checkbutton = memnew ( CheckButton ) ;
show_builtin_actions_checkbutton - > set_pressed ( false ) ;
show_builtin_actions_checkbutton - > set_text ( TTR ( " Show Built-in Actions " ) ) ;
show_builtin_actions_checkbutton - > connect ( " toggled " , callable_mp ( this , & ActionMapEditor : : set_show_builtin_actions ) ) ;
top_hbox - > add_child ( show_builtin_actions_checkbutton ) ;
2020-10-01 15:04:57 +02:00
// Adding Action line edit + button
add_hbox = memnew ( HBoxContainer ) ;
add_hbox - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
add_edit = memnew ( LineEdit ) ;
add_edit - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
add_edit - > set_placeholder ( TTR ( " Add New Action " ) ) ;
add_edit - > set_clear_button_enabled ( true ) ;
2022-05-04 01:47:29 +02:00
add_edit - > connect ( " text_changed " , callable_mp ( this , & ActionMapEditor : : _add_edit_text_changed ) ) ;
2021-06-16 18:43:34 +02:00
add_edit - > connect ( " text_submitted " , callable_mp ( this , & ActionMapEditor : : _add_action ) ) ;
2020-10-01 15:04:57 +02:00
add_hbox - > add_child ( add_edit ) ;
2022-05-04 01:47:29 +02:00
add_button = memnew ( Button ) ;
2021-05-05 21:49:35 +02:00
add_button - > set_text ( TTR ( " Add " ) ) ;
2020-10-01 15:04:57 +02:00
add_button - > connect ( " pressed " , callable_mp ( this , & ActionMapEditor : : _add_action_pressed ) ) ;
add_hbox - > add_child ( add_button ) ;
2022-05-04 01:47:29 +02:00
// Disable the button and set its tooltip.
_add_edit_text_changed ( add_edit - > get_text ( ) ) ;
2020-10-01 15:04:57 +02:00
main_vbox - > add_child ( add_hbox ) ;
// Action Editor Tree
action_tree = memnew ( Tree ) ;
action_tree - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
action_tree - > set_columns ( 3 ) ;
action_tree - > set_hide_root ( true ) ;
action_tree - > set_column_titles_visible ( true ) ;
action_tree - > set_column_title ( 0 , TTR ( " Action " ) ) ;
2021-07-04 05:13:28 +02:00
action_tree - > set_column_clip_content ( 0 , true ) ;
2020-10-01 15:04:57 +02:00
action_tree - > set_column_title ( 1 , TTR ( " Deadzone " ) ) ;
action_tree - > set_column_expand ( 1 , false ) ;
2021-06-28 15:40:56 +02:00
action_tree - > set_column_custom_minimum_width ( 1 , 80 * EDSCALE ) ;
2020-10-01 15:04:57 +02:00
action_tree - > set_column_expand ( 2 , false ) ;
2021-06-28 15:40:56 +02:00
action_tree - > set_column_custom_minimum_width ( 2 , 50 * EDSCALE ) ;
2020-10-01 15:04:57 +02:00
action_tree - > connect ( " item_edited " , callable_mp ( this , & ActionMapEditor : : _action_edited ) ) ;
action_tree - > connect ( " item_activated " , callable_mp ( this , & ActionMapEditor : : _tree_item_activated ) ) ;
2021-09-18 09:33:18 +02:00
action_tree - > connect ( " button_clicked " , callable_mp ( this , & ActionMapEditor : : _tree_button_pressed ) ) ;
2020-10-01 15:04:57 +02:00
main_vbox - > add_child ( action_tree ) ;
action_tree - > set_drag_forwarding ( this ) ;
// Adding event dialog
event_config_dialog = memnew ( InputEventConfigurationDialog ) ;
event_config_dialog - > connect ( " confirmed " , callable_mp ( this , & ActionMapEditor : : _event_config_confirmed ) ) ;
add_child ( event_config_dialog ) ;
message = memnew ( AcceptDialog ) ;
add_child ( message ) ;
}