2 class_name DialogicVisualEditorFieldNumber
3 extends DialogicVisualEditorField
5 ## Event block field for integers and floats. Improved version of the native spinbox.
7 @export var allow_string: bool = false
8 @export var step: float = 0.1
9 @export var enforce_step: bool = true
10 @export var min: float = -INF
11 @export var max: float = INF
12 @export var value = 0.0
13 @export var prefix: String = ""
14 @export var suffix: String = ""
16 var _is_holding_button: bool = false #For handling incrementing while holding key or click
19 ################################################################################
21 func _ready() -> void:
22 if %Value.text.is_empty():
29 func _load_display_info(info: Dictionary) -> void:
30 match info.get('mode', 0):
32 use_float_mode(info.get('step', 0.1))
34 use_int_mode(info.get('step', 1))
36 use_decibel_mode(info.get('step', step))
38 for option in info.keys():
40 'min': min = info[option]
41 'max': max = info[option]
42 'prefix': update_prefix(info[option])
43 'suffix': update_suffix(info[option])
47 'hide_step_button': %Spin.hide()
50 func _set_value(new_value: Variant) -> void:
51 _on_value_text_submitted(str(new_value), true)
52 %Value.tooltip_text = tooltip_text
55 func _autofocus() -> void:
59 func get_value() -> float:
63 func use_float_mode(value_step: float = 0.1) -> void:
69 func use_int_mode(value_step: float = 1) -> void:
75 func use_decibel_mode(value_step: float = step) -> void:
82 #region UI FUNCTIONALITY
83 ################################################################################
84 var _stop_button_holding: Callable = func(button: BaseButton) -> void:
85 _is_holding_button = false
86 if button.button_up.get_connections().find(_stop_button_holding):
87 button.button_up.disconnect(_stop_button_holding)
88 if button.focus_exited.get_connections().find(_stop_button_holding):
89 button.focus_exited.disconnect(_stop_button_holding)
90 if button.mouse_exited.get_connections().find(_stop_button_holding):
91 button.mouse_exited.disconnect(_stop_button_holding)
94 func _holding_button(value_direction: int, button: BaseButton) -> void:
95 if _is_holding_button:
97 if _stop_button_holding.get_bound_arguments_count() > 0:
98 _stop_button_holding.unbind(0)
100 _is_holding_button = true
102 #Ensure removal of our value changing routine when it shouldn't run anymore
103 button.button_up.connect(_stop_button_holding.bind(button))
104 button.focus_exited.connect(_stop_button_holding.bind(button))
105 button.mouse_exited.connect(_stop_button_holding.bind(button))
107 var scene_tree: SceneTree = get_tree()
108 var delay_timer_ms: int = 600
110 #Instead of awaiting for the duration, await per-frame so we can catch any changes in _is_holding_button and exit completely
111 while(delay_timer_ms > 0):
112 if _is_holding_button == false:
114 var pre_time: int = Time.get_ticks_msec()
115 await scene_tree.process_frame
116 delay_timer_ms -= Time.get_ticks_msec() - pre_time
118 var change_speed: float = 0.25
120 while(_is_holding_button == true):
121 await scene_tree.create_timer(change_speed).timeout
122 change_speed = maxf(0.05, change_speed - 0.01)
123 _on_value_text_submitted(str(value+(step * value_direction)))
126 func update_prefix(to_prefix: String) -> void:
128 %Prefix.visible = to_prefix != null and to_prefix != ""
129 %Prefix.text = prefix
132 func update_suffix(to_suffix: String) -> void:
134 %Suffix.visible = to_suffix != null and to_suffix != ""
135 %Suffix.text = suffix
139 #region SIGNAL METHODS
140 ################################################################################
141 func _on_gui_input(event: InputEvent) -> void:
142 if event.is_action('ui_up') and event.get_action_strength('ui_up') > 0.5:
143 _on_value_text_submitted(str(value+step))
144 elif event.is_action('ui_down') and event.get_action_strength('ui_down') > 0.5:
145 _on_value_text_submitted(str(value-step))
148 func _on_increment_button_down(button: NodePath) -> void:
149 _on_value_text_submitted(str(value+step))
150 _holding_button(1.0, get_node(button) as BaseButton)
153 func _on_decrement_button_down(button: NodePath) -> void:
154 _on_value_text_submitted(str(value-step))
155 _holding_button(-1.0, get_node(button) as BaseButton)
158 func _on_value_text_submitted(new_text: String, no_signal:= false) -> void:
159 if new_text.is_empty() and not allow_string:
161 if new_text.is_valid_float():
162 var temp: float = min(max(new_text.to_float(), min), max)
166 value = snapped(temp, step)
169 %Value.text = str(value).pad_decimals(len(str(float(step)-floorf(step)))-2)
171 value_changed.emit(property_name, value)
172 # Visually disable Up or Down arrow when limit is reached to better indicate a limit has been hit
173 %Spin/Decrement.disabled = value <= min
174 %Spin/Increment.disabled = value >= max
177 # If prefix or suffix was clicked, select the actual value box instead and move the caret to the closest side.
178 func _on_sublabel_clicked(event: InputEvent) -> void:
179 if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
180 var mousePos: Vector2 = get_global_mouse_position()
181 mousePos.x -= get_minimum_size().x / 2
182 if mousePos.x > global_position.x:
183 (%Value as LineEdit).caret_column = (%Value as LineEdit).text.length()
185 (%Value as LineEdit).caret_column = 0
186 (%Value as LineEdit).grab_focus()
189 func _on_value_focus_exited() -> void:
190 _on_value_text_submitted(%Value.text)
191 $Value_Panel.add_theme_stylebox_override('panel', get_theme_stylebox('panel', 'DialogicEventEdit'))
194 func _on_value_focus_entered() -> void:
195 $Value_Panel.add_theme_stylebox_override('panel', get_theme_stylebox('focus', 'DialogicEventEdit'))
196 %Value.select_all.call_deferred()