@tool class_name DialogicVisualEditorFieldNumber extends DialogicVisualEditorField ## Event block field for integers and floats. Improved version of the native spinbox. @export var allow_string : bool = false @export var step: float = 0.1 @export var enforce_step: bool = true @export var min: float = 0 @export var max: float= 999 @export var value = 0.0 @export var prefix: String = "" @export var suffix: String = "" var _is_holding_button: bool = false #For handling incrementing while holding key or click #region MAIN METHODS ################################################################################ func _ready() -> void: if %Value.text.is_empty(): set_value(value) update_prefix(prefix) update_suffix(suffix) $Value_Panel.add_theme_stylebox_override('panel', get_theme_stylebox('panel', 'DialogicEventEdit')) func _load_display_info(info: Dictionary) -> void: match info.get('mode', 0): 0: #FLOAT use_float_mode(info.get('step', 0.1)) 1: #INT use_int_mode(info.get('step', 1)) 2: #DECIBLE: use_decibel_mode(info.get('step', step)) for option in info.keys(): match option: 'min': min = info[option] 'max': max = info[option] 'prefix': update_prefix(info[option]) 'suffix': update_suffix(info[option]) 'step': enforce_step = true step = info[option] 'hide_step_button': %Spin.hide() func _set_value(new_value: Variant) -> void: _on_value_text_submitted(str(new_value), true) %Value.tooltip_text = tooltip_text func _autofocus(): %Value.grab_focus() func get_value() -> float: return value func use_float_mode(value_step: float = 0.1) -> void: step = value_step update_suffix("") enforce_step = false func use_int_mode(value_step: float = 1) -> void: step = value_step update_suffix("") enforce_step = true func use_decibel_mode(value_step: float = step) -> void: max = 6 update_suffix("dB") min = -80 #endregion #region UI FUNCTIONALITY ################################################################################ var _stop_button_holding: Callable = func(button: BaseButton) -> void: _is_holding_button = false if button.button_up.get_connections().find(_stop_button_holding): button.button_up.disconnect(_stop_button_holding) if button.focus_exited.get_connections().find(_stop_button_holding): button.focus_exited.disconnect(_stop_button_holding) if button.mouse_exited.get_connections().find(_stop_button_holding): button.mouse_exited.disconnect(_stop_button_holding) func _holding_button(value_direction: int, button: BaseButton) -> void: if _is_holding_button: return if _stop_button_holding.get_bound_arguments_count() > 0: _stop_button_holding.unbind(0) _is_holding_button = true #Ensure removal of our value changing routine when it shouldn't run anymore button.button_up.connect(_stop_button_holding.bind(button)) button.focus_exited.connect(_stop_button_holding.bind(button)) button.mouse_exited.connect(_stop_button_holding.bind(button)) var scene_tree: SceneTree = get_tree() var delay_timer_ms: int = 600 #Instead of awaiting for the duration, await per-frame so we can catch any changes in _is_holding_button and exit completely while(delay_timer_ms > 0): if _is_holding_button == false: return var pre_time: int = Time.get_ticks_msec() await scene_tree.process_frame delay_timer_ms -= Time.get_ticks_msec() - pre_time var change_speed: float = 0.25 while(_is_holding_button == true): await scene_tree.create_timer(change_speed).timeout change_speed = maxf(0.05, change_speed - 0.01) _on_value_text_submitted(str(value+(step * value_direction))) func update_prefix(to_prefix: String) -> void: prefix = to_prefix %Prefix.visible = to_prefix != null and to_prefix != "" %Prefix.text = prefix func update_suffix(to_suffix: String) -> void: suffix = to_suffix %Suffix.visible = to_suffix != null and to_suffix != "" %Suffix.text = suffix #endregion #region SIGNAL METHODS ################################################################################ func _on_gui_input(event: InputEvent) -> void: if event.is_action('ui_up') and event.get_action_strength('ui_up') > 0.5: _on_value_text_submitted(str(value+step)) elif event.is_action('ui_down') and event.get_action_strength('ui_down') > 0.5: _on_value_text_submitted(str(value-step)) func _on_increment_button_down(button: NodePath) -> void: _on_value_text_submitted(str(value+step)) _holding_button(1.0, get_node(button) as BaseButton) func _on_decrement_button_down(button: NodePath) -> void: _on_value_text_submitted(str(value-step)) _holding_button(-1.0, get_node(button) as BaseButton) func _on_value_text_submitted(new_text: String, no_signal:= false) -> void: if new_text.is_valid_float(): var temp: float = min(max(new_text.to_float(), min), max) if !enforce_step: value = temp else: value = snapped(temp, step) elif allow_string: value = new_text %Value.text = str(value).pad_decimals(len(str(float(step)-floorf(step)))-2) if not no_signal: value_changed.emit(property_name, value) # Visually disable Up or Down arrow when limit is reached to better indicate a limit has been hit %Spin/Decrement.disabled = value <= min %Spin/Increment.disabled = value >= max # If prefix or suffix was clicked, select the actual value box instead and move the caret to the closest side. func _on_sublabel_clicked(event: InputEvent) -> void: if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: var mousePos: Vector2 = get_global_mouse_position() mousePos.x -= get_minimum_size().x / 2 if mousePos.x > global_position.x: (%Value as LineEdit).caret_column = (%Value as LineEdit).text.length() else: (%Value as LineEdit).caret_column = 0 (%Value as LineEdit).grab_focus() func _on_value_focus_exited() -> void: _on_value_text_submitted(%Value.text) $Value_Panel.add_theme_stylebox_override('panel', get_theme_stylebox('panel', 'DialogicEventEdit')) func _on_value_focus_entered() -> void: $Value_Panel.add_theme_stylebox_override('panel', get_theme_stylebox('focus', 'DialogicEventEdit')) %Value.select_all.call_deferred() #endregion