## Oversimplified value box class_name SlidingSpinBox extends LineEdit ## Triggered when a valid value is submitted signal value_changed(v : float) ## Number number of decimals const MAX_DECIMALS : int = 8 ## Minimum value @export var min_value : float = -1. ## Maximum value @export var max_value : float = 1. ## Value step @export var step : float = 0.1 ## Current value. Setting a value here doesn't trigger the signal @export var value : float = 0. : set(v): value = clampf(v, min_value, max_value) if is_node_ready(): queue_redraw() ## Send signal while dragging @export var emit_signal_on_drag : bool = false var expression : Expression var drag : bool func _ready() -> void: text_submitted.connect(_evaluate_text) selecting_enabled = false # Force to false to avoid event collision with dragging expression = Expression.new() _update_field() ## Evaluate submitted text (simplified version that evaluates expression) func _evaluate_text(submitted : String) -> void: var evaluated : String = submitted.replace(',', '.') evaluated = evaluated.replace(';', ','); var lang : String = TranslationServer.get_locale() evaluated = TranslationServer.parse_number(evaluated, lang) var err : Error = expression.parse(text) if err != OK: evaluated = TranslationServer.parse_number(submitted, lang) err = expression.parse(text) if err != OK: _update_field() return var v : Variant = expression.execute([], null, false, true) if v: _validate_value(v) ## Validate the value. It must range between min and max. func _validate_value(v : float, send_signal : bool = true) -> void: value = clampf(v, min_value, max_value) if send_signal: value_changed.emit(v) _update_field() ## Update the field content and format the text according to the step func _update_field() -> void: var sval : String = String.num(value, min(MAX_DECIMALS, step_decimals(step))) if localize_numeral_system: sval = TranslationServer.format_number(sval, TranslationServer.get_locale()) if text.casecmp_to(sval) == 0: return text = sval queue_redraw() func _gui_input(event: InputEvent) -> void: if event is InputEventMouseButton: var mev : InputEventMouseButton = event if mev.pressed and mev.button_index == MOUSE_BUTTON_LEFT: if mev.shift_pressed: _validate_value(_retrieve_value_from_position(mev.position.x)) drag = true accept_event() elif drag and not mev.pressed: _validate_value(_retrieve_value_from_position(mev.position.x)) drag = false elif event is InputEventMouseMotion and drag: drag = (event as InputEventMouseMotion).shift_pressed _validate_value(_retrieve_value_from_position((event as InputEventMouseMotion).position.x), emit_signal_on_drag) accept_event() mouse_default_cursor_shape = Control.CURSOR_HSIZE if drag else Control.CURSOR_IBEAM func _retrieve_value_from_position(pos : float) -> float: var rect : Rect2 = get_rect() var ratio : float = (pos - rect.position.x) / rect.size.x return min_value + ratio * (max_value - min_value) func _draw() -> void: var r : Rect2 = get_rect() var t : Theme = theme if theme else ThemeDB.get_default_theme() var stylebox : StyleBox = t.get_stylebox(&"normal" if editable else &"read_only", &"LineEdit") if stylebox: r.position += Vector2(stylebox.content_margin_left, stylebox.content_margin_top) r.size -= Vector2( stylebox.content_margin_left + stylebox.content_margin_right, stylebox.content_margin_top + stylebox.content_margin_bottom) RenderingServer.canvas_item_set_clip(get_canvas_item(), true) var progression : float = (value - min_value) * (r.size.x / (max_value - min_value)) r.size.x = progression var c : Color = t.get_color(&"font_uneditable_color", &"LineEdit") draw_rect(r, c)
or share this direct link: