Merge pull request #55 from Nitwel/slider

Implement slider component
This commit is contained in:
Nitwel 2023-11-29 00:17:12 +01:00 committed by GitHub
commit a049f03b9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 268 additions and 119 deletions

View File

@ -4,11 +4,10 @@ extends StaticBody3D
@export var color_off = Color(0.23, 0.23, 0.23) @export var color_off = Color(0.23, 0.23, 0.23)
@export var color_on = Color(1.0, 0.85, 0.0) @export var color_on = Color(1.0, 0.85, 0.0)
@onready var shape = $CSGCombiner3D @onready var animation: AnimationPlayer = $AnimationPlayer
@onready var rod_top = $RodTop @onready var slider: Slider3D = $Slider
@onready var rod_bottom = $RodBottom
@onready var slider_knob = $Knob var state = true
var state = false
var brightness = 0 # 0-255 var brightness = 0 # 0-255
# Called when the node enters the scene tree for the first time. # Called when the node enters the scene tree for the first time.
@ -22,19 +21,30 @@ func _ready():
set_state(new_state["state"] == "on") set_state(new_state["state"] == "on")
) )
func set_state(state: bool, brightness = null): slider.on_value_changed.connect(func(new_value):
print("set_state ", state, brightness) var value = new_value / 100 * 255
self.state = state HomeApi.set_state(entity_id, "on" if state else "off", {"brightness": int(value)})
self.brightness = brightness set_state(state, value)
)
func set_state(new_state: bool, new_brightness = null):
if state == false && new_state == false:
return
state = new_state
brightness = new_brightness
if state: if state:
if brightness == null: if brightness == null:
shape.material_override.albedo_color = color_on animation.speed_scale = 1
animation.play_backwards("light")
else: else:
shape.material_override.albedo_color = color_off.lerp(color_on, brightness / 255.0) var duration = animation.get_animation("light").length
animation.speed_scale = 0
animation.seek(lerpf(0, duration, 1 - (brightness / 255.0)), true)
else: else:
shape.material_override.albedo_color = color_off animation.speed_scale = 1
animation.play("light")
func _on_click(event): func _on_click(event):
@ -45,31 +55,4 @@ func _on_click(event):
attributes["brightness"] = int(brightness) attributes["brightness"] = int(brightness)
HomeApi.set_state(entity_id, "on" if !state else "off", attributes) HomeApi.set_state(entity_id, "on" if !state else "off", attributes)
set_state(!state, brightness) set_state(!state, brightness)
else:
_on_clickable_on_click(event)
func _on_press_move(event):
if event.target != self:
_on_clickable_on_click(event)
func _on_request_completed():
pass
func _on_clickable_on_click(event):
var click_pos: Vector3 = to_local(event.ray.get_collision_point())
var vec_bottom_to_top = rod_top.position - rod_bottom.position
var length_click = click_pos.y - rod_bottom.position.y
var length_total = vec_bottom_to_top.y
var ratio = length_click / length_total
var new_pos = rod_bottom.position.lerp(rod_top.position, ratio)
slider_knob.position = new_pos
HomeApi.set_state(entity_id, "on" if state else "off", {"brightness": int(ratio * 255)})
set_state(state, ratio * 255)

View File

