diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cc2332..86f935b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/addons/gdfxr/editor/EditSlider.gd b/addons/gdfxr/editor/EditSlider.gd new file mode 100644 index 0000000..217a900 --- /dev/null +++ b/addons/gdfxr/editor/EditSlider.gd @@ -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 diff --git a/addons/gdfxr/editor/ParamOption.tscn b/addons/gdfxr/editor/ParamOption.tscn index 650087a..41452da 100644 --- a/addons/gdfxr/editor/ParamOption.tscn +++ b/addons/gdfxr/editor/ParamOption.tscn @@ -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" diff --git a/addons/gdfxr/editor/ParamSlider.gd b/addons/gdfxr/editor/ParamSlider.gd index 3f7ca4f..c88a258 100644 --- a/addons/gdfxr/editor/ParamSlider.gd +++ b/addons/gdfxr/editor/ParamSlider.gd @@ -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) diff --git a/addons/gdfxr/editor/ParamSlider.tscn b/addons/gdfxr/editor/ParamSlider.tscn index fd6cfea..33ba555 100644 --- a/addons/gdfxr/editor/ParamSlider.tscn +++ b/addons/gdfxr/editor/ParamSlider.tscn @@ -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"] diff --git a/screenshots/editor-zh_CN.png b/screenshots/editor-zh_CN.png index e708d5e..efda4f7 100644 Binary files a/screenshots/editor-zh_CN.png and b/screenshots/editor-zh_CN.png differ diff --git a/screenshots/editor.png b/screenshots/editor.png index b3d887b..a6bf1aa 100644 Binary files a/screenshots/editor.png and b/screenshots/editor.png differ