mirror of
https://github.com/Relintai/gdfxr.git
synced 2024-11-14 04:57:26 +01:00
Add custom slider
This is a slider inspired by those used in Blender. The control is a draggable slider displaying the current value as its background. Clicking the control brings up a LineEdit for manual value input. When dragging the slider, hold CTRL to round to 0.1 increments or hold SHIFT to round to 0.01 increments. Holding both CTRL and SHIFT rounds to 0.001 increments.
This commit is contained in:
parent
2a73615082
commit
b5322f430f
@ -6,8 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Display current Slider value when hovered.
|
||||
- Hold Ctrl to snap Slider value to 0.01 increments.
|
||||
- Implement custom slider.
|
||||
|
||||
## [1.1.1] - 2022-07-30
|
||||
### Fixed
|
||||
|
203
addons/gdfxr/editor/EditSlider.gd
Normal file
203
addons/gdfxr/editor/EditSlider.gd
Normal file
@ -0,0 +1,203 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
signal value_changed(value)
|
||||
|
||||
export var value: float = 0.0 setget set_value
|
||||
export var min_value: float = 0.0
|
||||
export var max_value: float = 1.0
|
||||
|
||||
var _line_edit: LineEdit
|
||||
|
||||
var _stylebox_normal: StyleBox
|
||||
var _stylebox_hover: StyleBox
|
||||
var _stylebox_editing: StyleBox
|
||||
var _stylebox_value: StyleBox
|
||||
|
||||
var _line_edit_just_closed := false
|
||||
var _mouse_hovering := false
|
||||
var _is_editing := false
|
||||
|
||||
var _drag_start_position: Vector2
|
||||
var _drag_cancelled := true
|
||||
var _drag_dist := 0.0
|
||||
var _drag_start_factor: float
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
mouse_default_cursor_shape = Control.CURSOR_HSIZE
|
||||
rect_clip_content = true
|
||||
focus_mode = Control.FOCUS_ALL
|
||||
|
||||
var style := StyleBoxEmpty.new()
|
||||
style.content_margin_left = 8
|
||||
style.content_margin_right = 8
|
||||
|
||||
_line_edit = LineEdit.new()
|
||||
_line_edit.set_as_toplevel(true)
|
||||
_line_edit.visible = false
|
||||
_line_edit.add_stylebox_override("normal", style)
|
||||
_line_edit.add_stylebox_override("focus", StyleBoxEmpty.new())
|
||||
|
||||
var _ret: int
|
||||
_ret = _line_edit.connect("focus_exited", self, "_on_line_edit_focus_exited")
|
||||
_ret = _line_edit.connect("text_entered", self, "_on_line_edit_text_entered")
|
||||
_ret = _line_edit.connect("visibility_changed", self, "_on_line_edit_visibility_changed")
|
||||
|
||||
add_child(_line_edit)
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
var font := get_font("font", "LineEdit")
|
||||
var color := get_color("highlighted_font_color" if _mouse_hovering else "font_color", "Editor")
|
||||
var number_string := "%.3f" % value
|
||||
var number_size := font.get_string_size(number_string)
|
||||
var pos := Vector2(
|
||||
(rect_size.x - number_size.x) / 2,
|
||||
(rect_size.y - number_size.y) / 2 + font.get_ascent()
|
||||
)
|
||||
|
||||
var stylebox := _stylebox_editing if _is_editing else _stylebox_hover if _mouse_hovering else _stylebox_normal
|
||||
|
||||
if _line_edit.visible:
|
||||
draw_style_box(stylebox, Rect2(Vector2.ZERO, rect_size))
|
||||
else:
|
||||
var value_width := rect_size.x * ((value - min_value) / (max_value - min_value))
|
||||
draw_style_box(stylebox, Rect2(value_width, 0, rect_size.x - value_width, rect_size.y))
|
||||
draw_style_box(_stylebox_value, Rect2(0, 0, value_width, rect_size.y))
|
||||
draw_string(font, pos, number_string, color)
|
||||
|
||||
|
||||
func _get_minimum_size() -> Vector2:
|
||||
var ms := _stylebox_normal.get_minimum_size()
|
||||
ms.y += get_font("font", "LineEdit").get_height()
|
||||
return ms
|
||||
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
var mb := event as InputEventMouseButton
|
||||
if mb and mb.button_index == BUTTON_LEFT:
|
||||
if mb.pressed:
|
||||
_drag_prepare(mb)
|
||||
else:
|
||||
_drag_done()
|
||||
if _drag_cancelled:
|
||||
_show_text_edit()
|
||||
_drag_cancelled = true
|
||||
_is_editing = mb.pressed
|
||||
update()
|
||||
|
||||
var mm := event as InputEventMouseMotion
|
||||
if mm and mm.button_mask & BUTTON_MASK_LEFT:
|
||||
_drag_motion(mm)
|
||||
_drag_cancelled = false
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
match what:
|
||||
NOTIFICATION_ENTER_TREE, NOTIFICATION_THEME_CHANGED:
|
||||
_update_stylebox()
|
||||
|
||||
NOTIFICATION_MOUSE_ENTER:
|
||||
_mouse_hovering = true
|
||||
update()
|
||||
|
||||
NOTIFICATION_MOUSE_EXIT:
|
||||
_mouse_hovering = false
|
||||
update()
|
||||
|
||||
NOTIFICATION_FOCUS_ENTER:
|
||||
if (Input.is_action_pressed("ui_focus_next") or Input.is_action_pressed("ui_focus_prev")) and not _line_edit_just_closed:
|
||||
_show_text_edit()
|
||||
_line_edit_just_closed = false
|
||||
|
||||
|
||||
func set_value(v: float) -> void:
|
||||
if is_equal_approx(v, value):
|
||||
return
|
||||
value = v
|
||||
emit_signal("value_changed", value)
|
||||
update()
|
||||
|
||||
|
||||
func _update_stylebox() -> void:
|
||||
_stylebox_normal = get_stylebox("normal", "LineEdit")
|
||||
_stylebox_hover = StyleBoxFlat.new()
|
||||
_stylebox_hover.bg_color = get_color("highlight_color", "Editor")
|
||||
_stylebox_editing = StyleBoxFlat.new()
|
||||
_stylebox_editing.bg_color = get_color("dark_color_2", "Editor")
|
||||
_stylebox_value = StyleBoxFlat.new()
|
||||
_stylebox_value.bg_color = get_color("accent_color", "Editor") * Color(1, 1, 1, 0.4)
|
||||
|
||||
|
||||
func _drag_prepare(mouse: InputEventMouse) -> void:
|
||||
_drag_dist = 0.0
|
||||
_drag_start_factor = (value - min_value) / (max_value - min_value)
|
||||
_drag_start_position = mouse.global_position
|
||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
||||
|
||||
|
||||
func _drag_done() -> void:
|
||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||
|
||||
if _drag_cancelled:
|
||||
Input.warp_mouse_position(_drag_start_position)
|
||||
else:
|
||||
Input.warp_mouse_position(rect_global_position + rect_size * Vector2(
|
||||
(value - min_value) / (max_value - min_value),
|
||||
0.5
|
||||
))
|
||||
|
||||
|
||||
func _drag_motion(motion: InputEventMouseMotion) -> void:
|
||||
_drag_dist += motion.relative.x
|
||||
|
||||
var factor := _drag_start_factor + _drag_dist / rect_size.x
|
||||
if factor < 0 or 1 < factor:
|
||||
factor = clamp(factor, 0, 1)
|
||||
_drag_dist = (factor - _drag_start_factor) * rect_size.x
|
||||
|
||||
var v := factor * (max_value - min_value) + min_value
|
||||
var snap := motion.command or motion.shift
|
||||
if snap and not (is_equal_approx(v, min_value) or is_equal_approx(v, max_value)):
|
||||
if motion.shift and motion.command:
|
||||
v = round(v * 1000.0) * 0.001
|
||||
elif motion.shift:
|
||||
v = round(v * 100.0) * 0.01
|
||||
else:
|
||||
v = round(v * 10.0) * 0.1
|
||||
|
||||
set_value(clamp(v, min_value, max_value))
|
||||
|
||||
update()
|
||||
|
||||
|
||||
func _show_text_edit() -> void:
|
||||
var gr := get_global_rect()
|
||||
_line_edit.text = str(value)
|
||||
_line_edit.set_position(gr.position)
|
||||
_line_edit.set_size(gr.size)
|
||||
_line_edit.show_modal()
|
||||
_line_edit.select_all()
|
||||
_line_edit.grab_focus()
|
||||
_line_edit.focus_next = find_next_valid_focus().get_path()
|
||||
_line_edit.focus_previous = find_prev_valid_focus().get_path()
|
||||
|
||||
|
||||
func _on_line_edit_focus_exited():
|
||||
if _line_edit.get_menu().visible:
|
||||
return
|
||||
if _line_edit.text.is_valid_float():
|
||||
set_value(clamp(_line_edit.text.to_float(), min_value, max_value))
|
||||
if not _line_edit_just_closed:
|
||||
_line_edit.hide()
|
||||
update()
|
||||
|
||||
|
||||
func _on_line_edit_text_entered(_text: String):
|
||||
_line_edit.hide()
|
||||
|
||||
|
||||
func _on_line_edit_visibility_changed():
|
||||
if not _line_edit.visible:
|
||||
_line_edit_just_closed = true
|
@ -9,13 +9,10 @@ margin_bottom = 40.0
|
||||
rect_pivot_offset = Vector2( 41, -65 )
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
margin_top = 13.0
|
||||
margin_right = 133.0
|
||||
margin_right = 128.0
|
||||
margin_bottom = 27.0
|
||||
rect_min_size = Vector2( 100, 0 )
|
||||
size_flags_horizontal = 3
|
||||
@ -23,11 +20,11 @@ text = "Waveform"
|
||||
align = 2
|
||||
|
||||
[node name="OptionButton" type="OptionButton" parent="."]
|
||||
margin_left = 137.0
|
||||
margin_left = 132.0
|
||||
margin_top = 10.0
|
||||
margin_right = 237.0
|
||||
margin_bottom = 30.0
|
||||
rect_min_size = Vector2( 100, 0 )
|
||||
rect_min_size = Vector2( 105, 0 )
|
||||
size_flags_vertical = 4
|
||||
clip_text = true
|
||||
|
||||
@ -36,7 +33,9 @@ margin_left = 241.0
|
||||
margin_top = 9.0
|
||||
margin_right = 253.0
|
||||
margin_bottom = 31.0
|
||||
focus_mode = 0
|
||||
size_flags_vertical = 4
|
||||
enabled_focus_mode = 0
|
||||
script = ExtResource( 2 )
|
||||
icon_name = "ReloadSmall"
|
||||
|
||||
|
@ -8,21 +8,6 @@ export var label: String setget set_label
|
||||
export var parameter: String
|
||||
export var bipolar := false setget set_bipolar
|
||||
|
||||
var is_dragging_slider := false
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
match what:
|
||||
NOTIFICATION_ENTER_TREE, NOTIFICATION_THEME_CHANGED:
|
||||
var display: Label = $HSlider/Anchor/ValueDisplay
|
||||
var bg := StyleBoxFlat.new()
|
||||
bg.bg_color = get_color("dark_color_3", "Editor")
|
||||
bg.content_margin_bottom = 2
|
||||
bg.content_margin_top = 2
|
||||
bg.content_margin_left = 4
|
||||
bg.content_margin_right = 4
|
||||
display.add_stylebox_override("normal", bg)
|
||||
|
||||
|
||||
func set_label(v: String) -> void:
|
||||
label = v
|
||||
@ -49,45 +34,10 @@ func set_resetable(v: bool) -> void:
|
||||
$Reset.disabled = not v
|
||||
|
||||
|
||||
func _update_slider_step():
|
||||
$HSlider.step = 0.01 if is_dragging_slider and Input.is_key_pressed(KEY_CONTROL) else 0
|
||||
|
||||
|
||||
func _update_value_display():
|
||||
var anchor: Control = $HSlider/Anchor
|
||||
var display: Label = $HSlider/Anchor/ValueDisplay
|
||||
var slider: Slider = $HSlider
|
||||
var grabber_size := slider.get_icon("Grabber").get_size()
|
||||
|
||||
display.text = str(slider.value)
|
||||
anchor.rect_position.x = slider.ratio * (slider.rect_size.x - grabber_size.x) + grabber_size.x * 0.5
|
||||
|
||||
|
||||
func _on_HSlider_value_changed(value: float):
|
||||
_update_value_display()
|
||||
emit_signal("param_changed", parameter, value)
|
||||
|
||||
|
||||
func _on_HSlider_mouse_entered():
|
||||
$HSlider/Anchor.show()
|
||||
_update_value_display()
|
||||
|
||||
|
||||
func _on_HSlider_mouse_exited():
|
||||
$HSlider/Anchor.hide()
|
||||
|
||||
|
||||
func _on_HSlider_gui_input(event: InputEvent):
|
||||
var mb := event as InputEventMouseButton
|
||||
if mb and mb.button_index == BUTTON_LEFT:
|
||||
is_dragging_slider = mb.pressed
|
||||
_update_slider_step()
|
||||
|
||||
var ek := event as InputEventKey
|
||||
if ek and is_dragging_slider and ek.scancode == KEY_CONTROL:
|
||||
_update_slider_step()
|
||||
|
||||
|
||||
func _on_Reset_pressed():
|
||||
emit_signal("param_reset", parameter)
|
||||
|
||||
|
@ -2,13 +2,7 @@
|
||||
|
||||
[ext_resource path="res://addons/gdfxr/editor/ParamSlider.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/gdfxr/editor/EditorIconButton.gd" type="Script" id=2]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id=1]
|
||||
content_margin_left = 4.0
|
||||
content_margin_right = 4.0
|
||||
content_margin_top = 2.0
|
||||
content_margin_bottom = 2.0
|
||||
bg_color = Color( 0, 0, 0, 1 )
|
||||
[ext_resource path="res://addons/gdfxr/editor/EditSlider.gd" type="Script" id=3]
|
||||
|
||||
[node name="ParamSlider" type="HBoxContainer"]
|
||||
margin_right = 253.0
|
||||
@ -18,45 +12,31 @@ script = ExtResource( 1 )
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
margin_top = 13.0
|
||||
margin_right = 143.0
|
||||
margin_right = 128.0
|
||||
margin_bottom = 27.0
|
||||
rect_min_size = Vector2( 100, 0 )
|
||||
size_flags_horizontal = 3
|
||||
align = 2
|
||||
|
||||
[node name="HSlider" type="HSlider" parent="."]
|
||||
margin_left = 147.0
|
||||
margin_top = 12.0
|
||||
[node name="HSlider" type="Control" parent="."]
|
||||
margin_left = 132.0
|
||||
margin_right = 237.0
|
||||
margin_bottom = 28.0
|
||||
rect_min_size = Vector2( 90, 0 )
|
||||
hint_tooltip = "Hold Ctrl to snap to 0.01 increments."
|
||||
size_flags_vertical = 4
|
||||
max_value = 1.0
|
||||
step = 0.0
|
||||
|
||||
[node name="Anchor" type="Control" parent="HSlider"]
|
||||
visible = false
|
||||
|
||||
[node name="ValueDisplay" type="Label" parent="HSlider/Anchor"]
|
||||
margin_bottom = -2.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
custom_styles/normal = SubResource( 1 )
|
||||
align = 1
|
||||
valign = 1
|
||||
margin_bottom = 40.0
|
||||
rect_min_size = Vector2( 105, 0 )
|
||||
rect_clip_content = true
|
||||
focus_mode = 2
|
||||
mouse_default_cursor_shape = 10
|
||||
script = ExtResource( 3 )
|
||||
|
||||
[node name="Reset" type="ToolButton" parent="."]
|
||||
margin_left = 241.0
|
||||
margin_top = 9.0
|
||||
margin_right = 253.0
|
||||
margin_bottom = 31.0
|
||||
focus_mode = 0
|
||||
size_flags_vertical = 4
|
||||
script = ExtResource( 2 )
|
||||
icon_name = "ReloadSmall"
|
||||
|
||||
[connection signal="gui_input" from="HSlider" to="." method="_on_HSlider_gui_input"]
|
||||
[connection signal="mouse_entered" from="HSlider" to="." method="_on_HSlider_mouse_entered"]
|
||||
[connection signal="mouse_exited" from="HSlider" to="." method="_on_HSlider_mouse_exited"]
|
||||
[connection signal="value_changed" from="HSlider" to="." method="_on_HSlider_value_changed"]
|
||||
[connection signal="pressed" from="Reset" to="." method="_on_Reset_pressed"]
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 21 KiB |
Binary file not shown.
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 19 KiB |
Loading…
Reference in New Issue
Block a user