@ -1,28 +1,13 @@
[gd_scene load_steps=15 format=3 uid="uid://cw86rc42dv2d8"] [gd_scene load_steps=9 format=3 uid="uid://cw86rc42dv2d8"]
[ext_resource type="Script" path="res://content/entities/light/light.gd" id="1_ykxy3"] [ext_resource type="Script" path="res://content/entities/light/light.gd" id="1_ykxy3"]
[ext_resource type="Texture2D" uid="uid://br3p0c2foputg" path="res://assets/materials/swich_on.png" id="2_6gn2e"]
[ext_resource type="Texture2D" uid="uid://co2ishj2hx57p" path="res://assets/materials/switch_off.png" id="3_qlm62"]
[ext_resource type="Script" path="res://content/functions/movable.gd" id="4_4sfxb"] [ext_resource type="Script" path="res://content/functions/movable.gd" id="4_4sfxb"]
[ext_resource type="Material" uid="uid://vce66e7sbc3n" path="res://content/entities/light/light_on.tres" id="5_50gph"] [ext_resource type="Material" uid="uid://vce66e7sbc3n" path="res://content/entities/light/light_on.tres" id="5_50gph"]
[ext_resource type="PackedScene" uid="uid://pk5k1q8bx0rj" path="res://content/ui/components/slider/slider.tscn" id="6_mhjlm"]
[sub_resource type="SphereShape3D" id="SphereShape3D_ukj14"] [sub_resource type="SphereShape3D" id="SphereShape3D_ukj14"]
radius = 0.05 radius = 0.05
[sub_resource type="SpriteFrames" id="SpriteFrames_ldpuo"]
animations = [{
"frames": [{
"duration": 1.0,
"texture": ExtResource("2_6gn2e")
}, {
"duration": 1.0,
"texture": ExtResource("3_qlm62")
}],
"loop": true,
"name": &"default",
"speed": 5.0
}]
[sub_resource type="Animation" id="Animation_afofi"] [sub_resource type="Animation" id="Animation_afofi"]
length = 0.001 length = 0.001
tracks/0/type = "value" tracks/0/type = "value"
@ -37,18 +22,6 @@ tracks/0/keys = {
"update": 0, "update": 0,
"values": [Color(1, 0.85098, 0, 1)] "values": [Color(1, 0.85098, 0, 1)]
} }
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("Knob:position")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector3(0, 0.0492394, -0.0903599)]
}
[sub_resource type="Animation" id="Animation_7o31s"] [sub_resource type="Animation" id="Animation_7o31s"]
resource_name = "light" resource_name = "light"
@ -72,24 +45,6 @@ _data = {
"light": SubResource("Animation_7o31s") "light": SubResource("Animation_7o31s")
} }
[sub_resource type="CylinderMesh" id="CylinderMesh_j3pn3"]
top_radius = 0.004
bottom_radius = 0.004
height = 0.1
[sub_resource type="CylinderShape3D" id="CylinderShape3D_tysib"]
height = 0.1
radius = 0.01
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_13uml"]
albedo_color = Color(0.231373, 0.239216, 0.231373, 1)
[sub_resource type="CylinderMesh" id="CylinderMesh_s8215"]
material = SubResource("StandardMaterial3D_13uml")
top_radius = 0.006
bottom_radius = 0.006
height = 0.004
[node name="Light" type="StaticBody3D" groups=["entity"]] [node name="Light" type="StaticBody3D" groups=["entity"]]
collision_mask = 0 collision_mask = 0
script = ExtResource("1_ykxy3") script = ExtResource("1_ykxy3")
@ -97,12 +52,6 @@ script = ExtResource("1_ykxy3")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."] [node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("SphereShape3D_ukj14") shape = SubResource("SphereShape3D_ukj14")
[node name="Icon" type="AnimatedSprite3D" parent="."]
visible = false
pixel_size = 0.0005
billboard = 1
sprite_frames = SubResource("SpriteFrames_ldpuo")
[node name="Movable" type="Node" parent="."] [node name="Movable" type="Node" parent="."]
script = ExtResource("4_4sfxb") script = ExtResource("4_4sfxb")
@ -126,23 +75,9 @@ libraries = {
"": SubResource("AnimationLibrary_8a76q") "": SubResource("AnimationLibrary_8a76q")
} }
[node name="Rod" type="StaticBody3D" parent="."] [node name="Slider" parent="." instance=ExtResource("6_mhjlm")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.0903599) transform = Transform3D(-4.37114e-08, 1, 0, 1, 4.37114e-08, 8.74228e-08, 8.74228e-08, 3.82137e-15, -1, 0.00190757, -0.00579122, -0.0914348)
collision_mask = 0 max = 100.0
value = 100.0
[node name="MeshInstance3D" type="MeshInstance3D" parent="Rod"] step = 1.0
mesh = SubResource("CylinderMesh_j3pn3") size = Vector3(10, 0.4, 1)
skeleton = NodePath("../..")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Rod"]
shape = SubResource("CylinderShape3D_tysib")
[node name="Knob" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.0492394, -0.0903599)
mesh = SubResource("CylinderMesh_s8215")
[node name="RodBottom" type="Marker3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.0501399, -0.090675)
[node name="RodTop" type="Marker3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.0498752, -0.090942)

View File

@ -1,6 +1,7 @@
extends StaticBody3D extends StaticBody3D
@export var entity_id = "media_player.bedroomspeaker" @export var entity_id = "media_player.bedroomspeaker"
@export var image_width = 0.15
@onready var previous = $Previous @onready var previous = $Previous
@onready var next = $Next @onready var next = $Next
@ -66,9 +67,12 @@ func load_image(url: String):
var image = Image.new() var image = Image.new()
var error = image.load_jpg_from_buffer(result[3]) var error = image.load_jpg_from_buffer(result[3])
var pixel_size = image_width / image.get_size().x
if error != OK: if error != OK:
print("Error loading image: ", error) print("Error loading image: ", error)
return return
var texture = ImageTexture.create_from_image(image) var texture = ImageTexture.create_from_image(image)
logo.texture = texture logo.texture = texture
logo.pixel_size = pixel_size

View File

@ -49,7 +49,7 @@ outline_size = 4
horizontal_alignment = 0 horizontal_alignment = 0
[node name="Logo" type="Sprite3D" parent="PlayingInfo"] [node name="Logo" type="Sprite3D" parent="PlayingInfo"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.15, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.178254, 0)
pixel_size = 0.001 pixel_size = 0.001
[node name="HTTPRequest" type="HTTPRequest" parent="PlayingInfo"] [node name="HTTPRequest" type="HTTPRequest" parent="PlayingInfo"]

View File

@ -1,5 +1,6 @@
@tool @tool
extends StaticBody3D extends StaticBody3D
class_name Input3D
var text_handler = preload("res://content/ui/components/input/text_handler.gd").new() var text_handler = preload("res://content/ui/components/input/text_handler.gd").new()

View File

@ -0,0 +1,151 @@
@tool
extends Node3D
class_name Slider3D
@export var min: float = 0.0:
set(new_value):
min = new_value
if value < min: value = min
if !is_node_ready(): await ready
_update_slider()
@export var max: float = 1.0:
set(new_value):
max = new_value
if value > max: value = max
if !is_node_ready(): await ready
_update_slider()
@export var value: float = 0.2:
set(new_value):
value = roundi(clamp(new_value, min, max) / step) * step
if !is_node_ready(): await ready
label.text = str(value) + " " + label_unit
on_value_changed.emit(value)
_update_slider()
@export var step: float = 0.01
@export var show_label: bool = false:
set(value):
show_label = value
if !is_node_ready(): await ready
label.visible = show_label
@export var label_unit: String = "":
set(new_value):
label_unit = new_value
if !is_node_ready(): await ready
label.text = str(value) + " " + label_unit
@export var size: Vector3 = Vector3(0.2, 0.01, 0.02): # Warning, units are in cm
set(value):
size = value
if !is_node_ready(): await ready
_update_shape()
@export var cutout_border: float = 0.02:
set(value):
cutout_border = value
if !is_node_ready(): await ready
_update_shape()
@export var cutout_depth: float = 0.05:
set(value):
cutout_depth = value
if !is_node_ready(): await ready
_update_shape()
@onready var outside_rod: CSGBox3D = $Rod/Outside
@onready var cutout: CSGCombiner3D = $Rod/Cutout
@onready var cutout_box: CSGBox3D = $Rod/Cutout/Length
@onready var cutout_end_left: CSGCylinder3D = $Rod/Cutout/EndLeft
@onready var cutout_end_right: CSGCylinder3D = $Rod/Cutout/EndRight
@onready var label: Label3D = $Label
@onready var slider_knob: MeshInstance3D = $Knob
signal on_value_changed(value: float)
var move_plane: Plane
func _ready():
_update_shape()
move_plane = Plane(Vector3.UP, Vector3(0, size.y / 200, 0))
func _on_press_down(event: EventPointer):
_handle_press(event)
func _on_press_move(event: EventPointer):
_handle_press(event)
func _get_slider_min_max():
var cutout_radius = (size.z - cutout_border * 2) / 2
return Vector2(-size.x / 2 + cutout_border + cutout_radius, size.x / 2 - cutout_border - cutout_radius) / 100
func _handle_press(event: EventPointer):
var ray_pos = event.ray.global_position
var ray_dir = -event.ray.global_transform.basis.z
var local_pos = to_local(ray_pos)
var local_dir = global_transform.basis.inverse() * ray_dir
var click_pos = move_plane.intersects_ray(local_pos, local_dir)
if click_pos == null:
return
var min_max = _get_slider_min_max()
var pos_x = clamp(click_pos.x, min_max.x, min_max.y)
var click_percent = inverse_lerp(min_max.x, min_max.y, pos_x)
value = lerp(min, max, click_percent)
_update_slider()
func _update_slider():
var min_max = _get_slider_min_max()
var click_percent = inverse_lerp(min, max, value)
slider_knob.position.x = lerp(min_max.x, min_max.y, click_percent)
func _update_shape():
outside_rod.size = size
var cutout_width = size.z - cutout_border * 2
cutout_box.size = Vector3(
size.x - cutout_border * 2 - (cutout_width),
cutout_depth,
cutout_width
)
cutout.position = Vector3(
0,
size.y / 2 - cutout_depth / 2 + 0.001,
0
)
cutout_end_left.radius = cutout_box.size.z / 2
cutout_end_right.radius = cutout_box.size.z / 2
cutout_end_left.height = cutout_depth
cutout_end_right.height = cutout_depth
cutout_end_left.position = Vector3(
-cutout_box.size.x / 2,
0,
0
)
cutout_end_right.position = Vector3(
cutout_box.size.x / 2,
0,
0
)
label.position = Vector3(
size.x / 200 + 0.005,
0,
0
)

View File

@ -0,0 +1,79 @@
[gd_scene load_steps=6 format=3 uid="uid://pk5k1q8bx0rj"]
[ext_resource type="Script" path="res://content/ui/components/slider/slider.gd" id="1_ylune"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_eiwwn"]
albedo_color = Color(0.133333, 0.133333, 0.133333, 1)
[sub_resource type="CylinderMesh" id="CylinderMesh_77ny1"]
material = SubResource("StandardMaterial3D_eiwwn")
top_radius = 0.008
bottom_radius = 0.008
height = 0.003
[sub_resource type="CylinderMesh" id="CylinderMesh_v34nn"]
top_radius = 0.002
bottom_radius = 0.002
height = 0.005
[sub_resource type="BoxShape3D" id="BoxShape3D_h1mn1"]
size = Vector3(0.2, 0.004, 0.01)
[node name="Slider" type="Node3D"]
script = ExtResource("1_ylune")
size = Vector3(20, 0.4, 1)
cutout_border = 0.2
cutout_depth = 0.36
[node name="Rod" type="CSGCombiner3D" parent="."]
transform = Transform3D(0.01, 0, 0, 0, 0.01, 0, 0, 0, 0.01, 0, 0, 0)
snap = 0.0001
[node name="Outside" type="CSGBox3D" parent="Rod"]
snap = 0.0001
size = Vector3(20, 0.4, 1)
[node name="Cutout" type="CSGCombiner3D" parent="Rod"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.021, 0)
operation = 2
snap = 0.0001
[node name="Length" type="CSGBox3D" parent="Rod/Cutout"]
snap = 0.0001
size = Vector3(19, 0.36, 0.6)
[node name="EndRight" type="CSGCylinder3D" parent="Rod/Cutout"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 9.5, 0, 0)
snap = 0.0001
radius = 0.3
height = 0.36
sides = 36
[node name="EndLeft" type="CSGCylinder3D" parent="Rod/Cutout"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -9.5, 0, 0)
snap = 0.0001
radius = 0.3
height = 0.36
sides = 36
[node name="Knob" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.057, 0.00409426, 0)
mesh = SubResource("CylinderMesh_77ny1")
[node name="Pin" type="MeshInstance3D" parent="Knob"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.00353538, 0)
mesh = SubResource("CylinderMesh_v34nn")
[node name="CollisionBody" type="StaticBody3D" parent="."]
[node name="CollisionShape3D" type="CollisionShape3D" parent="CollisionBody"]
shape = SubResource("BoxShape3D_h1mn1")
[node name="Label" type="Label3D" parent="."]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.105, 0, 0)
visible = false
pixel_size = 0.001
text = "0.2"
font_size = 10
outline_size = 4
horizontal_alignment = 0

View File

@ -36,8 +36,6 @@ func is_focused(node: Node):
return _active_node == node return _active_node == node
func _handle_focus(target: Variant, event: EventBubble): func _handle_focus(target: Variant, event: EventBubble):
print("focus ", target, " ", target.get_groups(), " ", event)
if target != null: if target != null:
if target.is_in_group("ui_focus_skip"): if target.is_in_group("ui_focus_skip"):
return false return false
@ -58,8 +56,6 @@ func _handle_focus(target: Variant, event: EventBubble):
_active_node = target _active_node = target
print("focus", _active_node )
if _active_node != null && _active_node.has_method(FN_PREFIX + "focus_in"): if _active_node != null && _active_node.has_method(FN_PREFIX + "focus_in"):
_active_node.call(FN_PREFIX + "focus_in", event_focus) _active_node.call(FN_PREFIX + "focus_in", event_focus)
on_focus_in.emit(event_focus) on_focus_in.emit(event_focus)