aboutsummaryrefslogtreecommitdiff
path: root/game/addons/zylann.hterrain/tools/util/spin_slider.gd
diff options
context:
space:
mode:
Diffstat (limited to 'game/addons/zylann.hterrain/tools/util/spin_slider.gd')
-rw-r--r--game/addons/zylann.hterrain/tools/util/spin_slider.gd385
1 files changed, 385 insertions, 0 deletions
diff --git a/game/addons/zylann.hterrain/tools/util/spin_slider.gd b/game/addons/zylann.hterrain/tools/util/spin_slider.gd
new file mode 100644
index 0000000..fa690d5
--- /dev/null
+++ b/game/addons/zylann.hterrain/tools/util/spin_slider.gd
@@ -0,0 +1,385 @@
+@tool
+extends Control
+
+const FG_MARGIN = 2
+const MAX_DECIMALS_VISUAL = 3
+
+signal value_changed(value)
+
+
+var _value := 0.0
+@export var value: float:
+ get:
+ return _value
+ set(v):
+ set_value_no_notify(v)
+
+
+var _min_value := 0.0
+@export var min_value: float:
+ get:
+ return _min_value
+ set(v):
+ set_min_value(v)
+
+
+var _max_value := 100.0
+@export var max_value: float:
+ get:
+ return _max_value
+ set(v):
+ set_max_value(v)
+
+
+var _prefix := ""
+@export var prefix: String:
+ get:
+ return _prefix
+ set(v):
+ set_prefix(v)
+
+
+var _suffix := ""
+@export var suffix: String:
+ get:
+ return _suffix
+ set(v):
+ set_suffix(v)
+
+
+var _rounded := false
+@export var rounded: bool:
+ get:
+ return _rounded
+ set(v):
+ set_rounded(v)
+
+
+var _centered := true
+@export var centered: bool:
+ get:
+ return _centered
+ set(v):
+ set_centered(v)
+
+
+var _allow_greater := false
+@export var allow_greater: bool:
+ get:
+ return _allow_greater
+ set(v):
+ set_allow_greater(v)
+
+
+# There is still a limit when typing a larger value, but this one is to prevent software
+# crashes or freezes. The regular min and max values are for slider UX. Exceeding it should be
+# a corner case.
+var _greater_max_value := 10000.0
+@export var greater_max_value: float:
+ get:
+ return _greater_max_value
+ set(v):
+ set_greater_max_value(v)
+
+
+var _label : Label
+var _label2 : Label
+var _line_edit : LineEdit
+var _ignore_line_edit := false
+var _pressing := false
+var _grabbing := false
+var _press_pos := Vector2()
+
+
+func _init():
+ custom_minimum_size = Vector2(32, 28)
+
+ _label = Label.new()
+ _label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+ _label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
+ _label.clip_text = true
+ #_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ _label.anchor_top = 0
+ _label.anchor_left = 0
+ _label.anchor_right = 1
+ _label.anchor_bottom = 1
+ _label.mouse_filter = Control.MOUSE_FILTER_IGNORE
+ _label.add_theme_color_override("font_color_shadow", Color(0,0,0,0.5))
+ _label.add_theme_constant_override("shadow_offset_x", 1)
+ _label.add_theme_constant_override("shadow_offset_y", 1)
+ add_child(_label)
+
+ _label2 = Label.new()
+ _label2.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
+ _label2.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
+ _label2.clip_text = true
+ #_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ _label2.anchor_top = 0
+ _label2.anchor_left = 0
+ _label2.anchor_right = 1
+ _label2.anchor_bottom = 1
+ _label2.offset_left = 8
+ _label2.mouse_filter = Control.MOUSE_FILTER_IGNORE
+ _label2.add_theme_color_override("font_color_shadow", Color(0,0,0,0.5))
+ _label2.add_theme_constant_override("shadow_offset_x", 1)
+ _label2.add_theme_constant_override("shadow_offset_y", 1)
+ _label2.hide()
+ add_child(_label2)
+
+ _line_edit = LineEdit.new()
+ _line_edit.alignment = HORIZONTAL_ALIGNMENT_CENTER
+ _line_edit.anchor_top = 0
+ _line_edit.anchor_left = 0
+ _line_edit.anchor_right = 1
+ _line_edit.anchor_bottom = 1
+ _line_edit.gui_input.connect(_on_LineEdit_gui_input)
+ _line_edit.focus_exited.connect(_on_LineEdit_focus_exited)
+ _line_edit.text_submitted.connect(_on_LineEdit_text_submitted)
+ _line_edit.hide()
+ add_child(_line_edit)
+
+ mouse_default_cursor_shape = Control.CURSOR_HSIZE
+
+
+func _ready():
+ pass # Replace with function body.
+
+
+func set_centered(p_centered: bool):
+ _centered = p_centered
+ if _centered:
+ _label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
+ _label.offset_right = 0
+ _label2.hide()
+ else:
+ _label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
+ _label.offset_right = -8
+ _label2.show()
+ queue_redraw()
+
+
+func is_centered() -> bool:
+ return _centered
+
+
+func set_value_no_notify(v: float):
+ set_value(v, false, false)
+
+
+func set_value(v: float, notify_change: bool, use_slider_maximum: bool = false):
+ if _allow_greater and not use_slider_maximum:
+ v = clampf(v, _min_value, _greater_max_value)
+ else:
+ v = clampf(v, _min_value, _max_value)
+
+ if v != _value:
+ _value = v
+
+ queue_redraw()
+
+ if notify_change:
+ value_changed.emit(get_value())
+
+
+func get_value():
+ if _rounded:
+ return int(roundf(_value))
+ return _value
+
+
+func set_min_value(minv: float):
+ _min_value = minv
+ #queue_redraw()
+
+
+func get_min_value() -> float:
+ return _min_value
+
+
+func set_max_value(maxv: float):
+ _max_value = maxv
+ #queue_redraw()
+
+
+func get_max_value() -> float:
+ return _max_value
+
+
+func set_greater_max_value(gmax: float):
+ _greater_max_value = gmax
+
+
+func get_greater_max_value() -> float:
+ return _greater_max_value
+
+
+func set_rounded(b: bool):
+ _rounded = b
+ queue_redraw()
+
+
+func is_rounded() -> bool:
+ return _rounded
+
+
+func set_prefix(p_prefix: String):
+ _prefix = p_prefix
+ queue_redraw()
+
+
+func get_prefix() -> String:
+ return _prefix
+
+
+func set_suffix(p_suffix: String):
+ _suffix = p_suffix
+ queue_redraw()
+
+
+func get_suffix() -> String:
+ return _suffix
+
+
+func set_allow_greater(allow: bool):
+ _allow_greater = allow
+
+
+func is_allowing_greater() -> bool:
+ return _allow_greater
+
+
+func _set_from_pixel(px: float):
+ var r := (px - FG_MARGIN) / (size.x - FG_MARGIN * 2.0)
+ var v := _ratio_to_value(r)
+ set_value(v, true, true)
+
+
+func get_ratio() -> float:
+ return _value_to_ratio(get_value())
+
+
+func _ratio_to_value(r: float) -> float:
+ return r * (_max_value - _min_value) + _min_value
+
+
+func _value_to_ratio(v: float) -> float:
+ if absf(_max_value - _min_value) < 0.001:
+ return 0.0
+ return (v - _min_value) / (_max_value - _min_value)
+
+
+func _on_LineEdit_gui_input(event: InputEvent):
+ if event is InputEventKey:
+ if event.pressed:
+ if event.keycode == KEY_ESCAPE:
+ _ignore_line_edit = true
+ _hide_line_edit()
+ grab_focus()
+ _ignore_line_edit = false
+
+
+func _on_LineEdit_focus_exited():
+ if _ignore_line_edit:
+ return
+ _enter_text()
+
+
+func _on_LineEdit_text_submitted(text: String):
+ _enter_text()
+
+
+func _enter_text():
+ var s = _line_edit.text.strip_edges()
+ if s.is_valid_float():
+ var v := s.to_float()
+ if not _allow_greater:
+ v = minf(v, _max_value)
+ set_value(v, true, false)
+ _hide_line_edit()
+
+
+func _hide_line_edit():
+ _line_edit.hide()
+ _label.show()
+ queue_redraw()
+
+
+func _show_line_edit():
+ _line_edit.show()
+ _line_edit.text = str(get_value())
+ _line_edit.select_all()
+ _line_edit.grab_focus()
+ _label.hide()
+ queue_redraw()
+
+
+func _gui_input(event: InputEvent):
+ if event is InputEventMouseButton:
+ if event.pressed:
+ if event.button_index == MOUSE_BUTTON_LEFT:
+ _press_pos = event.position
+ _pressing = true
+ else:
+ if event.button_index == MOUSE_BUTTON_LEFT:
+ _pressing = false
+ if _grabbing:
+ _grabbing = false
+ _set_from_pixel(event.position.x)
+ else:
+ _show_line_edit()
+
+ elif event is InputEventMouseMotion:
+ if _pressing and _press_pos.distance_to(event.position) > 2.0:
+ _grabbing = true
+ if _grabbing:
+ _set_from_pixel(event.position.x)
+
+
+func _draw():
+ if _line_edit.visible:
+ return
+
+ #var grabber_width := 3
+ var background_v_margin := 0
+ var foreground_margin := FG_MARGIN
+ #var grabber_color := Color(0.8, 0.8, 0.8)
+ var interval_color := Color(0.4,0.4,0.4)
+ var background_color := Color(0.1, 0.1, 0.1)
+
+ var control_rect := Rect2(Vector2(), size)
+
+ var bg_rect := Rect2(
+ control_rect.position.x,
+ control_rect.position.y + background_v_margin,
+ control_rect.size.x,
+ control_rect.size.y - 2 * background_v_margin)
+ draw_rect(bg_rect, background_color)
+
+ var fg_rect := control_rect.grow(-foreground_margin)
+ # Clamping the ratio because the value can be allowed to exceed the slider's boundaries
+ var ratio := clampf(get_ratio(), 0.0, 1.0)
+ fg_rect.size.x *= ratio
+ draw_rect(fg_rect, interval_color)
+
+ var value_text := str(get_value())
+
+ var dot_pos := value_text.find(".")
+ if dot_pos != -1:
+ var decimal_count := len(value_text) - dot_pos
+ if decimal_count > MAX_DECIMALS_VISUAL:
+ value_text = value_text.substr(0, dot_pos + MAX_DECIMALS_VISUAL + 1)
+
+ if _centered:
+ var text := value_text
+ if _prefix != "":
+ text = str(_prefix, " ", text)
+ if _suffix != "":
+ text = str(text, " ", _suffix)
+ _label.text = text
+
+ else:
+ _label2.text = _prefix
+ var text := value_text
+ if _suffix != "":
+ text = str(text, " ", _suffix)
+ _label.text = text