From d87e06a56109b499698413972e2675ef8176262d Mon Sep 17 00:00:00 2001 From: Nitwel Date: Tue, 21 Nov 2023 12:44:44 +0100 Subject: [PATCH 01/13] fix not fetching all tags --- .github/workflows/build-prod.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-prod.yml b/.github/workflows/build-prod.yml index ced0f5d..aadb5b6 100644 --- a/.github/workflows/build-prod.yml +++ b/.github/workflows/build-prod.yml @@ -29,6 +29,7 @@ jobs: uses: actions/checkout@v3.3.0 with: lfs: true + fetch-depth: 0 - name: Set up JDK 17 uses: actions/setup-java@v3 From 16201224ced29e12fec4f229d1786060414f19e2 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Tue, 21 Nov 2023 13:45:54 +0100 Subject: [PATCH 02/13] add release tag workflow --- .github/workflows/release.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..30f8d3e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,24 @@ +name: Create new Release + +on: + workflow_dispatch: + inputs: + tag: + description: "New tag name" + required: true + +jobs: + version: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + lfs: true + fetch-depth: 0 + - name: Create and push new Tag + run: | + git config user.name "Nitwel" + git config user.email "mail@nitwel.de" + + git tag ${{ github.event.inputs.tag }} + git push origin ${{ github.event.inputs.tag }} From a2bc1a5d37b2f282b7920667bef3cd7ca78b1c3d Mon Sep 17 00:00:00 2001 From: Nitwel Date: Wed, 22 Nov 2023 01:44:07 +0100 Subject: [PATCH 03/13] rework event system and add keyboard --- README.md | 10 ++ content/functions/clickable.gd | 36 +++--- content/functions/movable.gd | 6 +- content/main.tscn | 24 +--- content/raycast.gd | 121 ------------------ .../controller_left}/controller_left.gd | 6 +- .../controller_left}/controller_left.tscn | 7 +- content/system/raycast/raycast.gd | 94 ++++++++++++++ content/system/raycast/raycast.tscn | 16 +++ content/ui/components/button/button.gd | 44 +++---- content/ui/keyboard/keyboard.gd | 73 ++++++++--- content/ui/keyboard/keyboard.tscn | 29 ++++- lib/events/event.gd | 9 ++ lib/events/event_bubble.gd | 5 + lib/events/event_key.gd | 16 +++ lib/events/event_ray.gd | 6 + lib/events/event_with_modifiers.gd | 7 + lib/globals/event_system.gd | 55 ++++++++ lib/globals/events.gd | 15 --- lib/home_adapters/hass_ws/hass.gd | 6 + project.godot | 2 +- 21 files changed, 359 insertions(+), 228 deletions(-) delete mode 100644 content/raycast.gd rename content/{ => system/controller_left}/controller_left.gd (88%) rename content/{ => system/controller_left}/controller_left.tscn (91%) create mode 100644 content/system/raycast/raycast.gd create mode 100644 content/system/raycast/raycast.tscn create mode 100644 lib/events/event.gd create mode 100644 lib/events/event_bubble.gd create mode 100644 lib/events/event_key.gd create mode 100644 lib/events/event_ray.gd create mode 100644 lib/events/event_with_modifiers.gd create mode 100644 lib/globals/event_system.gd delete mode 100644 lib/globals/events.gd diff --git a/README.md b/README.md index 3f0158d..c4be6d2 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,10 @@ InteractionEvent { "ray": RayCast3D, # The ray-cast that triggered the event "target": Node3D, # The node that was hit by the ray-cast } + +KeyEvent { + "key": String, # The key that was pressed +} ``` | Function called | Args | Description | @@ -114,6 +118,12 @@ InteractionEvent { | `_on_grab_up` | `[event: InteractionEvent]` | The side grab button been released | | `_on_ray_enter` | `[event: InteractionEvent]` | The ray-cast enters the the collision body | | `_on_ray_leave` | `[event: InteractionEvent]` | The ray-cast leaves the the collision body | +| `_on_key_down` | `[event: KeyEvent]` | The ray-cast leaves the the collision body | +| `_on_key_up` | `[event: KeyEvent]` | The ray-cast leaves the the collision body | + +After considering using the build in godot event system, I've decided that it would be better to use a custom event system. +The reason being that we would have to check each tick if the event matches the desired one which seems very inefficient compared to using signals like the browser does. +Thus I've decided to use a custom event system that is similar to the one used in the browser. ### Functions diff --git a/content/functions/clickable.gd b/content/functions/clickable.gd index bbf1366..01dcab5 100644 --- a/content/functions/clickable.gd +++ b/content/functions/clickable.gd @@ -1,39 +1,39 @@ extends Function class_name Clickable -signal on_click(event: Dictionary) -signal on_press_down(event: Dictionary) -signal on_press_move(event: Dictionary) -signal on_press_up(event: Dictionary) -signal on_grab_down(event: Dictionary) -signal on_grab_move(event: Dictionary) -signal on_grab_up(event: Dictionary) -signal on_ray_enter(event: Dictionary) -signal on_ray_leave(event: Dictionary) +signal on_click(event: EventRay) +signal on_press_down(event: EventRay) +signal on_press_move(event: EventRay) +signal on_press_up(event: EventRay) +signal on_grab_down(event: EventRay) +signal on_grab_move(event: EventRay) +signal on_grab_up(event: EventRay) +signal on_ray_enter(event: EventRay) +signal on_ray_leave(event: EventRay) -func _on_click(event: Dictionary): +func _on_click(event: EventRay): on_click.emit(event) -func _on_press_down(event: Dictionary): +func _on_press_down(event: EventRay): on_press_down.emit(event) -func _on_press_move(event: Dictionary): +func _on_press_move(event: EventRay): on_press_move.emit(event) -func _on_press_up(event: Dictionary): +func _on_press_up(event: EventRay): on_press_up.emit(event) -func _on_grab_down(event: Dictionary): +func _on_grab_down(event: EventRay): on_grab_down.emit(event) -func _on_grab_move(event: Dictionary): +func _on_grab_move(event: EventRay): on_grab_move.emit(event) -func _on_grab_up(event: Dictionary): +func _on_grab_up(event: EventRay): on_grab_up.emit(event) -func _on_ray_enter(event: Dictionary): +func _on_ray_enter(event: EventRay): on_ray_enter.emit(event) -func _on_ray_leave(event: Dictionary): +func _on_ray_leave(event: EventRay): on_ray_leave.emit(event) \ No newline at end of file diff --git a/content/functions/movable.gd b/content/functions/movable.gd index e0b1cad..07f3bad 100644 --- a/content/functions/movable.gd +++ b/content/functions/movable.gd @@ -4,14 +4,14 @@ class_name Movable var hit_node := Node3D.new() -func _on_grab_down(event): +func _on_grab_down(event: EventRay): event.controller.add_child(hit_node) hit_node.global_transform = get_parent().global_transform -func _on_grab_move(event): +func _on_grab_move(_event: EventRay): get_parent().global_transform = hit_node.global_transform -func _on_grab_up(event): +func _on_grab_up(event: EventRay): event.controller.remove_child(hit_node) func _get_configuration_warnings() -> PackedStringArray: diff --git a/content/main.tscn b/content/main.tscn index 7609b70..381fae9 100644 --- a/content/main.tscn +++ b/content/main.tscn @@ -1,13 +1,13 @@ [gd_scene load_steps=13 format=3 uid="uid://eecv28y6jxk4"] [ext_resource type="PackedScene" uid="uid://clc5dre31iskm" path="res://addons/godot-xr-tools/xr/start_xr.tscn" id="1_i4c04"] -[ext_resource type="Script" path="res://content/raycast.gd" id="1_tsqxc"] [ext_resource type="Script" path="res://content/main.gd" id="1_uvrd4"] -[ext_resource type="PackedScene" uid="uid://b30w6tywfj4fp" path="res://content/controller_left.tscn" id="2_2lraw"] -[ext_resource type="Texture2D" uid="uid://bo55nohs0wsgf" path="res://assets/materials/pointer.png" id="4_wcfej"] +[ext_resource type="PackedScene" uid="uid://b30w6tywfj4fp" path="res://content/system/controller_left/controller_left.tscn" id="2_2lraw"] +[ext_resource type="PackedScene" uid="uid://d3f8glx1xgm5w" path="res://content/system/raycast/raycast.tscn" id="3_67lii"] [ext_resource type="PackedScene" uid="uid://ctltchlf2j2r4" path="res://addons/xr-simulator/XRSimulator.tscn" id="5_3qc8g"] [ext_resource type="Material" uid="uid://bf5ina366dwm6" path="res://assets/materials/sky.material" id="5_wgwf8"] [ext_resource type="PackedScene" uid="uid://83lb5p4e0qk0" path="res://content/scenes/house.tscn" id="8_qkrg7"] +[ext_resource type="PackedScene" uid="uid://lrehk38exd5n" path="res://content/ui/keyboard/keyboard.tscn" id="9_e5n3p"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_m58yb"] ao_enabled = true @@ -46,20 +46,7 @@ pose = &"aim" [node name="MeshInstance3D" type="MeshInstance3D" parent="XROrigin3D/XRControllerRight"] mesh = SubResource("BoxMesh_ir3co") -[node name="Raycast" type="Node3D" parent="XROrigin3D/XRControllerRight" node_paths=PackedStringArray("ray")] -script = ExtResource("1_tsqxc") -ray = NodePath("RayCast3D") - -[node name="RayCast3D" type="RayCast3D" parent="XROrigin3D/XRControllerRight/Raycast"] -transform = Transform3D(-4.62364e-10, 4.3714e-08, 0.999998, 0.999999, -4.37194e-08, 9.2768e-12, 4.37103e-08, 0.999999, -4.3714e-08, -0.000467122, 0.00228411, -0.0016689) -target_position = Vector3(0, -5, 0) - -[node name="Decal" type="Decal" parent="XROrigin3D/XRControllerRight/Raycast"] -transform = Transform3D(0.999999, -0.000567105, -2.5179e-05, -2.51789e-05, 4.39886e-08, -0.999999, 0.000567105, 1, 2.97064e-08, -0.000775784, -1.09076e-05, -2.46767) -size = Vector3(0.02, 4.91995, 0.02) -texture_albedo = ExtResource("4_wcfej") -upper_fade = 0.000985425 -lower_fade = 0.000919435 +[node name="Raycast" parent="XROrigin3D/XRControllerRight" instance=ExtResource("3_67lii")] [node name="StartXR" parent="." instance=ExtResource("1_i4c04")] enable_passthrough = true @@ -77,3 +64,6 @@ xr_origin = NodePath("../XROrigin3D") [node name="House" parent="." instance=ExtResource("8_qkrg7")] transform = Transform3D(0.404247, 0.000180645, 0.914648, 0.00017221, 0.999999, -0.000273614, -0.914648, 0.00026812, 0.404247, -0.343479, 0.000110551, 1.91547) visible = false + +[node name="Keyboard" parent="." instance=ExtResource("9_e5n3p")] +transform = Transform3D(0.5, -6.98186e-12, 0, 4.73988e-12, 0.5, 0, 0, 0, 0.5, -0.00874073, 0.253873, -0.357135) diff --git a/content/raycast.gd b/content/raycast.gd deleted file mode 100644 index 530e703..0000000 --- a/content/raycast.gd +++ /dev/null @@ -1,121 +0,0 @@ -extends Node3D - -@onready var _controller := XRHelpers.get_xr_controller(self) -@export var ray: RayCast3D -@export var timespan_click = 200.0 - -# Called when the node enters the scene tree for the first time. -func _ready(): - _controller.button_pressed.connect(_on_button_pressed) - _controller.button_released.connect(_on_button_released) - -var _last_collided: Object = null -var _is_pressed := false -var _is_grabbed := false -var _time_pressed := 0.0 -var _moved := false -var _click_point := Vector3.ZERO - -func _physics_process(delta): - _handle_enter_leave() - _handle_move() - -func _handle_move(): - var time_passed = Time.get_ticks_msec() - _time_pressed - if time_passed <= timespan_click || (_is_pressed == false && _is_grabbed == false): - return - - _moved = true - - if _is_pressed: - _call_fn(_last_collided, "_on_press_move") - - if _is_grabbed: - _call_fn(_last_collided, "_on_grab_move") - -func _handle_enter_leave(): - var collider = ray.get_collider() - - if collider == _last_collided || _is_grabbed || _is_pressed: - return - - _call_fn(collider, "_on_ray_enter") - _call_fn(_last_collided, "_on_ray_leave") - - _last_collided = collider - -func _on_button_pressed(button): - var collider = ray.get_collider() - - if collider == null: - return - - match button: - "trigger_click": - _is_pressed = true - _time_pressed = Time.get_ticks_msec() - _click_point = ray.get_collision_point() - _call_fn(collider, "_on_press_down") - "grip_click": - _is_grabbed = true - _click_point = ray.get_collision_point() - _call_fn(collider, "_on_grab_down") - -func _on_button_released(button): - if _last_collided == null: - return - - match button: - "trigger_click": - if _is_pressed: - if _moved == false: - _call_fn(_last_collided, "_on_click") - _call_fn(_last_collided, "_on_press_up") - _is_pressed = false - _last_collided = null - _moved = false - "grip_click": - if _is_grabbed: - _call_fn(_last_collided, "_on_grab_up") - _is_grabbed = false - _last_collided = null - _moved = false - -func _call_fn(collider: Variant, fn_name: String, node: Node3D = null, event = null): - if collider == null: - return - - if node == null: - node = collider - event = { - "controller": _controller, - "ray": ray, - "target": collider, - } - - if node.has_method(fn_name): - var result = node.call(fn_name, event) - - if result != null && result is Dictionary: - result.merge(event, true) - event = result - - if result != null && result is bool && result == false: - # Stop the event from bubbling up - return - - for child in node.get_children(): - if child is Function && child.has_method(fn_name): - child.call(fn_name, event) - - - var parent = node.get_parent() - - if parent != null && parent is Node3D: - _call_fn(collider, fn_name, parent, event) - else: - # in case the top has been reached - _call_global_fn(fn_name, event) - -func _call_global_fn(fn_name: String, event = null): - Events.get(fn_name.substr(1)).emit(event) diff --git a/content/controller_left.gd b/content/system/controller_left/controller_left.gd similarity index 88% rename from content/controller_left.gd rename to content/system/controller_left/controller_left.gd index 355bc3b..a8fa9d0 100644 --- a/content/controller_left.gd +++ b/content/system/controller_left/controller_left.gd @@ -34,11 +34,11 @@ var trash_bin_large: bool = false: func _ready(): trash_bin_visible = false - Events.on_grab_down.connect(func(event): + EventSystem.on_grab_down.connect(func(event: EventRay): trash_bin_visible = event.target.is_in_group("entity") ) - Events.on_grab_move.connect(func(event): + EventSystem.on_grab_move.connect(func(event): if !trash_bin_visible: return @@ -53,7 +53,7 @@ func _ready(): ) - Events.on_grab_up.connect(func(event): + EventSystem.on_grab_up.connect(func(_event: EventRay): if !trash_bin_visible: return diff --git a/content/controller_left.tscn b/content/system/controller_left/controller_left.tscn similarity index 91% rename from content/controller_left.tscn rename to content/system/controller_left/controller_left.tscn index de6a854..fb8344e 100644 --- a/content/controller_left.tscn +++ b/content/system/controller_left/controller_left.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=10 format=3 uid="uid://b30w6tywfj4fp"] +[gd_scene load_steps=11 format=3 uid="uid://b30w6tywfj4fp"] -[ext_resource type="Script" path="res://content/controller_left.gd" id="1_2j3qs"] +[ext_resource type="Script" path="res://content/system/controller_left/controller_left.gd" id="1_2j3qs"] [ext_resource type="PackedScene" uid="uid://c3kdssrmv84kv" path="res://content/ui/menu/menu.tscn" id="1_ccbr3"] [ext_resource type="PackedScene" uid="uid://cj42our8uhfq6" path="res://assets/models/trash_bin/trash_bin.gltf" id="3_m33ce"] +[ext_resource type="PackedScene" uid="uid://d3f8glx1xgm5w" path="res://content/system/raycast/raycast.tscn" id="4_n7lao"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_m58yb"] ao_enabled = true @@ -119,3 +120,5 @@ shape = SubResource("CylinderShape3D_x2eyr") libraries = { "": SubResource("AnimationLibrary_hkli8") } + +[node name="Raycast" parent="." instance=ExtResource("4_n7lao")] diff --git a/content/system/raycast/raycast.gd b/content/system/raycast/raycast.gd new file mode 100644 index 0000000..3b9d6ce --- /dev/null +++ b/content/system/raycast/raycast.gd @@ -0,0 +1,94 @@ +extends RayCast3D + +@export var is_right: bool = true + +var controller: XRController3D +var timespan_click = 200.0 + +var last_collided: Object = null +var is_pressed := false +var is_grabbed := false +var time_pressed := 0.0 +var moved := false +var click_point := Vector3.ZERO + +func _ready(): + controller = get_parent() + assert(controller is XRController3D, "XRController3D is not found in parent") + + controller.button_pressed.connect(_on_button_pressed) + controller.button_released.connect(_on_button_released) + +func _physics_process(_delta): + _handle_enter_leave() + _handle_move() + +func _handle_move(): + var time_passed = Time.get_ticks_msec() - time_pressed + if time_passed <= timespan_click || (is_pressed == false && is_grabbed == false): + return + + moved = true + + if is_pressed: + _emit_event("press_move", last_collided ) + + if is_grabbed: + _emit_event("grab_move", last_collided ) + +func _handle_enter_leave(): + var collider = get_collider() + + if collider == last_collided || is_grabbed || is_pressed: + return + + _emit_event("ray_enter", collider ) + _emit_event("ray_leave", last_collided ) + + last_collided = collider + +func _on_button_pressed(button: String): + var collider = get_collider() + + if collider == null: + return + + match button: + "trigger_click": + is_pressed = true + time_pressed = Time.get_ticks_msec() + click_point = get_collision_point() + _emit_event("press_down", collider ) + "grip_click": + is_grabbed = true + click_point = get_collision_point() + _emit_event("grab_down", collider ) + +func _on_button_released(button: String): + if last_collided == null: + return + + match button: + "trigger_click": + if is_pressed: + if moved == false: + _emit_event("click", last_collided ) + _emit_event("press_up", last_collided ) + is_pressed = false + last_collided = null + moved = false + "grip_click": + if is_grabbed: + _emit_event("grab_up", last_collided ) + is_grabbed = false + last_collided = null + moved = false + +func _emit_event(type: String, target: Object): + var event = EventRay.new() + event.controller = controller + event.target = target + event.ray = self + event.is_right_controller = is_right + + EventSystem.emit(type, event) \ No newline at end of file diff --git a/content/system/raycast/raycast.tscn b/content/system/raycast/raycast.tscn new file mode 100644 index 0000000..1d448b3 --- /dev/null +++ b/content/system/raycast/raycast.tscn @@ -0,0 +1,16 @@ +[gd_scene load_steps=3 format=3 uid="uid://d3f8glx1xgm5w"] + +[ext_resource type="Texture2D" uid="uid://bo55nohs0wsgf" path="res://assets/materials/pointer.png" id="1_2f2iv"] +[ext_resource type="Script" path="res://content/system/raycast/raycast.gd" id="1_gp8nv"] + +[node name="Raycast" type="RayCast3D"] +transform = Transform3D(0.999999, -1.39624e-11, 0, 9.48108e-12, 0.999999, 0, 0, 4.54747e-13, 0.999998, -0.000467122, 0.00228411, -0.0016689) +target_position = Vector3(0, 0, -5) +script = ExtResource("1_gp8nv") + +[node name="Decal" type="Decal" parent="."] +transform = Transform3D(1, -0.000567106, -2.5179e-05, -2.5179e-05, 4.39886e-08, -1, 0.000567106, 1, 2.97068e-08, -0.000308663, -0.00229502, -2.46601) +size = Vector3(0.02, 4.91995, 0.02) +texture_albedo = ExtResource("1_2f2iv") +upper_fade = 0.000985425 +lower_fade = 0.000919435 diff --git a/content/ui/components/button/button.gd b/content/ui/components/button/button.gd index 960eef3..dbcdc4d 100644 --- a/content/ui/components/button/button.gd +++ b/content/ui/components/button/button.gd @@ -1,12 +1,14 @@ extends StaticBody3D class_name Button3D +signal on_button_down() +signal on_button_up() + @export var toggleable: bool = false @export var disabled: bool = false @export var initial_active: bool = false var active: bool = false : set(value): - print("set active", value) animation_player.stop() if value == active: return @@ -17,8 +19,6 @@ var active: bool = false : animation_player.play("down") else: animation_player.play_backwards("down") - get: - return active @onready var animation_player: AnimationPlayer = $AnimationPlayer @onready var click_sound: AudioStreamPlayer = $ClickSound @@ -27,35 +27,31 @@ func _ready(): if initial_active: active = true -func _on_click(_event): +func _on_press_down(event): if disabled: - return false - - if !toggleable: + event.bubbling = false return - active = !active - AudioPlayer.play_effect("click") - - return { - "active": active - } - -func _on_press_down(_event): - if disabled: - return false - if toggleable: return + + active = true + on_button_down.emit() AudioPlayer.play_effect("click") - animation_player.play("down") + -func _on_press_up(_event): +func _on_press_up(event): if disabled: - return false - - if toggleable: + event.bubbling = false return - animation_player.play_backwards("down") + if toggleable: + active = !active + if active: + on_button_down.emit() + else: + on_button_up.emit() + else: + active = false + on_button_up.emit() diff --git a/content/ui/keyboard/keyboard.gd b/content/ui/keyboard/keyboard.gd index ca9b60e..f511f6f 100644 --- a/content/ui/keyboard/keyboard.gd +++ b/content/ui/keyboard/keyboard.gd @@ -1,51 +1,84 @@ -extends Node3D +@tool +extends StaticBody3D const button_scene = preload("res://content/ui/components/button/button.tscn") @onready var keys = $Keys @onready var caps_button = $Caps +@onready var backspace_button = $Backspace var key_list = [ - ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "~"], - ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "/"], - ["A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\\"], - ["Z", "X", "C", "V", "B", "N", "M", ",", ".", "-"] + [KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_ASCIITILDE], + [KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_SLASH], + [KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_COLON, KEY_BACKSLASH], + [KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_COMMA , KEY_PERIOD, KEY_MINUS] ] -var caps = false +var caps = false : + set(value): + caps = value + update_labels() func _ready(): for row in key_list: for key in row: - print(key) var button = create_key(key) keys.add_child(button) + button.on_button_down.connect(func(): + _emit_event("key_down", key) + ) + button.on_button_up.connect(func(): + _emit_event("key_up", key) + ) keys.columns = key_list[0].size() -func _on_click(event): - if event.target == caps_button: - caps = event.active - return + backspace_button.on_button_down.connect(func(): + _emit_event("key_down", KEY_BACKSPACE) + ) - var code = event.target.get_children()[event.target.get_child_count() - 1].text + backspace_button.on_button_up.connect(func(): + _emit_event("key_up", KEY_BACKSPACE) + ) - if caps: - code = code.to_upper() - else: - code = code.to_lower() + caps_button.on_button_down.connect(func(): + caps = true + _emit_event("key_down", KEY_CAPSLOCK) + ) - Events.typed.emit(code) - print(code) + caps_button.on_button_up.connect(func(): + caps = false + _emit_event("key_up", KEY_CAPSLOCK) + ) -func create_key(key: String): +func create_key(key: Key): var button = button_scene.instantiate() var label = Label3D.new() - label.text = key + label.text = EventKey.key_to_string(key, caps) label.pixel_size = 0.001 label.position = Vector3(0, 0.012, 0) label.rotate_x(deg_to_rad(-90)) + label.add_to_group("button_label") + + button.set_meta("key", key) button.add_child(label) return button + +func update_labels(): + for key_button in keys.get_children(): + var label = key_button.get_children()[key_button.get_children().size() - 1] + if caps: + label.text = label.text.to_upper() + else: + label.text = label.text.to_lower() + +func _emit_event(type: String, key: Key): + var event = EventKey.new() + event.key = key + event.shift_pressed = caps + + EventSystem.emit(type, event) + print("Emitting event: " + type + " " + EventKey.key_to_string(key, caps)) + diff --git a/content/ui/keyboard/keyboard.tscn b/content/ui/keyboard/keyboard.tscn index 721e0bd..0e7e822 100644 --- a/content/ui/keyboard/keyboard.tscn +++ b/content/ui/keyboard/keyboard.tscn @@ -1,14 +1,28 @@ -[gd_scene load_steps=4 format=3 uid="uid://lrehk38exd5n"] +[gd_scene load_steps=6 format=3 uid="uid://lrehk38exd5n"] [ext_resource type="Script" path="res://content/ui/keyboard/keyboard.gd" id="1_maojw"] [ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="1_xdpwr"] [ext_resource type="Script" path="res://content/ui/menu/grid.gd" id="3_mx544"] +[ext_resource type="Script" path="res://content/functions/movable.gd" id="4_86fct"] -[node name="Keyboard" type="Node3D"] +[sub_resource type="BoxShape3D" id="BoxShape3D_k5ib7"] +size = Vector3(0.744504, 0.0402036, 0.296009) + +[node name="Keyboard" type="StaticBody3D"] +transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0) script = ExtResource("1_maojw") +[node name="Backspace" parent="." instance=ExtResource("1_xdpwr")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.66, 0, 0.02) +metadata/key = 4194308 + +[node name="Label3D" type="Label3D" parent="Backspace"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.012, 0) +pixel_size = 0.001 +text = "back" + [node name="Caps" parent="." instance=ExtResource("1_xdpwr")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0668889, 0, 0.03) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.06, 0, 0.15) toggleable = true [node name="Label3D" type="Label3D" parent="Caps"] @@ -18,6 +32,13 @@ text = "caps" [node name="Keys" type="Node3D" parent="."] script = ExtResource("3_mx544") -columns = 1 +columns = 11 depth_gap = 0.06 size = Vector3(0.6, 1, 1) + +[node name="Movable" type="Node" parent="."] +script = ExtResource("4_86fct") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.276719, -0.025645, 0.0928761) +shape = SubResource("BoxShape3D_k5ib7") diff --git a/lib/events/event.gd b/lib/events/event.gd new file mode 100644 index 0000000..33779f8 --- /dev/null +++ b/lib/events/event.gd @@ -0,0 +1,9 @@ +extends Resource +class_name Event + +func merge(event: Event): + assert(self.is_class(event.get_class()), "Can only merge events of the same type.") + + for prop in event.get_property_list(): + if prop.name in self: + self.set(prop.name, event.get(prop.name)) diff --git a/lib/events/event_bubble.gd b/lib/events/event_bubble.gd new file mode 100644 index 0000000..a72ca89 --- /dev/null +++ b/lib/events/event_bubble.gd @@ -0,0 +1,5 @@ +extends Event +class_name EventBubble + +var bubbling := true +var target: Node \ No newline at end of file diff --git a/lib/events/event_key.gd b/lib/events/event_key.gd new file mode 100644 index 0000000..a7fe112 --- /dev/null +++ b/lib/events/event_key.gd @@ -0,0 +1,16 @@ +extends EventWithModifiers +class_name EventKey + +var key: Key +var echo: bool + +static func key_to_string(key: Key, caps: bool = false) -> String: + match key: + KEY_ASCIITILDE: return "~" + KEY_SLASH: return "/" + KEY_BACKSLASH: return "\\" + KEY_COLON: return ";" + KEY_COMMA: return "," + KEY_PERIOD: return "." + KEY_MINUS: return "-" + _: return OS.get_keycode_string(key).to_upper() if caps else OS.get_keycode_string(key).to_lower() \ No newline at end of file diff --git a/lib/events/event_ray.gd b/lib/events/event_ray.gd new file mode 100644 index 0000000..aa2765d --- /dev/null +++ b/lib/events/event_ray.gd @@ -0,0 +1,6 @@ +extends EventBubble +class_name EventRay + +var controller: XRController3D +var is_right_controller: bool +var ray: RayCast3D \ No newline at end of file diff --git a/lib/events/event_with_modifiers.gd b/lib/events/event_with_modifiers.gd new file mode 100644 index 0000000..14a7389 --- /dev/null +++ b/lib/events/event_with_modifiers.gd @@ -0,0 +1,7 @@ +extends Event +class_name EventWithModifiers + +var alt_pressed := false +var shift_pressed := false +var control_pressed := false +var meta_pressed := false \ No newline at end of file diff --git a/lib/globals/event_system.gd b/lib/globals/event_system.gd new file mode 100644 index 0000000..8e3d9e6 --- /dev/null +++ b/lib/globals/event_system.gd @@ -0,0 +1,55 @@ +extends Node + +const FN_PREFIX = "_on_" +const SIGNAL_PREFIX = "on_" + +# Interaction Events +signal on_click(event: EventRay) +signal on_press_down(event: EventRay) +signal on_press_move(event: EventRay) +signal on_press_up(event: EventRay) +signal on_grab_down(event: EventRay) +signal on_grab_move(event: EventRay) +signal on_grab_up(event: EventRay) +signal on_ray_enter(event: EventRay) +signal on_ray_leave(event: EventRay) + +signal on_key_down(event: EventKey) +signal on_key_up(event: EventKey) + +func emit(type: String, event: Event): + if event is EventBubble: + _bubble_call(type, event.target, event) + else: + _root_call(type, event) + + +func _bubble_call(type: String, target: Variant, event: EventBubble): + if target == null: + return + + if target.has_method(FN_PREFIX + type): + var updated_event = target.call(FN_PREFIX + type, event) + + if updated_event is EventBubble: + updated_event.merge(event) + event = updated_event + + if event.bubbling == false: + return + + for child in target.get_children(): + if child is Function && child.has_method(FN_PREFIX + type): + child.call(FN_PREFIX + type, event) + + var parent = target.get_parent() + + if parent != null && parent is Node: + _bubble_call(type, parent, event) + else: + # in case the top has been reached + _root_call(type, event) + + +func _root_call(type: String, event: Event): + get(SIGNAL_PREFIX + type).emit(event) diff --git a/lib/globals/events.gd b/lib/globals/events.gd deleted file mode 100644 index 6fa7e02..0000000 --- a/lib/globals/events.gd +++ /dev/null @@ -1,15 +0,0 @@ -# Global event bus -extends Node - -# Interaction Events -signal on_click(event: Dictionary) -signal on_press_down(event: Dictionary) -signal on_press_move(event: Dictionary) -signal on_press_up(event: Dictionary) -signal on_grab_down(event: Dictionary) -signal on_grab_move(event: Dictionary) -signal on_grab_up(event: Dictionary) -signal on_ray_enter(event: Dictionary) -signal on_ray_leave(event: Dictionary) - -signal typed(key: String) \ No newline at end of file diff --git a/lib/home_adapters/hass_ws/hass.gd b/lib/home_adapters/hass_ws/hass.gd index 7479829..0a413b3 100644 --- a/lib/home_adapters/hass_ws/hass.gd +++ b/lib/home_adapters/hass_ws/hass.gd @@ -16,6 +16,7 @@ var authenticated := false var loading := true var id := 1 var entities: Dictionary = {} +var retries := 5 var entitiy_callbacks := CallbackMap.new() var packet_callbacks := CallbackMap.new() @@ -31,6 +32,11 @@ func _init(url := self.url, token := self.token): connect_ws() func connect_ws(): + retries -= 1 + if retries < 0: + print("Failed to connect to %s" % self.url) + return + print("Connecting to %s" % self.url) socket.connect_to_url(self.url) set_process(true) diff --git a/project.godot b/project.godot index 73ec25b..5bcc1f1 100644 --- a/project.godot +++ b/project.godot @@ -21,7 +21,7 @@ XRToolsUserSettings="*res://addons/godot-xr-tools/user_settings/user_settings.gd Request="*res://lib/globals/request.gd" HomeAdapters="*res://lib/globals/home_adapters.gd" AudioPlayer="*res://lib/globals/audio_player.gd" -Events="*res://lib/globals/events.gd" +EventSystem="*res://lib/globals/event_system.gd" [editor_plugins] From 222b0feae7b443741e2633d7b15bed63f72f7b89 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Wed, 22 Nov 2023 16:43:06 +0100 Subject: [PATCH 04/13] remove privacy policy, update logo --- assets/design.afdesign | 4 +- assets/logo.png | 4 +- assets/meta/Hero-Cover.png | 4 +- privacy_policy.html | 145 ------------------------------------- 4 files changed, 6 insertions(+), 151 deletions(-) delete mode 100644 privacy_policy.html diff --git a/assets/design.afdesign b/assets/design.afdesign index d8ce1f5..0b5c61b 100644 --- a/assets/design.afdesign +++ b/assets/design.afdesign @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8bc1c66cc868d68efe828c304c6ed348136cdfd8b814fe0f243954f328648997 -size 3586528 +oid sha256:84daccd9540dfcf964fc4ec224ed88238f3a9fac55a30d1f48161d1ede0a7907 +size 2804209 diff --git a/assets/logo.png b/assets/logo.png index e7a0b12..039f438 100644 --- a/assets/logo.png +++ b/assets/logo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f10630e063d8d85238a4487595ec7869c79798ae7547ea0f636d6c78e0dc178 -size 85601 +oid sha256:fc77fb979fd1cec813e7bf6002ad05da443097ca5e04712f36f65a1dbc17cef2 +size 85732 diff --git a/assets/meta/Hero-Cover.png b/assets/meta/Hero-Cover.png index 271c8a0..af382e0 100644 --- a/assets/meta/Hero-Cover.png +++ b/assets/meta/Hero-Cover.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:451fcd3ec43720477df80b56031118ea0b947cfa32198bf47ef095b53beadf95 -size 1656729 +oid sha256:8da6b8e647a21f76a72096dad5eabd775f668ab0e45bdf8321633d99f894b175 +size 1656450 diff --git a/privacy_policy.html b/privacy_policy.html deleted file mode 100644 index 630faef..0000000 --- a/privacy_policy.html +++ /dev/null @@ -1,145 +0,0 @@ -

Privacy Policy

-

Last updated: November 21, 2023

-

- This Privacy Policy describes Our policies and procedures on the collection, - use and disclosure of Your information when You use the Service and tells - You about Your privacy rights and how the law protects You. -

-

- We use Your Personal data to provide and improve the Service. By using the - Service, You agree to the collection and use of information in accordance - with this Privacy Policy. This Privacy Policy has been created with the help - of the - Privacy Policy Generator. -

-

Interpretation and Definitions

-

Interpretation

-

- The words of which the initial letter is capitalized have meanings defined - under the following conditions. The following definitions shall have the - same meaning regardless of whether they appear in singular or in plural. -

-

Definitions

-

For the purposes of this Privacy Policy:

-
    -
  • -

    - Account means a unique account created for You to - access our Service or parts of our Service. -

    -
  • -
  • -

    - Affiliate means an entity that controls, is - controlled by or is under common control with a party, where - "control" means ownership of 50% or more of the shares, - equity interest or other securities entitled to vote for election of - directors or other managing authority. -

    -
  • -
  • -

    - Application refers to Immersive Home, the software - program provided by the Company. -

    -
  • -
  • -

    - Company (referred to as either "the - Company", "We", "Us" or "Our" in - this Agreement) refers to Immersive Home. -

    -
  • -
  • -

    Country refers to: Sachsen, Germany

    -
  • -
  • -

    - Device means any device that can access the Service - such as a computer, a cellphone or a digital tablet. -

    -
  • -
  • -

    - Personal Data is any information that relates to an - identified or identifiable individual. -

    -
  • -
  • -

    Service refers to the Application.

    -
  • -
  • -

    - Service Provider means any natural or legal person - who processes the data on behalf of the Company. It refers to - third-party companies or individuals employed by the Company to - facilitate the Service, to provide the Service on behalf of the - Company, to perform services related to the Service or to assist the - Company in analyzing how the Service is used. -

    -
  • -
  • -

    - Usage Data refers to data collected automatically, - either generated by the use of the Service or from the Service - infrastructure itself (for example, the duration of a page visit). -

    -
  • -
  • -

    - You means the individual accessing or using the - Service, or the company, or other legal entity on behalf of which - such individual is accessing or using the Service, as applicable. -

    -
  • -
-

Collecting and Using Your Personal Data

-

Types of Data Collected

-

- We do not collect any data about you under any circumstances. -

-

Links to Other Websites

-

- Our Service may contain links to other websites that are not operated by Us. - If You click on a third party link, You will be directed to that third - party's site. We strongly advise You to review the Privacy Policy of every - site You visit. -

-

- We have no control over and assume no responsibility for the content, - privacy policies or practices of any third party sites or services. -

-

Changes to this Privacy Policy

-

- We may update Our Privacy Policy from time to time. We will notify You of - any changes by posting the new Privacy Policy on this page. -

-

- We will let You know via email and/or a prominent notice on Our Service, - prior to the change becoming effective and update the "Last - updated" date at the top of this Privacy Policy. -

-

- You are advised to review this Privacy Policy periodically for any changes. - Changes to this Privacy Policy are effective when they are posted on this - page. -

-

Contact Us

-

If you have any questions about this Privacy Policy, You can contact us:

- From d2a8e927c9859017efe5be3f06c4ce9949c138b0 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Wed, 22 Nov 2023 17:05:34 +0100 Subject: [PATCH 05/13] move keyboard and fix signal tool error --- content/main.tscn | 2 +- content/{ui => system}/keyboard/keyboard.gd | 1 + content/{ui => system}/keyboard/keyboard.tscn | 2 +- content/ui/components/button/button.gd | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) rename content/{ui => system}/keyboard/keyboard.gd (99%) rename content/{ui => system}/keyboard/keyboard.tscn (94%) diff --git a/content/main.tscn b/content/main.tscn index 381fae9..0832e3b 100644 --- a/content/main.tscn +++ b/content/main.tscn @@ -7,7 +7,7 @@ [ext_resource type="PackedScene" uid="uid://ctltchlf2j2r4" path="res://addons/xr-simulator/XRSimulator.tscn" id="5_3qc8g"] [ext_resource type="Material" uid="uid://bf5ina366dwm6" path="res://assets/materials/sky.material" id="5_wgwf8"] [ext_resource type="PackedScene" uid="uid://83lb5p4e0qk0" path="res://content/scenes/house.tscn" id="8_qkrg7"] -[ext_resource type="PackedScene" uid="uid://lrehk38exd5n" path="res://content/ui/keyboard/keyboard.tscn" id="9_e5n3p"] +[ext_resource type="PackedScene" uid="uid://lrehk38exd5n" path="res://content/system/keyboard/keyboard.tscn" id="9_e5n3p"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_m58yb"] ao_enabled = true diff --git a/content/ui/keyboard/keyboard.gd b/content/system/keyboard/keyboard.gd similarity index 99% rename from content/ui/keyboard/keyboard.gd rename to content/system/keyboard/keyboard.gd index f511f6f..e74f8e0 100644 --- a/content/ui/keyboard/keyboard.gd +++ b/content/system/keyboard/keyboard.gd @@ -23,6 +23,7 @@ func _ready(): for key in row: var button = create_key(key) keys.add_child(button) + button.on_button_down.connect(func(): _emit_event("key_down", key) ) diff --git a/content/ui/keyboard/keyboard.tscn b/content/system/keyboard/keyboard.tscn similarity index 94% rename from content/ui/keyboard/keyboard.tscn rename to content/system/keyboard/keyboard.tscn index 0e7e822..90b304f 100644 --- a/content/ui/keyboard/keyboard.tscn +++ b/content/system/keyboard/keyboard.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=6 format=3 uid="uid://lrehk38exd5n"] -[ext_resource type="Script" path="res://content/ui/keyboard/keyboard.gd" id="1_maojw"] +[ext_resource type="Script" path="res://content/system/keyboard/keyboard.gd" id="1_maojw"] [ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="1_xdpwr"] [ext_resource type="Script" path="res://content/ui/menu/grid.gd" id="3_mx544"] [ext_resource type="Script" path="res://content/functions/movable.gd" id="4_86fct"] diff --git a/content/ui/components/button/button.gd b/content/ui/components/button/button.gd index dbcdc4d..f12787d 100644 --- a/content/ui/components/button/button.gd +++ b/content/ui/components/button/button.gd @@ -1,3 +1,4 @@ +@tool extends StaticBody3D class_name Button3D @@ -21,7 +22,6 @@ var active: bool = false : animation_player.play_backwards("down") @onready var animation_player: AnimationPlayer = $AnimationPlayer -@onready var click_sound: AudioStreamPlayer = $ClickSound func _ready(): if initial_active: From 198dc00aebb61de7776d1e0e08b398038c4b9531 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Wed, 22 Nov 2023 17:07:28 +0100 Subject: [PATCH 06/13] move keyboard and fix signal tool error --- content/main.tscn | 2 +- content/{ui => system}/keyboard/keyboard.gd | 7 +++++++ content/{ui => system}/keyboard/keyboard.tscn | 2 +- content/ui/components/button/button.gd | 1 - 4 files changed, 9 insertions(+), 3 deletions(-) rename content/{ui => system}/keyboard/keyboard.gd (96%) rename content/{ui => system}/keyboard/keyboard.tscn (94%) diff --git a/content/main.tscn b/content/main.tscn index 381fae9..0832e3b 100644 --- a/content/main.tscn +++ b/content/main.tscn @@ -7,7 +7,7 @@ [ext_resource type="PackedScene" uid="uid://ctltchlf2j2r4" path="res://addons/xr-simulator/XRSimulator.tscn" id="5_3qc8g"] [ext_resource type="Material" uid="uid://bf5ina366dwm6" path="res://assets/materials/sky.material" id="5_wgwf8"] [ext_resource type="PackedScene" uid="uid://83lb5p4e0qk0" path="res://content/scenes/house.tscn" id="8_qkrg7"] -[ext_resource type="PackedScene" uid="uid://lrehk38exd5n" path="res://content/ui/keyboard/keyboard.tscn" id="9_e5n3p"] +[ext_resource type="PackedScene" uid="uid://lrehk38exd5n" path="res://content/system/keyboard/keyboard.tscn" id="9_e5n3p"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_m58yb"] ao_enabled = true diff --git a/content/ui/keyboard/keyboard.gd b/content/system/keyboard/keyboard.gd similarity index 96% rename from content/ui/keyboard/keyboard.gd rename to content/system/keyboard/keyboard.gd index f511f6f..584e526 100644 --- a/content/ui/keyboard/keyboard.gd +++ b/content/system/keyboard/keyboard.gd @@ -23,6 +23,10 @@ func _ready(): for key in row: var button = create_key(key) keys.add_child(button) + + if Engine.is_editor_hint(): + continue + button.on_button_down.connect(func(): _emit_event("key_down", key) ) @@ -32,6 +36,9 @@ func _ready(): keys.columns = key_list[0].size() + if Engine.is_editor_hint(): + return + backspace_button.on_button_down.connect(func(): _emit_event("key_down", KEY_BACKSPACE) ) diff --git a/content/ui/keyboard/keyboard.tscn b/content/system/keyboard/keyboard.tscn similarity index 94% rename from content/ui/keyboard/keyboard.tscn rename to content/system/keyboard/keyboard.tscn index 0e7e822..90b304f 100644 --- a/content/ui/keyboard/keyboard.tscn +++ b/content/system/keyboard/keyboard.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=6 format=3 uid="uid://lrehk38exd5n"] -[ext_resource type="Script" path="res://content/ui/keyboard/keyboard.gd" id="1_maojw"] +[ext_resource type="Script" path="res://content/system/keyboard/keyboard.gd" id="1_maojw"] [ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="1_xdpwr"] [ext_resource type="Script" path="res://content/ui/menu/grid.gd" id="3_mx544"] [ext_resource type="Script" path="res://content/functions/movable.gd" id="4_86fct"] diff --git a/content/ui/components/button/button.gd b/content/ui/components/button/button.gd index dbcdc4d..6f0562c 100644 --- a/content/ui/components/button/button.gd +++ b/content/ui/components/button/button.gd @@ -21,7 +21,6 @@ var active: bool = false : animation_player.play_backwards("down") @onready var animation_player: AnimationPlayer = $AnimationPlayer -@onready var click_sound: AudioStreamPlayer = $ClickSound func _ready(): if initial_active: From 93c82f17909ba1f54eeafc4531dd6f5359f0ecc8 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Wed, 22 Nov 2023 17:35:54 +0100 Subject: [PATCH 07/13] add focus event --- README.md | 43 +++++++++++------------- content/ui/components/button/button.tscn | 2 +- lib/events/event_focus.gd | 5 +++ lib/globals/event_system.gd | 29 ++++++++++++++-- 4 files changed, 53 insertions(+), 26 deletions(-) create mode 100644 lib/events/event_focus.gd diff --git a/README.md b/README.md index c4be6d2..61b6c7b 100644 --- a/README.md +++ b/README.md @@ -95,36 +95,33 @@ In case that an event of a specific node has to be reacted on, use the `Clickabl It is also possible to bubble up information by returning a dictionary from a function like `_on_click`. -```python -InteractionEvent { - "controller": XRController3D, # The controller that triggered the event - "ray": RayCast3D, # The ray-cast that triggered the event - "target": Node3D, # The node that was hit by the ray-cast -} - -KeyEvent { - "key": String, # The key that was pressed -} -``` - | Function called | Args | Description | | -- | -- | -- | -| `_on_click` | `[event: InteractionEvent]` | The back trigger button has been pressed and released | -| `_on_press_down` | `[event: InteractionEvent]` | The back trigger button has been pressed down | -| `_on_press_move` | `[event: InteractionEvent]` | The back trigger button has been moved while pressed down | -| `_on_press_up` | `[event: InteractionEvent]` | The back trigger button has been released | -| `_on_grab_down` | `[event: InteractionEvent]` | The side grab button been pressed down | -| `_on_grab_move` | `[event: InteractionEvent]` | The side grab button been pressed down | -| `_on_grab_up` | `[event: InteractionEvent]` | The side grab button been released | -| `_on_ray_enter` | `[event: InteractionEvent]` | The ray-cast enters the the collision body | -| `_on_ray_leave` | `[event: InteractionEvent]` | The ray-cast leaves the the collision body | -| `_on_key_down` | `[event: KeyEvent]` | The ray-cast leaves the the collision body | -| `_on_key_up` | `[event: KeyEvent]` | The ray-cast leaves the the collision body | +| `_on_click` | `[event: EventRay]` | The back trigger button has been pressed and released | +| `_on_press_down` | `[event: EventRay]` | The back trigger button has been pressed down | +| `_on_press_move` | `[event: EventRay]` | The back trigger button has been moved while pressed down | +| `_on_press_up` | `[event: EventRay]` | The back trigger button has been released | +| `_on_grab_down` | `[event: EventRay]` | The side grab button been pressed down | +| `_on_grab_move` | `[event: EventRay]` | The side grab button been pressed down | +| `_on_grab_up` | `[event: EventRay]` | The side grab button been released | +| `_on_ray_enter` | `[event: EventRay]` | The ray-cast enters the the collision body | +| `_on_ray_leave` | `[event: EventRay]` | The ray-cast leaves the the collision body | +| `_on_key_down` | `[event: EventKey]` | The ray-cast leaves the the collision body | +| `_on_key_up` | `[event: EventKey]` | The ray-cast leaves the the collision body | +| `_on_focus_in` | `[event: EventFocus]` | The node is got focused | +| `_on_focus_out` | `[event: EventFocus]` | The node lost focus | After considering using the build in godot event system, I've decided that it would be better to use a custom event system. The reason being that we would have to check each tick if the event matches the desired one which seems very inefficient compared to using signals like the browser does. Thus I've decided to use a custom event system that is similar to the one used in the browser. +### UI Groups + +| Group | Description | +| -- | -- | +| `ui_focus` | The element can be focused | + + ### Functions In order to implement generic features, a set of functions is available to be used in the project. diff --git a/content/ui/components/button/button.tscn b/content/ui/components/button/button.tscn index 11dae36..6b1eff7 100644 --- a/content/ui/components/button/button.tscn +++ b/content/ui/components/button/button.tscn @@ -81,7 +81,7 @@ _data = { "down": SubResource("Animation_iu2ed") } -[node name="Button" type="StaticBody3D"] +[node name="Button" type="StaticBody3D" groups=["ui_focus"]] script = ExtResource("1_74x7g") [node name="MeshInstance3D" type="MeshInstance3D" parent="."] diff --git a/lib/events/event_focus.gd b/lib/events/event_focus.gd new file mode 100644 index 0000000..24b0449 --- /dev/null +++ b/lib/events/event_focus.gd @@ -0,0 +1,5 @@ +extends Event +class_name EventFocus + +var target: Node +var previous_target: Node \ No newline at end of file diff --git a/lib/globals/event_system.gd b/lib/globals/event_system.gd index 8e3d9e6..0c3c606 100644 --- a/lib/globals/event_system.gd +++ b/lib/globals/event_system.gd @@ -17,16 +17,39 @@ signal on_ray_leave(event: EventRay) signal on_key_down(event: EventKey) signal on_key_up(event: EventKey) +signal on_focus_in(event: EventFocus) +signal on_focus_out(event: EventFocus) + +var active_node: Node = null + func emit(type: String, event: Event): if event is EventBubble: _bubble_call(type, event.target, event) + if event.target.is_in_group("ui_focus"): + _handle_focus(event.target) + else: + _handle_focus(null) else: _root_call(type, event) +func _handle_focus(node: Node): + var event = EventFocus.new() + event.previous_target = active_node + event.target = node + + if active_node != null && active_node.has_method(FN_PREFIX + "focus_in"): + active_node.call(FN_PREFIX + "focus_out", event) + on_focus_out.emit(event) + + active_node = node + + if active_node != null: + active_node.call(FN_PREFIX + "focus_in", event) + on_focus_in.emit(event) func _bubble_call(type: String, target: Variant, event: EventBubble): if target == null: - return + return false if target.has_method(FN_PREFIX + type): var updated_event = target.call(FN_PREFIX + type, event) @@ -36,7 +59,7 @@ func _bubble_call(type: String, target: Variant, event: EventBubble): event = updated_event if event.bubbling == false: - return + return false for child in target.get_children(): if child is Function && child.has_method(FN_PREFIX + type): @@ -50,6 +73,8 @@ func _bubble_call(type: String, target: Variant, event: EventBubble): # in case the top has been reached _root_call(type, event) + return true + func _root_call(type: String, event: Event): get(SIGNAL_PREFIX + type).emit(event) From 58000dcc1f6fe5630cfd1bd764b6920d4b8584ab Mon Sep 17 00:00:00 2001 From: Nitwel Date: Wed, 22 Nov 2023 23:57:49 +0100 Subject: [PATCH 08/13] update gitattributes --- .gitattributes | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitattributes b/.gitattributes index 0583c83..753fc59 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,3 +15,6 @@ *.mtl filter=lfs diff=lfs merge=lfs -text *.wav filter=lfs diff=lfs merge=lfs -text *.blend filter=lfs diff=lfs merge=lfs -text +*.dll filter=lfs diff=lfs merge=lfs -text +*.so filter=lfs diff=lfs merge=lfs -text +*.dylib filter=lfs diff=lfs merge=lfs -text From 2433845df72b1a576612d6ca91c42c15fa7fe01f Mon Sep 17 00:00:00 2001 From: Nitwel Date: Wed, 22 Nov 2023 23:59:46 +0100 Subject: [PATCH 09/13] Add debug draw addon, implement basic input event --- README.md | 1 + addons/debug_draw_3d/LICENSE | 21 ++ addons/debug_draw_3d/README.md | 183 ++++++++++++++++++ .../debug_draw_3d/debug_draw_3d.gdextension | 55 ++++++ addons/debug_draw_3d/libs/.gdignore | 0 .../libdd3d.android.template_debug.arm32.so | 3 + .../libdd3d.android.template_debug.arm64.so | 3 + .../libdd3d.android.template_debug.x86_32.so | 3 + .../libdd3d.android.template_debug.x86_64.so | 3 + .../libdd3d.android.template_release.arm32.so | 3 + .../libdd3d.android.template_release.arm64.so | 3 + ...libdd3d.android.template_release.x86_32.so | 3 + ...libdd3d.android.template_release.x86_64.so | 3 + .../libs/libdd3d.linux.editor.x86_64.so | 3 + ...d.linux.template_release.x86_64.enabled.so | 3 + .../libdd3d.linux.template_release.x86_64.so | 3 + .../libs/libdd3d.macos.editor.universal.dylib | 3 + ...d3d.macos.template_release.universal.dylib | 3 + ...s.template_release.universal.enabled.dylib | 3 + .../libs/libdd3d.windows.editor.x86_64.dll | 3 + ...ibdd3d.windows.template_release.x86_64.dll | 3 + ...indows.template_release.x86_64.enabled.dll | 3 + assets/materials/interface.tres | 3 - assets/materials/ui_element.material | 3 + content/main.tscn | 6 +- content/system/keyboard/keyboard.gd | 12 +- content/system/keyboard/keyboard.tscn | 12 +- content/ui/components/button/button.gd | 1 - content/ui/components/button/button.tscn | 6 +- content/ui/components/input/input.gd | 85 ++++++++ content/ui/components/input/input.tscn | 80 ++++++++ content/ui/menu/edit/edit_menu.tscn | 2 + content/ui/menu/menu.tscn | 6 +- lib/events/event_focus.gd | 1 + lib/events/event_key.gd | 26 ++- lib/globals/event_system.gd | 41 ++-- 36 files changed, 554 insertions(+), 41 deletions(-) create mode 100644 addons/debug_draw_3d/LICENSE create mode 100644 addons/debug_draw_3d/README.md create mode 100644 addons/debug_draw_3d/debug_draw_3d.gdextension create mode 100644 addons/debug_draw_3d/libs/.gdignore create mode 100644 addons/debug_draw_3d/libs/libdd3d.android.template_debug.arm32.so create mode 100644 addons/debug_draw_3d/libs/libdd3d.android.template_debug.arm64.so create mode 100644 addons/debug_draw_3d/libs/libdd3d.android.template_debug.x86_32.so create mode 100644 addons/debug_draw_3d/libs/libdd3d.android.template_debug.x86_64.so create mode 100644 addons/debug_draw_3d/libs/libdd3d.android.template_release.arm32.so create mode 100644 addons/debug_draw_3d/libs/libdd3d.android.template_release.arm64.so create mode 100644 addons/debug_draw_3d/libs/libdd3d.android.template_release.x86_32.so create mode 100644 addons/debug_draw_3d/libs/libdd3d.android.template_release.x86_64.so create mode 100644 addons/debug_draw_3d/libs/libdd3d.linux.editor.x86_64.so create mode 100644 addons/debug_draw_3d/libs/libdd3d.linux.template_release.x86_64.enabled.so create mode 100644 addons/debug_draw_3d/libs/libdd3d.linux.template_release.x86_64.so create mode 100644 addons/debug_draw_3d/libs/libdd3d.macos.editor.universal.dylib create mode 100644 addons/debug_draw_3d/libs/libdd3d.macos.template_release.universal.dylib create mode 100644 addons/debug_draw_3d/libs/libdd3d.macos.template_release.universal.enabled.dylib create mode 100644 addons/debug_draw_3d/libs/libdd3d.windows.editor.x86_64.dll create mode 100644 addons/debug_draw_3d/libs/libdd3d.windows.template_release.x86_64.dll create mode 100644 addons/debug_draw_3d/libs/libdd3d.windows.template_release.x86_64.enabled.dll delete mode 100644 assets/materials/interface.tres create mode 100644 assets/materials/ui_element.material create mode 100644 content/ui/components/input/input.gd create mode 100644 content/ui/components/input/input.tscn diff --git a/README.md b/README.md index 61b6c7b..b66e0f6 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,7 @@ Thus I've decided to use a custom event system that is similar to the one used i | Group | Description | | -- | -- | | `ui_focus` | The element can be focused | +| `ui_focus_skip` | The focus will not be reset. Useful for keyboard | ### Functions diff --git a/addons/debug_draw_3d/LICENSE b/addons/debug_draw_3d/LICENSE new file mode 100644 index 0000000..d5bafad --- /dev/null +++ b/addons/debug_draw_3d/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 DmitriySalnikov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, andor sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/addons/debug_draw_3d/README.md b/addons/debug_draw_3d/README.md new file mode 100644 index 0000000..a2c272b --- /dev/null +++ b/addons/debug_draw_3d/README.md @@ -0,0 +1,183 @@ +![icon](/images/icon.png) + +# Debug drawing utility for Godot + +This is an add-on for debug drawing in 3D and for some 2D overlays, which is written in `C++` and can be used with `GDScript` or `C#`. + +Based on my previous addon, which was developed only for C# https://github.com/DmitriySalnikov/godot_debug_draw_cs, and which was inspired by Zylann's GDScript addon https://github.com/Zylann/godot_debug_draw + +## [Godot 3 version](https://github.com/DmitriySalnikov/godot_debug_draw_3d/tree/godot_3) + +## Support me + +Your support adds motivation to develop my public projects. + +[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I53VZ2D) + +[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://paypal.me/dmitriysalnikov) + +[qiwi](https://qiwi.com/n/DMITRIYSALNIKOV) + +## Features + +3D: + +* Arrow +* Billboard opaque square +* Box +* Camera Frustum +* Cylinder +* Gizmo +* Grid +* Line +* Line Path +* Line with Arrow +* Points +* Position 3D (3 crossing axes) +* Sphere + +2D: + +* **[Work in progress]** + +Overlay: + +* Text (with grouping and coloring) +* FPS Graph +* Custom Graphs + +Precompiled for: + +* Windows +* Linux +* macOS +* Android + +## Download + +To download, use the [Godot Asset Library](https://godotengine.org/asset-library/asset/1766) or download the archive by clicking the button at the top of the main repository page: `Code -> Download ZIP`, then unzip it to your project folder. Or use one of the stable versions from the [GitHub Releases](https://github.com/DmitriySalnikov/godot_debug_draw_3d/releases) page (just download one of the `Source Codes` in assets). + +## Usage + +* Close editor +* Copy `addons/debug_draw_3d` to your `addons` folder, create it if the folder doesn't exist +* Launch editor + +### C\# + +When you start the engine for the first time, bindings for `C#` will be generated automatically. If this does not happen, you can manually generate them through the `Project - Tools - Debug Draw` menu. + +![project_tools_menu](/images/project_tools_menu.png) + +## Examples + +More examples can be found in the `examples_dd3d/` folder. + +Simple test: + +```gdscript +func _process(delta: float) -> void: + var _time = Time.get_ticks_msec() / 1000.0 + var box_pos = Vector3(0, sin(_time * 4), 0) + var line_begin = Vector3(-1, sin(_time * 4), 0) + var line_end = Vector3(1, cos(_time * 4), 0) + + DebugDraw3D.draw_box(box_pos, Vector3(1, 2, 1), Color(0, 1, 0)) + DebugDraw3D.draw_line(line_begin, line_end, Color(1, 1, 0)) + DebugDraw2D.set_text("Time", _time) + DebugDraw2D.set_text("Frames drawn", Engine.get_frames_drawn()) + DebugDraw2D.set_text("FPS", Engine.get_frames_per_second()) + DebugDraw2D.set_text("delta", delta) +``` + +```csharp +public override void _Process(float delta) +{ + var _time = Time.GetTicksMsec() / 1000.0f; + var box_pos = new Vector3(0, Mathf.Sin(_time * 4f), 0); + var line_begin = new Vector3(-1, Mathf.Sin(_time * 4f), 0); + var line_end = new Vector3(1, Mathf.Cos(_time * 4f), 0); + + DebugDraw3D.DrawBox(box_pos, new Vector3(1, 2, 1), new Color(0, 1, 0)); + DebugDraw3D.DrawLine(line_begin, line_end, new Color(1, 1, 0)); + DebugDraw2D.SetText("Time", _time); + DebugDraw2D.SetText("Frames drawn", Engine.GetFramesDrawn()); + DebugDraw2D.SetText("FPS", Engine.GetFramesPerSecond()); + DebugDraw2D.SetText("delta", delta); +} +``` + +![screenshot_1](/images/screenshot_1.png) + +## API + +A list of all functions is available in the documentation inside the editor. +![screenshot_4](/images/screenshot_4.png) + +Besides `DebugDraw2D/3D`, you can also use `Dbg2/3`. + +```gdscript + DebugDraw3D.draw_box_xf(Transform3D(), Color.GREEN) + Dbg3.draw_box_xf(Transform3D(), Color.GREEN) + + DebugDraw2D.set_text("delta", delta) + Dbg2.set_text("delta", delta) +``` + +But unfortunately at the moment `GDExtension` does not support adding documentation. + +## Exporting a project + +Most likely, when exporting a release version of a game, you don't want to export the debug library along with it. But since there is still no `Conditional Compilation` in `GDScript`, so I decided to create a `dummy` library that has the same API as a regular library, but has minimal impact on performance, even if calls to its methods occur. The `dummy` library is used by default in the release version. However if you need to use debug rendering in the release version, then you can add the `forced_dd3d` feature when exporting. In this case, the release library with all the functionality will be used. + +![export_features](/images/export_features.png) + +In C#, these tags are not taken into account at compile time, so the Release build will use Runtime checks to disable draw calls. If you want to avoid this, you can manually specify the `FORCED_DD3D` symbol. + +![csharp_compilation_symbols](/images/csharp_compilation_symbols.png) + +## Known issues and limitations + +Enabling occlusion culing can lower fps instead of increasing it. At the moment I do not know how to speed up the calculation of the visibility of objects. + +The text in the keys and values of a text group cannot contain multi-line strings. + +The entire text overlay can only be placed in one corner, unlike `DataGraphs`. + +[Frustum of Camera3D does not take into account the window size from ProjectSettings](https://github.com/godotengine/godot/issues/70362). + +**The version for Godot 4.0 requires explicitly specifying the exact data types, otherwise errors may occur.** + +## More screenshots + +`DebugDrawDemoScene.tscn` in editor +![screenshot_2](/images/screenshot_2.png) + +`DebugDrawDemoScene.tscn` in play mode +![screenshot_3](/images/screenshot_3.png) + +## Build + +As well as for the engine itself, you will need to configure the [environment](https://docs.godotengine.org/en/4.1/contributing/development/compiling/index.html). +And also you need to apply several patches: + +```bash +cd godot-cpp +git apply --ignore-space-change --ignore-whitespace ../patches/always_build_fix.patch +git apply --ignore-space-change --ignore-whitespace ../patches/1165.patch +# Optional +## Build only the necessary classes +git apply --ignore-space-change --ignore-whitespace ../patches/godot_cpp_exclude_unused_classes.patch +## Faster build +git apply --ignore-space-change --ignore-whitespace ../patches/unity_build.patch +``` + +Then you can just run scons as usual: + +```bash +# build for the current system. +# target=editor is used for both the editor and the debug template. +scons target=editor dev_build=yes debug_symbols=yes +# build for the android. ANDROID_NDK_ROOT is required in your environment variables. +scons platform=android target=template_release arch=arm64v8 +``` diff --git a/addons/debug_draw_3d/debug_draw_3d.gdextension b/addons/debug_draw_3d/debug_draw_3d.gdextension new file mode 100644 index 0000000..c914b15 --- /dev/null +++ b/addons/debug_draw_3d/debug_draw_3d.gdextension @@ -0,0 +1,55 @@ +[configuration] + +entry_symbol = "debug_draw_3d_library_init" +compatibility_minimum = "4.1" + +[dependencies] + +# example.x86_64 = { "relative or absolute path to the dependency" : "the path relative to the exported project", } + +macos = { } +windows.x86_64 = { } +linux.x86_64 = { } + +android.arm32 = { } +android.arm64 = { } +android.x86_32 = { } +android.x86_64 = { } + +macos.template_release = { } +windows.template_release.x86_64 = { } +linux.template_release.x86_64 = { } + +android.template_release.arm32 = { } +android.template_release.arm64 = { } +android.template_release.x86_32 = { } +android.template_release.x86_64 = { } + +[libraries] + +macos = "libs/libdd3d.macos.editor.universal.dylib" +windows.x86_64 = "libs/libdd3d.windows.editor.x86_64.dll" +linux.x86_64 = "libs/libdd3d.linux.editor.x86_64.so" + +android.arm32 = "libs/libdd3d.android.template_debug.arm32.so" +android.arm64 = "libs/libdd3d.android.template_debug.arm64.so" +android.x86_32 = "libs/libdd3d.android.template_debug.x86_32.so" +android.x86_64 = "libs/libdd3d.android.template_debug.x86_64.so" + +macos.template_release = "libs/libdd3d.macos.template_release.universal.dylib" +windows.template_release.x86_64 = "libs/libdd3d.windows.template_release.x86_64.dll" +linux.template_release.x86_64 = "libs/libdd3d.linux.template_release.x86_64.so" + +android.template_release.arm32 = "libs/libdd3d.android.template_release.arm32.so" +android.template_release.arm64 = "libs/libdd3d.android.template_release.arm64.so" +android.template_release.x86_32 = "libs/libdd3d.android.template_release.x86_32.so" +android.template_release.x86_64 = "libs/libdd3d.android.template_release.x86_64.so" + +macos.template_release.forced_dd3d = "libs/libdd3d.macos.template_release.universal.enabled.dylib" +windows.template_release.x86_64.forced_dd3d = "libs/libdd3d.windows.template_release.x86_64.enabled.dll" +linux.template_release.x86_64.forced_dd3d = "libs/libdd3d.linux.template_release.x86_64.enabled.so" + +android.template_release.arm32.forced_dd3d = "libs/libdd3d.android.template_release.arm32.enabled.so" +android.template_release.arm64.forced_dd3d = "libs/libdd3d.android.template_release.arm64.enabled.so" +android.template_release.x86_32.forced_dd3d = "libs/libdd3d.android.template_release.x86_32.enabled.so" +android.template_release.x86_64.forced_dd3d = "libs/libdd3d.android.template_release.x86_64.enabled.so" diff --git a/addons/debug_draw_3d/libs/.gdignore b/addons/debug_draw_3d/libs/.gdignore new file mode 100644 index 0000000..e69de29 diff --git a/addons/debug_draw_3d/libs/libdd3d.android.template_debug.arm32.so b/addons/debug_draw_3d/libs/libdd3d.android.template_debug.arm32.so new file mode 100644 index 0000000..a8c5001 --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.android.template_debug.arm32.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0fb41898f762f555f379bc351a0d2d82bc9cbf3b701a1f9fe8035646ddad44b5 +size 2928328 diff --git a/addons/debug_draw_3d/libs/libdd3d.android.template_debug.arm64.so b/addons/debug_draw_3d/libs/libdd3d.android.template_debug.arm64.so new file mode 100644 index 0000000..ae4fe93 --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.android.template_debug.arm64.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1dfdb52ffe2e428ea0a0b9ff951b986546291f383f37c957d7de4c367f79892f +size 2993032 diff --git a/addons/debug_draw_3d/libs/libdd3d.android.template_debug.x86_32.so b/addons/debug_draw_3d/libs/libdd3d.android.template_debug.x86_32.so new file mode 100644 index 0000000..a6c31e2 --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.android.template_debug.x86_32.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c580b7b4d44342c56d1990ab09358b63fa60cf99846a355c86ceb2cb5023b14 +size 3057172 diff --git a/addons/debug_draw_3d/libs/libdd3d.android.template_debug.x86_64.so b/addons/debug_draw_3d/libs/libdd3d.android.template_debug.x86_64.so new file mode 100644 index 0000000..59d21d3 --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.android.template_debug.x86_64.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6c2b95269938e60e709539c8166cd9e5f450bd3fbd2b6bdfdf652fca3f594db +size 2977744 diff --git a/addons/debug_draw_3d/libs/libdd3d.android.template_release.arm32.so b/addons/debug_draw_3d/libs/libdd3d.android.template_release.arm32.so new file mode 100644 index 0000000..ac25634 --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.android.template_release.arm32.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c40f93bd53bc4b132d075f18814b1198e71df6b9f2601c11f0cfbc2494698cf +size 1804756 diff --git a/addons/debug_draw_3d/libs/libdd3d.android.template_release.arm64.so b/addons/debug_draw_3d/libs/libdd3d.android.template_release.arm64.so new file mode 100644 index 0000000..267c209 --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.android.template_release.arm64.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0181cc7c1a08ecc39de983b1b1e35f66572479da533f744190fa803cbde44612 +size 2001576 diff --git a/addons/debug_draw_3d/libs/libdd3d.android.template_release.x86_32.so b/addons/debug_draw_3d/libs/libdd3d.android.template_release.x86_32.so new file mode 100644 index 0000000..382bfdb --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.android.template_release.x86_32.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24497f5a2ad8a7d99e2acb62d6c526017d7c365f8fd7fd6ae3042d96bd6e980a +size 1996072 diff --git a/addons/debug_draw_3d/libs/libdd3d.android.template_release.x86_64.so b/addons/debug_draw_3d/libs/libdd3d.android.template_release.x86_64.so new file mode 100644 index 0000000..a38fb1e --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.android.template_release.x86_64.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6eab3ff00fc2d7347a6b166e67a3800d6117fa84bcfa3317429d6e0bff896654 +size 2028472 diff --git a/addons/debug_draw_3d/libs/libdd3d.linux.editor.x86_64.so b/addons/debug_draw_3d/libs/libdd3d.linux.editor.x86_64.so new file mode 100644 index 0000000..07a3f4e --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.linux.editor.x86_64.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b39c1ca40f0a22e66e742daba8e050884caa7a3ff959824b0c99a7e061fd869 +size 3806144 diff --git a/addons/debug_draw_3d/libs/libdd3d.linux.template_release.x86_64.enabled.so b/addons/debug_draw_3d/libs/libdd3d.linux.template_release.x86_64.enabled.so new file mode 100644 index 0000000..619483e --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.linux.template_release.x86_64.enabled.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4bfbec2037276c6858cbfc4019bb0381eb9bd83cdcdfcfa3cc802bfc0d5f427b +size 3072224 diff --git a/addons/debug_draw_3d/libs/libdd3d.linux.template_release.x86_64.so b/addons/debug_draw_3d/libs/libdd3d.linux.template_release.x86_64.so new file mode 100644 index 0000000..d9df68f --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.linux.template_release.x86_64.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7efcb3de6de7d5a878c4a221931a94a74f3741a6aeb7d7e9a4f19fb07bf1115c +size 2193840 diff --git a/addons/debug_draw_3d/libs/libdd3d.macos.editor.universal.dylib b/addons/debug_draw_3d/libs/libdd3d.macos.editor.universal.dylib new file mode 100644 index 0000000..1ba502d --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.macos.editor.universal.dylib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:818647a1234354725573918ce71972193812dd19d9ffb4bbe35f4a17ff48341f +size 5739144 diff --git a/addons/debug_draw_3d/libs/libdd3d.macos.template_release.universal.dylib b/addons/debug_draw_3d/libs/libdd3d.macos.template_release.universal.dylib new file mode 100644 index 0000000..f05a3b9 --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.macos.template_release.universal.dylib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5bbf87c77f9c9fe6643948257e91f271825a2874172c97cdbd915a6203a6eff3 +size 3339680 diff --git a/addons/debug_draw_3d/libs/libdd3d.macos.template_release.universal.enabled.dylib b/addons/debug_draw_3d/libs/libdd3d.macos.template_release.universal.enabled.dylib new file mode 100644 index 0000000..6ac5ee5 --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.macos.template_release.universal.enabled.dylib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0f280b00b057266534e477e4b33e476f108b41d1927d4db39e8d1e3a781adc5 +size 4689496 diff --git a/addons/debug_draw_3d/libs/libdd3d.windows.editor.x86_64.dll b/addons/debug_draw_3d/libs/libdd3d.windows.editor.x86_64.dll new file mode 100644 index 0000000..2206af5 --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.windows.editor.x86_64.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac3ac3647171c53e2f47564df2e25eff19fbfab8bf763d0078fe1c82476d8761 +size 1291264 diff --git a/addons/debug_draw_3d/libs/libdd3d.windows.template_release.x86_64.dll b/addons/debug_draw_3d/libs/libdd3d.windows.template_release.x86_64.dll new file mode 100644 index 0000000..07578b6 --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.windows.template_release.x86_64.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d3113daaa53fe431ca8ce55f127c0e41ef4243ab5aadd359dc6d2c63b31f057 +size 680448 diff --git a/addons/debug_draw_3d/libs/libdd3d.windows.template_release.x86_64.enabled.dll b/addons/debug_draw_3d/libs/libdd3d.windows.template_release.x86_64.enabled.dll new file mode 100644 index 0000000..c26c24b --- /dev/null +++ b/addons/debug_draw_3d/libs/libdd3d.windows.template_release.x86_64.enabled.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af5ae3a80d09e6212d21e4cf608aa38f711d230914918d0f2f427bdcc5488e24 +size 862208 diff --git a/assets/materials/interface.tres b/assets/materials/interface.tres deleted file mode 100644 index 3af2e71..0000000 --- a/assets/materials/interface.tres +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:55d5f30db336a8f8f7633743c23e41077d5c1a1b900c475a6d3811746eba3d1b -size 141 diff --git a/assets/materials/ui_element.material b/assets/materials/ui_element.material new file mode 100644 index 0000000..78953be --- /dev/null +++ b/assets/materials/ui_element.material @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:57f8bfca31acba3de3a15a2b9a88b8afd12f8db9c356f14d906840dcee48d6f6 +size 1021 diff --git a/content/main.tscn b/content/main.tscn index 0832e3b..d0c91b9 100644 --- a/content/main.tscn +++ b/content/main.tscn @@ -33,13 +33,13 @@ script = ExtResource("1_uvrd4") [node name="XROrigin3D" type="XROrigin3D" parent="."] [node name="XRCamera3D" type="XRCamera3D" parent="XROrigin3D"] -transform = Transform3D(1, 2.18865e-10, 3.7835e-10, 3.85836e-11, 1, 2.08752e-08, -2.91038e-11, 8.6402e-12, 1, 0.0356344, 0.79808, 0.202806) +transform = Transform3D(1, 1.8976e-10, 4.07454e-10, 6.76872e-11, 1, 2.08738e-08, -5.82077e-11, 1.04592e-11, 1, 0.0356618, 0.71033, 0.00564247) [node name="XRControllerLeft" parent="XROrigin3D" instance=ExtResource("2_2lraw")] transform = Transform3D(0.999999, -1.39633e-11, 0, 9.48075e-12, 1, 0, 0, 0, 1, -0.355145, 0.550439, -0.477945) [node name="XRControllerRight" type="XRController3D" parent="XROrigin3D"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.488349, 0.559219, -0.2988) +transform = Transform3D(0.999999, -1.39633e-11, 0, 9.48075e-12, 1, 0, 0, 0, 1, 0.272616, 0.559282, -0.468369) tracker = &"right_hand" pose = &"aim" @@ -66,4 +66,4 @@ transform = Transform3D(0.404247, 0.000180645, 0.914648, 0.00017221, 0.999999, - visible = false [node name="Keyboard" parent="." instance=ExtResource("9_e5n3p")] -transform = Transform3D(0.5, -6.98186e-12, 0, 4.73988e-12, 0.5, 0, 0, 0, 0.5, -0.00874073, 0.253873, -0.357135) +transform = Transform3D(0.499999, -6.98142e-12, 0, 4.74065e-12, 0.5, -2.27374e-13, 0, 2.27374e-13, 0.5, -0.125313, 0.424282, -0.263966) diff --git a/content/system/keyboard/keyboard.gd b/content/system/keyboard/keyboard.gd index 584e526..1eb2ca4 100644 --- a/content/system/keyboard/keyboard.gd +++ b/content/system/keyboard/keyboard.gd @@ -6,6 +6,7 @@ const button_scene = preload("res://content/ui/components/button/button.tscn") @onready var keys = $Keys @onready var caps_button = $Caps @onready var backspace_button = $Backspace +@onready var paste_button = $Paste var key_list = [ [KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_ASCIITILDE], [KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_SLASH], @@ -57,6 +58,15 @@ func _ready(): _emit_event("key_up", KEY_CAPSLOCK) ) + paste_button.on_button_down.connect(func(): + # There is no KEY_PASTE obviously, so we use KEY_INSERT for now + _emit_event("key_down", KEY_INSERT) + ) + + paste_button.on_button_up.connect(func(): + _emit_event("key_up", KEY_INSERT) + ) + func create_key(key: Key): var button = button_scene.instantiate() @@ -68,7 +78,7 @@ func create_key(key: Key): label.add_to_group("button_label") button.set_meta("key", key) - + button.add_to_group("ui_focus_skip") button.add_child(label) return button diff --git a/content/system/keyboard/keyboard.tscn b/content/system/keyboard/keyboard.tscn index 90b304f..abaaab4 100644 --- a/content/system/keyboard/keyboard.tscn +++ b/content/system/keyboard/keyboard.tscn @@ -12,7 +12,7 @@ size = Vector3(0.744504, 0.0402036, 0.296009) transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0) script = ExtResource("1_maojw") -[node name="Backspace" parent="." instance=ExtResource("1_xdpwr")] +[node name="Backspace" parent="." groups=["ui_focus_skip"] instance=ExtResource("1_xdpwr")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.66, 0, 0.02) metadata/key = 4194308 @@ -21,7 +21,7 @@ transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.0 pixel_size = 0.001 text = "back" -[node name="Caps" parent="." instance=ExtResource("1_xdpwr")] +[node name="Caps" parent="." groups=["ui_focus_skip"] instance=ExtResource("1_xdpwr")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.06, 0, 0.15) toggleable = true @@ -30,6 +30,14 @@ transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.0 pixel_size = 0.001 text = "caps" +[node name="Paste" parent="." groups=["ui_focus_skip"] instance=ExtResource("1_xdpwr")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.66, 0, 0.18) + +[node name="Label3D" type="Label3D" parent="Paste"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.012, 0) +pixel_size = 0.001 +text = "paste" + [node name="Keys" type="Node3D" parent="."] script = ExtResource("3_mx544") columns = 11 diff --git a/content/ui/components/button/button.gd b/content/ui/components/button/button.gd index f12787d..6f0562c 100644 --- a/content/ui/components/button/button.gd +++ b/content/ui/components/button/button.gd @@ -1,4 +1,3 @@ -@tool extends StaticBody3D class_name Button3D diff --git a/content/ui/components/button/button.tscn b/content/ui/components/button/button.tscn index 6b1eff7..a2c6572 100644 --- a/content/ui/components/button/button.tscn +++ b/content/ui/components/button/button.tscn @@ -1,13 +1,11 @@ [gd_scene load_steps=8 format=3 uid="uid://bsjqdvkt0u87c"] [ext_resource type="Script" path="res://content/ui/components/button/button.gd" id="1_74x7g"] - -[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_peqek"] -albedo_color = Color(0.151534, 0.211909, 0.619523, 1) +[ext_resource type="Material" uid="uid://bujy3egn1oqac" path="res://assets/materials/ui_element.material" id="2_h7ln4"] [sub_resource type="BoxMesh" id="BoxMesh_jwpm5"] resource_local_to_scene = true -material = SubResource("StandardMaterial3D_peqek") +material = ExtResource("2_h7ln4") size = Vector3(0.05, 0.02, 0.05) [sub_resource type="ConvexPolygonShape3D" id="ConvexPolygonShape3D_o4j7g"] diff --git a/content/ui/components/input/input.gd b/content/ui/components/input/input.gd new file mode 100644 index 0000000..88fb85e --- /dev/null +++ b/content/ui/components/input/input.gd @@ -0,0 +1,85 @@ +extends StaticBody3D + +@onready var caret: MeshInstance3D = $Label/Caret +@onready var label: Label3D = $Label +@onready var animation: AnimationPlayer = $AnimationPlayer + +var text: String = "Hello World": + set(value): + var old_text = text + text = value + label.text = value + gap_offsets = _calculate_text_gaps() + caret_position += text.length() - old_text.length() + +var keyboard_input: bool = false +var gap_offsets = [] +var caret_position: int = 3: + set(value): + caret_position = clampi(value, 0, text.length()) + caret.position.x = gap_offsets[caret_position] + +func _ready(): + EventSystem.on_key_down.connect(func(event): + if EventSystem.is_focused(self) == false: + return + + text = EventKey.key_to_string(event.key, event.shift_pressed, text.substr(0, caret_position)) + text.substr(caret_position, text.length()) + ) + +func _input(event): + if event is InputEventKey && EventSystem.is_focused(self) && event.pressed: + if event.keycode == KEY_F1: + keyboard_input = !keyboard_input + return + + if keyboard_input: + text = EventKey.key_to_string(event.keycode, event.shift_pressed, text.substr(0, caret_position)) + text.substr(caret_position, text.length()) + +func _process(_delta): + if get_tree().debug_collisions_hint && OS.get_name() != "Android": + _draw_debug_text_gaps() + +func _calculate_caret_pos(click_pos_x: float): + for i in range(1, gap_offsets.size()): + var left = gap_offsets[i] + + if click_pos_x < left: + return i - 1 + + return gap_offsets.size() - 1 + + +func _on_focus_in(event): + gap_offsets = _calculate_text_gaps() + + var pos_x = label.to_local(event.ray.get_collision_point()).x + + caret_position = _calculate_caret_pos(pos_x) + animation.play("blink") + +func _on_focus_out(_event): + animation.stop() + caret.hide() + +func _calculate_text_gaps(): + var font = label.get_font() + var offsets = [0.0] + + var offset = 0.0 + for i in range(text.length()): + var character = text[i] + var size = font.get_string_size(character, HORIZONTAL_ALIGNMENT_CENTER, -1, label.font_size) + + offset += size.x * label.pixel_size + offsets.append(offset) + + return offsets + +func _draw_debug_text_gaps(): + for offset in gap_offsets: + DebugDraw3D.draw_line( + label.to_global(Vector3(offset, -0.01, 0)), + label.to_global(Vector3(offset, 0.01, 0)), + Color(1, 0, 0), + ) diff --git a/content/ui/components/input/input.tscn b/content/ui/components/input/input.tscn new file mode 100644 index 0000000..289b515 --- /dev/null +++ b/content/ui/components/input/input.tscn @@ -0,0 +1,80 @@ +[gd_scene load_steps=10 format=3 uid="uid://blrhy2uccrdn4"] + +[ext_resource type="Material" uid="uid://bujy3egn1oqac" path="res://assets/materials/ui_element.material" id="1_0kd7r"] +[ext_resource type="Script" path="res://content/ui/components/input/input.gd" id="1_uml3t"] + +[sub_resource type="BoxMesh" id="BoxMesh_kjbca"] +size = Vector3(0.2, 0.006, 0.03) + +[sub_resource type="BoxShape3D" id="BoxShape3D_x4yp8"] +size = Vector3(0.2, 0.006, 0.03) + +[sub_resource type="SystemFont" id="SystemFont_nbea0"] + +[sub_resource type="BoxMesh" id="BoxMesh_2736g"] +size = Vector3(0.001, 0.02, 0.001) + +[sub_resource type="Animation" id="Animation_65tpe"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Label/Caret:visible") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [true] +} + +[sub_resource type="Animation" id="Animation_8ny1h"] +resource_name = "blink" +loop_mode = 1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Label/Caret:visible") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.5, 1), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 0, +"values": [true, false, true] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_1sy4t"] +_data = { +"RESET": SubResource("Animation_65tpe"), +"blink": SubResource("Animation_8ny1h") +} + +[node name="Input" type="StaticBody3D" groups=["ui_focus"]] +script = ExtResource("1_uml3t") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.003, 0) +material_override = ExtResource("1_0kd7r") +mesh = SubResource("BoxMesh_kjbca") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.003, 0) +shape = SubResource("BoxShape3D_x4yp8") + +[node name="Label" type="Label3D" parent="."] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -0.096, 0.00618291, 0) +pixel_size = 0.0004 +text = "Hello World" +font = SubResource("SystemFont_nbea0") +horizontal_alignment = 0 + +[node name="Caret" type="MeshInstance3D" parent="Label"] +mesh = SubResource("BoxMesh_2736g") +skeleton = NodePath("../..") + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +"": SubResource("AnimationLibrary_1sy4t") +} diff --git a/content/ui/menu/edit/edit_menu.tscn b/content/ui/menu/edit/edit_menu.tscn index ebd6feb..aea0caf 100644 --- a/content/ui/menu/edit/edit_menu.tscn +++ b/content/ui/menu/edit/edit_menu.tscn @@ -24,6 +24,7 @@ font_size = 36 transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.19, 0.01, 0.27) [node name="NextPageButton" parent="Buttons" instance=ExtResource("4_tvimg")] +focusable = true [node name="Decal" type="Decal" parent="Buttons/NextPageButton"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.02, 0) @@ -35,6 +36,7 @@ script = ExtResource("6_pf8jy") [node name="PreviousPageButton" parent="Buttons" instance=ExtResource("4_tvimg")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.08, 0, 0) +focusable = true [node name="Decal" type="Decal" parent="Buttons/PreviousPageButton"] transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0.02, 0) diff --git a/content/ui/menu/menu.tscn b/content/ui/menu/menu.tscn index 6f80042..e21ab5f 100644 --- a/content/ui/menu/menu.tscn +++ b/content/ui/menu/menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=16 format=3 uid="uid://c3kdssrmv84kv"] +[gd_scene load_steps=17 format=3 uid="uid://c3kdssrmv84kv"] [ext_resource type="Script" path="res://content/ui/menu/menu.gd" id="1_ng4u3"] [ext_resource type="PackedScene" uid="uid://crrb0l3ekuotj" path="res://content/ui/menu/edit/edit_menu.tscn" id="4_r2raj"] @@ -11,6 +11,7 @@ [ext_resource type="Texture2D" uid="uid://dyh0ax51xqp8n" path="res://assets/icons/settings_white_24dp.svg" id="9_mel13"] [ext_resource type="PackedScene" uid="uid://c01gkeldvjwtr" path="res://content/ui/menu/room/room_menu.tscn" id="10_u4i1x"] [ext_resource type="PackedScene" uid="uid://c6r4higceibif" path="res://content/ui/menu/settings/settings_menu.tscn" id="11_7wm6b"] +[ext_resource type="PackedScene" uid="uid://blrhy2uccrdn4" path="res://content/ui/components/input/input.tscn" id="12_ov3em"] [sub_resource type="BoxMesh" id="BoxMesh_08du6"] size = Vector3(0.3, 0.01, 0.3) @@ -236,3 +237,6 @@ mesh = ExtResource("7_f4u4o") libraries = { "": SubResource("AnimationLibrary_s30cd") } + +[node name="Input" parent="." instance=ExtResource("12_ov3em")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.19, 0, -0.04) diff --git a/lib/events/event_focus.gd b/lib/events/event_focus.gd index 24b0449..7c88f49 100644 --- a/lib/events/event_focus.gd +++ b/lib/events/event_focus.gd @@ -1,5 +1,6 @@ extends Event class_name EventFocus +var ray: RayCast3D var target: Node var previous_target: Node \ No newline at end of file diff --git a/lib/events/event_key.gd b/lib/events/event_key.gd index a7fe112..9f6b535 100644 --- a/lib/events/event_key.gd +++ b/lib/events/event_key.gd @@ -4,13 +4,19 @@ class_name EventKey var key: Key var echo: bool -static func key_to_string(key: Key, caps: bool = false) -> String: - match key: - KEY_ASCIITILDE: return "~" - KEY_SLASH: return "/" - KEY_BACKSLASH: return "\\" - KEY_COLON: return ";" - KEY_COMMA: return "," - KEY_PERIOD: return "." - KEY_MINUS: return "-" - _: return OS.get_keycode_string(key).to_upper() if caps else OS.get_keycode_string(key).to_lower() \ No newline at end of file +static func key_to_string(key: Key, caps: bool = false, apply_to: String = "") -> String: + match key: + KEY_INSERT: apply_to += DisplayServer.clipboard_get() + KEY_BACKSPACE: apply_to = apply_to.substr(0, apply_to.length() - 1) + KEY_SPACE: apply_to += " " + KEY_ASCIITILDE: apply_to += "~" + KEY_SLASH: apply_to += "/" + KEY_BACKSLASH: apply_to += "\\" + KEY_COLON: apply_to += ";" + KEY_COMMA: apply_to += "," + KEY_PERIOD: apply_to += "." + KEY_MINUS: apply_to += "-" + KEY_CAPSLOCK: return apply_to + _: apply_to += OS.get_keycode_string(key).to_upper() if caps else OS.get_keycode_string(key).to_lower() + + return apply_to \ No newline at end of file diff --git a/lib/globals/event_system.gd b/lib/globals/event_system.gd index 0c3c606..fed2f1b 100644 --- a/lib/globals/event_system.gd +++ b/lib/globals/event_system.gd @@ -20,32 +20,41 @@ signal on_key_up(event: EventKey) signal on_focus_in(event: EventFocus) signal on_focus_out(event: EventFocus) -var active_node: Node = null +var _active_node: Node = null func emit(type: String, event: Event): if event is EventBubble: _bubble_call(type, event.target, event) - if event.target.is_in_group("ui_focus"): - _handle_focus(event.target) - else: - _handle_focus(null) + if type == "press_down": + _handle_focus(event) else: _root_call(type, event) -func _handle_focus(node: Node): - var event = EventFocus.new() - event.previous_target = active_node - event.target = node +func is_focused(node: Node): + return _active_node == node - if active_node != null && active_node.has_method(FN_PREFIX + "focus_in"): - active_node.call(FN_PREFIX + "focus_out", event) - on_focus_out.emit(event) +func _handle_focus(event: EventRay): + if event.target != null && event.target.is_in_group("ui_focus_skip"): + return - active_node = node + var event_focus = EventFocus.new() + event_focus.previous_target = _active_node + event_focus.target = event.target + event_focus.ray = event.ray - if active_node != null: - active_node.call(FN_PREFIX + "focus_in", event) - on_focus_in.emit(event) + if _active_node != null && _active_node.has_method(FN_PREFIX + "focus_out"): + _active_node.call(FN_PREFIX + "focus_out", event_focus) + on_focus_out.emit(event_focus) + + if event.target == null || event.target.is_in_group("ui_focus") == false: + _active_node = null + return + + _active_node = event.target + + if _active_node != null && _active_node.has_method(FN_PREFIX + "focus_in"): + _active_node.call(FN_PREFIX + "focus_in", event_focus) + on_focus_in.emit(event_focus) func _bubble_call(type: String, target: Variant, event: EventBubble): if target == null: From f8033e7e064e90f3007c1ace040d5574c1761e72 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Thu, 23 Nov 2023 03:41:13 +0100 Subject: [PATCH 10/13] finish input, fix menu, implement conn config --- content/main.gd | 1 + content/system/keyboard/keyboard.tscn | 6 +- content/ui/components/button/button.gd | 10 ++- content/ui/components/input/input.gd | 26 ++++++- content/ui/components/input/input.tscn | 3 +- content/ui/device/device.tscn | 2 +- content/ui/menu/edit/edit_menu.gd | 15 ++-- content/ui/menu/menu.gd | 81 +++++++-------------- content/ui/menu/menu.tscn | 14 ++-- content/ui/menu/settings/settings_menu.gd | 23 ++++++ content/ui/menu/settings/settings_menu.tscn | 39 +++++++++- export_presets.cfg | 4 +- export_presets_prod.cfg | 4 +- lib/globals/config_data.gd | 23 ++++++ lib/home_adapters/adapter.gd | 17 +++-- lib/home_adapters/hass_ws/hass.gd | 3 + project.godot | 1 + 17 files changed, 179 insertions(+), 93 deletions(-) create mode 100644 lib/globals/config_data.gd diff --git a/content/main.gd b/content/main.gd index ce68db4..963a577 100644 --- a/content/main.gd +++ b/content/main.gd @@ -12,6 +12,7 @@ var sky_passthrough = preload("res://assets/materials/sky_passthrough.material") func _ready(): # In case we're running on the headset, use the passthrough sky if OS.get_name() == "Android": + OS.request_permissions() environment.environment.sky.set_material(sky_passthrough) house.visible = false else: diff --git a/content/system/keyboard/keyboard.tscn b/content/system/keyboard/keyboard.tscn index abaaab4..83da9bc 100644 --- a/content/system/keyboard/keyboard.tscn +++ b/content/system/keyboard/keyboard.tscn @@ -6,9 +6,9 @@ [ext_resource type="Script" path="res://content/functions/movable.gd" id="4_86fct"] [sub_resource type="BoxShape3D" id="BoxShape3D_k5ib7"] -size = Vector3(0.744504, 0.0402036, 0.296009) +size = Vector3(0.84, 0.0402036, 0.296009) -[node name="Keyboard" type="StaticBody3D"] +[node name="Keyboard" type="StaticBody3D" groups=["ui_focus_skip"]] transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0) script = ExtResource("1_maojw") @@ -48,5 +48,5 @@ size = Vector3(0.6, 1, 1) script = ExtResource("4_86fct") [node name="CollisionShape3D" type="CollisionShape3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.276719, -0.025645, 0.0928761) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.296719, -0.025645, 0.0928761) shape = SubResource("BoxShape3D_k5ib7") diff --git a/content/ui/components/button/button.gd b/content/ui/components/button/button.gd index 6f0562c..1d3f351 100644 --- a/content/ui/components/button/button.gd +++ b/content/ui/components/button/button.gd @@ -6,6 +6,7 @@ signal on_button_up() @export var toggleable: bool = false @export var disabled: bool = false +@export var external_state: bool = false @export var initial_active: bool = false var active: bool = false : set(value): @@ -31,13 +32,14 @@ func _on_press_down(event): event.bubbling = false return - if toggleable: + AudioPlayer.play_effect("click") + + if external_state || toggleable: return active = true on_button_down.emit() - AudioPlayer.play_effect("click") func _on_press_up(event): @@ -45,8 +47,12 @@ func _on_press_up(event): event.bubbling = false return + if external_state: + return + if toggleable: active = !active + if active: on_button_down.emit() else: diff --git a/content/ui/components/input/input.gd b/content/ui/components/input/input.gd index 88fb85e..f098429 100644 --- a/content/ui/components/input/input.gd +++ b/content/ui/components/input/input.gd @@ -1,16 +1,26 @@ +@tool extends StaticBody3D -@onready var caret: MeshInstance3D = $Label/Caret @onready var label: Label3D = $Label -@onready var animation: AnimationPlayer = $AnimationPlayer - -var text: String = "Hello World": +@export var text: String = "Hello World": set(value): var old_text = text text = value + if label == null: + return + label.text = value + if Engine.is_editor_hint(): + return gap_offsets = _calculate_text_gaps() caret_position += text.length() - old_text.length() + text_changed.emit(value) + +@onready var caret: MeshInstance3D = $Label/Caret + +@onready var animation: AnimationPlayer = $AnimationPlayer + +signal text_changed(text: String) var keyboard_input: bool = false var gap_offsets = [] @@ -20,6 +30,11 @@ var caret_position: int = 3: caret.position.x = gap_offsets[caret_position] func _ready(): + text = text # So @tool works + + if Engine.is_editor_hint(): + return + EventSystem.on_key_down.connect(func(event): if EventSystem.is_focused(self) == false: return @@ -37,6 +52,9 @@ func _input(event): text = EventKey.key_to_string(event.keycode, event.shift_pressed, text.substr(0, caret_position)) + text.substr(caret_position, text.length()) func _process(_delta): + if Engine.is_editor_hint(): + return + if get_tree().debug_collisions_hint && OS.get_name() != "Android": _draw_debug_text_gaps() diff --git a/content/ui/components/input/input.tscn b/content/ui/components/input/input.tscn index 289b515..33cd01f 100644 --- a/content/ui/components/input/input.tscn +++ b/content/ui/components/input/input.tscn @@ -26,7 +26,7 @@ tracks/0/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 0, -"values": [true] +"values": [false] } [sub_resource type="Animation" id="Animation_8ny1h"] @@ -71,6 +71,7 @@ font = SubResource("SystemFont_nbea0") horizontal_alignment = 0 [node name="Caret" type="MeshInstance3D" parent="Label"] +visible = false mesh = SubResource("BoxMesh_2736g") skeleton = NodePath("../..") diff --git a/content/ui/device/device.tscn b/content/ui/device/device.tscn index ee30614..2c47541 100644 --- a/content/ui/device/device.tscn +++ b/content/ui/device/device.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=4 format=3 uid="uid://dbe8slnyhro2n"] [ext_resource type="Script" path="res://content/ui/device/device.gd" id="1_rbo86"] -[ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/button/button.tscn" id="2_go2es"] +[ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="2_go2es"] [ext_resource type="Script" path="res://content/functions/clickable.gd" id="3_6wicx"] [node name="Device" type="Node3D"] diff --git a/content/ui/menu/edit/edit_menu.gd b/content/ui/menu/edit/edit_menu.gd index 164589e..04589eb 100644 --- a/content/ui/menu/edit/edit_menu.gd +++ b/content/ui/menu/edit/edit_menu.gd @@ -10,7 +10,7 @@ const Sensor = preload("res://content/entities/sensor/sensor.tscn") @onready var next_page_button = $Buttons/NextPageButton @onready var previous_page_button = $Buttons/PreviousPageButton @onready var page_number_label = $PageNumberLabel -var devices +var devices = [] var page = 0 var last_device_page = 0 var page_size = 20 @@ -19,7 +19,10 @@ var pages = 0 var selected_device = null # Called when the node enters the scene tree for the first time. func _ready(): - devices = await HomeAdapters.adapter.get_devices() + HomeAdapters.adapter.adapter.on_connect.connect(func(): + devices = await HomeAdapters.adapter.get_devices() + render() + ) next_page_button.get_node("Clickable").on_click.connect(func(_event): print("next page") @@ -30,8 +33,6 @@ func _ready(): previous_page() ) - render() - func update_pages(): if selected_device == null: pages = ceil(float(devices.size()) / page_size) @@ -62,6 +63,9 @@ func previous_page(): render() func render(): + if devices.size() == 0: + return + update_pages() page_number_label.set_text(str(page + 1) + " / " + str(pages)) @@ -91,7 +95,7 @@ func render_devices(): _on_device_click(device_instance.id) ) devices_node.add_child(device_instance) - device_instance.set_device_name(info["name"]) + device_instance.set_device_name.call_deferred(info["name"]) devices_node._update_container() @@ -158,6 +162,7 @@ func _on_entity_click(entity_name): func clear_menu(): for child in devices_node.get_children(): devices_node.remove_child(child) + child.queue_free() # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta): diff --git a/content/ui/menu/menu.gd b/content/ui/menu/menu.gd index ffabd62..0d4aca1 100644 --- a/content/ui/menu/menu.gd +++ b/content/ui/menu/menu.gd @@ -16,18 +16,9 @@ extends Node3D @onready var nav = $AnimationContainer/Navigation @onready var animation_player = $AnimationPlayer -enum Menu { - VIEW, - EDIT, - ROOM, - AUTOMATE, - SETTINGS -} +var selected_nav = null -var selected_menu := Menu.EDIT var show_menu := true: - get: - return show_menu set(value): show_menu = value if value: @@ -39,67 +30,47 @@ var show_menu := true: func _ready(): _controller.button_pressed.connect(func(button): - print(button) if button == "by_button": show_menu = !show_menu ) - select_menu(selected_menu) + select_menu(nav_edit) func _on_click(event): - if event.target == nav_view: - select_menu(Menu.VIEW) - elif event.target == nav_edit: - select_menu(Menu.EDIT) - elif event.target == nav_room: - select_menu(Menu.ROOM) - elif event.target == nav_automate: - select_menu(Menu.AUTOMATE) - elif event.target == nav_settings: - select_menu(Menu.SETTINGS) + select_menu(event.target) + +func select_menu(nav): + if _is_valid_nav(nav) == false || selected_nav == nav: + return -func select_menu(menu: Menu): - selected_menu = menu for child in content.get_children(): content.remove_child(child) - var menu_node = enum_to_menu(menu) - var nav_node = enum_to_nav(menu) + if selected_nav != null: + selected_nav.active = false - if nav_node != null: - nav_node.disabled = true + selected_nav = nav - if menu_node != null: - menu_node.visible = true - content.add_child(menu_node) + if selected_nav != null: + selected_nav.active = true + var menu = _nav_to_menu(selected_nav) + if menu != null: + content.add_child(menu) + menu.visible = true - for child in nav.get_children(): - if child.active && child != nav_node: - child.active = false - child.disabled = false - -func enum_to_nav(menu: Menu): - match menu: - Menu.VIEW: - return nav_view - Menu.EDIT: - return nav_edit - Menu.ROOM: - return nav_room - Menu.AUTOMATE: - return nav_automate - Menu.SETTINGS: - return nav_settings +func _is_valid_nav(nav): + return nav == nav_view || nav == nav_edit || nav == nav_room || nav == nav_automate || nav == nav_settings -func enum_to_menu(menu: Menu): - match menu: - Menu.VIEW: +func _nav_to_menu(nav): + match nav: + nav_view: return null - Menu.EDIT: + nav_edit: return menu_edit - Menu.ROOM: + nav_room: return menu_room - Menu.AUTOMATE: + nav_automate: return null - Menu.SETTINGS: + nav_settings: return menu_settings + return null diff --git a/content/ui/menu/menu.tscn b/content/ui/menu/menu.tscn index e21ab5f..cb0a976 100644 --- a/content/ui/menu/menu.tscn +++ b/content/ui/menu/menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=17 format=3 uid="uid://c3kdssrmv84kv"] +[gd_scene load_steps=16 format=3 uid="uid://c3kdssrmv84kv"] [ext_resource type="Script" path="res://content/ui/menu/menu.gd" id="1_ng4u3"] [ext_resource type="PackedScene" uid="uid://crrb0l3ekuotj" path="res://content/ui/menu/edit/edit_menu.tscn" id="4_r2raj"] @@ -11,7 +11,6 @@ [ext_resource type="Texture2D" uid="uid://dyh0ax51xqp8n" path="res://assets/icons/settings_white_24dp.svg" id="9_mel13"] [ext_resource type="PackedScene" uid="uid://c01gkeldvjwtr" path="res://content/ui/menu/room/room_menu.tscn" id="10_u4i1x"] [ext_resource type="PackedScene" uid="uid://c6r4higceibif" path="res://content/ui/menu/settings/settings_menu.tscn" id="11_7wm6b"] -[ext_resource type="PackedScene" uid="uid://blrhy2uccrdn4" path="res://content/ui/components/input/input.tscn" id="12_ov3em"] [sub_resource type="BoxMesh" id="BoxMesh_08du6"] size = Vector3(0.3, 0.01, 0.3) @@ -169,6 +168,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.06, 0, 0) [node name="View" parent="AnimationContainer/Navigation" instance=ExtResource("5_w4i01")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.03, 0, 0.03) toggleable = true +external_state = true [node name="Sprite3D" type="Sprite3D" parent="AnimationContainer/Navigation/View"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.012, 0) @@ -179,7 +179,7 @@ texture = ExtResource("5_8o1rb") [node name="Edit" parent="AnimationContainer/Navigation" instance=ExtResource("5_w4i01")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.03, 0, 0.09) toggleable = true -initial_active = true +external_state = true [node name="Sprite3D" type="Sprite3D" parent="AnimationContainer/Navigation/Edit"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.012, 0) @@ -190,6 +190,7 @@ texture = ExtResource("6_344ot") [node name="Room" parent="AnimationContainer/Navigation" instance=ExtResource("5_w4i01")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.03, 0, 0.15) toggleable = true +external_state = true [node name="Sprite3D" type="Sprite3D" parent="AnimationContainer/Navigation/Room"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.012, 0) @@ -200,6 +201,7 @@ texture = ExtResource("7_wvovx") [node name="Automate" parent="AnimationContainer/Navigation" instance=ExtResource("5_w4i01")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.03, 0, 0.21) toggleable = true +external_state = true [node name="Sprite3D" type="Sprite3D" parent="AnimationContainer/Navigation/Automate"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.012, 0) @@ -210,6 +212,7 @@ texture = ExtResource("8_3d082") [node name="Settings" parent="AnimationContainer/Navigation" instance=ExtResource("5_w4i01")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.03, 0, 0.27) toggleable = true +external_state = true [node name="Sprite3D" type="Sprite3D" parent="AnimationContainer/Navigation/Settings"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.012, 0) @@ -221,12 +224,12 @@ texture = ExtResource("9_mel13") transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.06, 0, 0) [node name="EditMenu" parent="AnimationContainer/Content" instance=ExtResource("4_r2raj")] +visible = false [node name="RoomMenu" parent="AnimationContainer/Content" instance=ExtResource("10_u4i1x")] visible = false [node name="SettingsMenu" parent="AnimationContainer/Content" instance=ExtResource("11_7wm6b")] -visible = false [node name="ImmersiveHomePanels" type="MeshInstance3D" parent="."] transform = Transform3D(-4.37114e-10, 0, 0.01, 0, 0.01, 0, -0.01, 0, -4.37114e-10, 0.32, 0, -0.0500001) @@ -237,6 +240,3 @@ mesh = ExtResource("7_f4u4o") libraries = { "": SubResource("AnimationLibrary_s30cd") } - -[node name="Input" parent="." instance=ExtResource("12_ov3em")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.19, 0, -0.04) diff --git a/content/ui/menu/settings/settings_menu.gd b/content/ui/menu/settings/settings_menu.gd index ff0fad1..1eabd4d 100644 --- a/content/ui/menu/settings/settings_menu.gd +++ b/content/ui/menu/settings/settings_menu.gd @@ -5,6 +5,10 @@ const ball_scene = preload("res://content/ui/menu/settings/ball.tscn") @onready var clickable = $Content/Button/Clickable @onready var connection_status = $Content/ConnectionStatus +@onready var input_url = $Content/InputURL +@onready var input_token = $Content/InputToken +@onready var button_connect = $Content/Connect + func _ready(): clickable.on_click.connect(func(event): var ball = ball_scene.instantiate() @@ -13,6 +17,25 @@ func _ready(): get_tree().root.add_child(ball) ) + var config = ConfigData.load_config() + + if config.has("url"): + input_url.text = config["url"] + if config.has("token"): + input_token.text = config["token"] + + button_connect.on_button_down.connect(func(): + HomeAdapters.adapter.adapter.url = input_url.text + "/api/websocket" + HomeAdapters.adapter.adapter.token = input_token.text + HomeAdapters.adapter.adapter.retries = 5 + HomeAdapters.adapter.adapter.connect_ws() + + ConfigData.save_config({ + "url": input_url.text, + "token": input_token.text + }) + ) + HomeAdapters.adapter.adapter.on_connect.connect(func(): connection_status.text = "Connected" ) diff --git a/content/ui/menu/settings/settings_menu.tscn b/content/ui/menu/settings/settings_menu.tscn index 07e790c..628e466 100644 --- a/content/ui/menu/settings/settings_menu.tscn +++ b/content/ui/menu/settings/settings_menu.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=5 format=3 uid="uid://c6r4higceibif"] +[gd_scene load_steps=6 format=3 uid="uid://c6r4higceibif"] [ext_resource type="Script" path="res://content/ui/menu/settings/settings_menu.gd" id="1_0lte6"] [ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="1_faxng"] [ext_resource type="Script" path="res://content/functions/clickable.gd" id="3_qmg6q"] +[ext_resource type="PackedScene" uid="uid://blrhy2uccrdn4" path="res://content/ui/components/input/input.tscn" id="4_q3x6k"] [sub_resource type="BoxMesh" id="BoxMesh_e51x8"] size = Vector3(0.3, 0.01, 0.3) @@ -18,12 +19,12 @@ mesh = SubResource("BoxMesh_e51x8") transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.0102286, 0) [node name="Label3D" type="Label3D" parent="Content"] -transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.184377, 0, 0.0435752) +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.184377, 0, 0.253575) pixel_size = 0.001 text = "Spawn Ball" [node name="Button" parent="Content" instance=ExtResource("1_faxng")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0458097, 0, 0.0435752) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0458097, 0, 0.253575) [node name="Clickable" type="Node" parent="Content/Button"] script = ExtResource("3_qmg6q") @@ -32,3 +33,35 @@ script = ExtResource("3_qmg6q") transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.26, 0, 0.29) pixel_size = 0.0003 text = "Disconnected" + +[node name="LabelURL" type="Label3D" parent="Content"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.01, 0, 0.03) +pixel_size = 0.0005 +text = "URL" +font_size = 36 +horizontal_alignment = 0 + +[node name="InputURL" parent="Content" instance=ExtResource("4_q3x6k")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.18, 0, 0.03) +text = "ws://192.168.33.33:8123" + +[node name="LabelToken" type="Label3D" parent="Content"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.01, 0, 0.07) +pixel_size = 0.0005 +text = "TOKEN" +font_size = 36 +horizontal_alignment = 0 + +[node name="InputToken" parent="Content" instance=ExtResource("4_q3x6k")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.18, 0, 0.07) +text = "paste token here" + +[node name="LabelConnect" type="Label3D" parent="Content"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.15, 0, 0.12) +pixel_size = 0.0005 +text = "TOKEN" +font_size = 36 +horizontal_alignment = 0 + +[node name="Connect" parent="Content" instance=ExtResource("1_faxng")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.25, 0, 0.12) diff --git a/export_presets.cfg b/export_presets.cfg index db9013a..b664e50 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -142,7 +142,7 @@ permissions/process_outgoing_calls=false permissions/read_calendar=false permissions/read_call_log=false permissions/read_contacts=false -permissions/read_external_storage=false +permissions/read_external_storage=true permissions/read_frame_buffer=false permissions/read_history_bookmarks=false permissions/read_input_state=false @@ -193,7 +193,7 @@ permissions/write_apn_settings=false permissions/write_calendar=false permissions/write_call_log=false permissions/write_contacts=false -permissions/write_external_storage=false +permissions/write_external_storage=true permissions/write_gservices=false permissions/write_history_bookmarks=false permissions/write_profile=false diff --git a/export_presets_prod.cfg b/export_presets_prod.cfg index 9ba8701..514cf7c 100644 --- a/export_presets_prod.cfg +++ b/export_presets_prod.cfg @@ -142,7 +142,7 @@ permissions/process_outgoing_calls=false permissions/read_calendar=false permissions/read_call_log=false permissions/read_contacts=false -permissions/read_external_storage=false +permissions/read_external_storage=true permissions/read_frame_buffer=false permissions/read_history_bookmarks=false permissions/read_input_state=false @@ -193,7 +193,7 @@ permissions/write_apn_settings=false permissions/write_calendar=false permissions/write_call_log=false permissions/write_contacts=false -permissions/write_external_storage=false +permissions/write_external_storage=true permissions/write_gservices=false permissions/write_history_bookmarks=false permissions/write_profile=false diff --git a/lib/globals/config_data.gd b/lib/globals/config_data.gd new file mode 100644 index 0000000..7d75ccc --- /dev/null +++ b/lib/globals/config_data.gd @@ -0,0 +1,23 @@ +extends Node + +var file_url: String = "user://config.json" + +func save_config(data: Dictionary): + var file := FileAccess.open(file_url, FileAccess.WRITE) + + if file == null: + return + + var json_data := JSON.stringify(data) + file.store_string(json_data) + +func load_config(): + var file := FileAccess.open(file_url, FileAccess.READ) + + if file == null: + return {} + + var json_data := file.get_as_text() + var data = JSON.parse_string(json_data) + + return data \ No newline at end of file diff --git a/lib/home_adapters/adapter.gd b/lib/home_adapters/adapter.gd index e330a4d..5942bda 100644 --- a/lib/home_adapters/adapter.gd +++ b/lib/home_adapters/adapter.gd @@ -25,15 +25,16 @@ var adapter: Node func _init(type: ADAPTER_TYPES): - var clipboard := DisplayServer.clipboard_get() + var url = "" + var token = "" + var config = ConfigData.load_config() - if clipboard != null && clipboard.find(" ") != -1: - var clip_url = clipboard.split(" ")[0] - var clip_token = clipboard.split(" ")[1] - adapter = adapters[type].new(clip_url, clip_token) - else: - adapter = adapters[type].new() - + if config.has("url"): + url = config["url"] + "/api/websocket" + if config.has("token"): + token = config["token"] + + adapter = adapters[type].new(url, token) add_child(adapter) for method in methods: diff --git a/lib/home_adapters/hass_ws/hass.gd b/lib/home_adapters/hass_ws/hass.gd index 0a413b3..6406f13 100644 --- a/lib/home_adapters/hass_ws/hass.gd +++ b/lib/home_adapters/hass_ws/hass.gd @@ -32,6 +32,9 @@ func _init(url := self.url, token := self.token): connect_ws() func connect_ws(): + if url == "" || token == "": + return + retries -= 1 if retries < 0: print("Failed to connect to %s" % self.url) diff --git a/project.godot b/project.godot index 5bcc1f1..03f59f8 100644 --- a/project.godot +++ b/project.godot @@ -18,6 +18,7 @@ config/icon="res://assets/logo.png" [autoload] XRToolsUserSettings="*res://addons/godot-xr-tools/user_settings/user_settings.gd" +ConfigData="*res://lib/globals/config_data.gd" Request="*res://lib/globals/request.gd" HomeAdapters="*res://lib/globals/home_adapters.gd" AudioPlayer="*res://lib/globals/audio_player.gd" From c1ba32552dbb560e5a2d7506e3d71dee4c7c7a27 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Thu, 23 Nov 2023 18:26:09 +0100 Subject: [PATCH 11/13] get basic input moving to work --- content/ui/components/input/input.gd | 96 ++++++++++----------- content/ui/components/input/input.tscn | 13 +-- content/ui/components/input/text_handler.gd | 89 +++++++++++++++++++ content/ui/menu/settings/settings_menu.tscn | 4 +- 4 files changed, 145 insertions(+), 57 deletions(-) create mode 100644 content/ui/components/input/text_handler.gd diff --git a/content/ui/components/input/input.gd b/content/ui/components/input/input.gd index f098429..f380a83 100644 --- a/content/ui/components/input/input.gd +++ b/content/ui/components/input/input.gd @@ -1,36 +1,36 @@ @tool extends StaticBody3D -@onready var label: Label3D = $Label -@export var text: String = "Hello World": - set(value): - var old_text = text - text = value - if label == null: - return - - label.text = value - if Engine.is_editor_hint(): - return - gap_offsets = _calculate_text_gaps() - caret_position += text.length() - old_text.length() - text_changed.emit(value) +var text_handler = preload("res://content/ui/components/input/text_handler.gd").new() @onready var caret: MeshInstance3D = $Label/Caret - +@onready var mesh_box: MeshInstance3D = $Box +@onready var collision: CollisionShape3D = $Collision @onready var animation: AnimationPlayer = $AnimationPlayer +@onready var label: Label3D = $Label -signal text_changed(text: String) +@export_range(0.1, 2, 0.01, "suffix:m") var width: float = 0.15: + get: + return text_handler.width + set(value): + set_width(value) + +@export var text: String: + get: + return text_handler.text + set(value): + text_handler.set_text(value, EventSystem.is_focused(self) == false) + + if label != null: + label.text = text_handler.get_display_text() var keyboard_input: bool = false -var gap_offsets = [] -var caret_position: int = 3: - set(value): - caret_position = clampi(value, 0, text.length()) - caret.position.x = gap_offsets[caret_position] + func _ready(): - text = text # So @tool works + text_handler.label = label + text = text + width = width if Engine.is_editor_hint(): return @@ -39,7 +39,9 @@ func _ready(): if EventSystem.is_focused(self) == false: return - text = EventKey.key_to_string(event.key, event.shift_pressed, text.substr(0, caret_position)) + text.substr(caret_position, text.length()) + text = EventKey.key_to_string(event.key, event.shift_pressed, text.substr(0, text_handler.caret_position)) + text.substr(text_handler.caret_position, text.length()) + caret.position.x = text_handler.get_caret_position() + label.text = text_handler.get_display_text() ) func _input(event): @@ -49,7 +51,8 @@ func _input(event): return if keyboard_input: - text = EventKey.key_to_string(event.keycode, event.shift_pressed, text.substr(0, caret_position)) + text.substr(caret_position, text.length()) + text = EventKey.key_to_string(event.keycode, event.shift_pressed, text.substr(0, text_handler.caret_position)) + text.substr(text_handler.caret_position, text.length()) + caret.position.x = text_handler.get_caret_position() func _process(_delta): if Engine.is_editor_hint(): @@ -58,46 +61,37 @@ func _process(_delta): if get_tree().debug_collisions_hint && OS.get_name() != "Android": _draw_debug_text_gaps() -func _calculate_caret_pos(click_pos_x: float): - for i in range(1, gap_offsets.size()): - var left = gap_offsets[i] - - if click_pos_x < left: - return i - 1 +func set_width(value: float): + text_handler.width = value - return gap_offsets.size() - 1 - + if mesh_box == null || collision == null || label == null: + return + + mesh_box.mesh.size.x = value + collision.shape.size.x = value + label.position.x = -value / 2 + 0.002 func _on_focus_in(event): - gap_offsets = _calculate_text_gaps() - var pos_x = label.to_local(event.ray.get_collision_point()).x - caret_position = _calculate_caret_pos(pos_x) + text_handler.update_caret_position(pos_x) + + caret.position.x = text_handler.get_caret_position() + caret.show() animation.play("blink") func _on_focus_out(_event): animation.stop() caret.hide() -func _calculate_text_gaps(): - var font = label.get_font() - var offsets = [0.0] - - var offset = 0.0 - for i in range(text.length()): - var character = text[i] - var size = font.get_string_size(character, HORIZONTAL_ALIGNMENT_CENTER, -1, label.font_size) - - offset += size.x * label.pixel_size - offsets.append(offset) - - return offsets - func _draw_debug_text_gaps(): - for offset in gap_offsets: + if text_handler.gap_offsets == null: + return + + for i in range(text_handler.gap_offsets.size()): + var offset = text_handler.gap_offsets[i] - text_handler.gap_offsets[text_handler.char_offset] DebugDraw3D.draw_line( label.to_global(Vector3(offset, -0.01, 0)), label.to_global(Vector3(offset, 0.01, 0)), - Color(1, 0, 0), + Color(1, 0, 0) if i != text_handler.overflow_index else Color(0, 1, 0) ) diff --git a/content/ui/components/input/input.tscn b/content/ui/components/input/input.tscn index 33cd01f..633210b 100644 --- a/content/ui/components/input/input.tscn +++ b/content/ui/components/input/input.tscn @@ -4,10 +4,12 @@ [ext_resource type="Script" path="res://content/ui/components/input/input.gd" id="1_uml3t"] [sub_resource type="BoxMesh" id="BoxMesh_kjbca"] -size = Vector3(0.2, 0.006, 0.03) +resource_local_to_scene = true +size = Vector3(0.15, 0.006, 0.03) [sub_resource type="BoxShape3D" id="BoxShape3D_x4yp8"] -size = Vector3(0.2, 0.006, 0.03) +resource_local_to_scene = true +size = Vector3(0.15, 0.006, 0.03) [sub_resource type="SystemFont" id="SystemFont_nbea0"] @@ -53,18 +55,19 @@ _data = { [node name="Input" type="StaticBody3D" groups=["ui_focus"]] script = ExtResource("1_uml3t") +text = "Hello World" -[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +[node name="Box" type="MeshInstance3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.003, 0) material_override = ExtResource("1_0kd7r") mesh = SubResource("BoxMesh_kjbca") -[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +[node name="Collision" type="CollisionShape3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.003, 0) shape = SubResource("BoxShape3D_x4yp8") [node name="Label" type="Label3D" parent="."] -transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -0.096, 0.00618291, 0) +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -0.073, 0.00618291, 0) pixel_size = 0.0004 text = "Hello World" font = SubResource("SystemFont_nbea0") diff --git a/content/ui/components/input/text_handler.gd b/content/ui/components/input/text_handler.gd new file mode 100644 index 0000000..9b75af1 --- /dev/null +++ b/content/ui/components/input/text_handler.gd @@ -0,0 +1,89 @@ +extends Object + +var label: Label3D + +var text: String = "" +var width: float = 0.2 +var gap_offsets = null +var overflow_index: int = -1 +var char_offset: int = 0 +var caret_position: int = 3: + set(value): + caret_position = clampi(value, 0, text.length()) + +func set_width(value: float): + width = value + +func set_text(value: String, insert: bool = false): + var old_text = text + text = value + + if label == null: + return + + gap_offsets = _calculate_text_gaps() + if insert == false: + caret_position += text.length() - old_text.length() + else: + caret_position = 0 + + overflow_index = _calculate_overflow_index() + focus_caret() + +func get_display_text(): + # In case all chars fit, return the whole text. + if overflow_index == -1: + return text.substr(char_offset) + return text.substr(char_offset, overflow_index - char_offset) + +func focus_caret(): + if overflow_index == -1: + char_offset = 0 + return + + while caret_position > overflow_index: + char_offset += caret_position - overflow_index + overflow_index = _calculate_overflow_index() + + while caret_position < char_offset: + char_offset = caret_position# + overflow_index = _calculate_overflow_index() + +func get_caret_position(): + return gap_offsets[caret_position] - gap_offsets[char_offset] + +func update_caret_position(click_pos_x: float): + caret_position = _calculate_caret_position(click_pos_x) + print("caret_position: ", caret_position) + +func _calculate_caret_position(click_pos_x: float): + for i in range(1, gap_offsets.size()): + var left = gap_offsets[i] - gap_offsets[char_offset] + + if click_pos_x < left: + return i - 1 + + return gap_offsets.size() - 1 + +func _calculate_text_gaps(): + var font = label.get_font() + var offsets = [0.0] + var offset = 0.0 + + for i in range(text.length()): + var character = text[i] + var size = font.get_string_size(character, HORIZONTAL_ALIGNMENT_CENTER, -1, label.font_size) + + offset += size.x * label.pixel_size + offsets.append(offset) + + return offsets + +## Returns the index of the last character that fits in the width. +## If all characters fit, returns -1. +func _calculate_overflow_index(): + + for i in range(char_offset, gap_offsets.size()): + if gap_offsets[i] - gap_offsets[char_offset] >= width: + return i - 1 + return -1 diff --git a/content/ui/menu/settings/settings_menu.tscn b/content/ui/menu/settings/settings_menu.tscn index 628e466..f7aa1f9 100644 --- a/content/ui/menu/settings/settings_menu.tscn +++ b/content/ui/menu/settings/settings_menu.tscn @@ -43,6 +43,7 @@ horizontal_alignment = 0 [node name="InputURL" parent="Content" instance=ExtResource("4_q3x6k")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.18, 0, 0.03) +width = 0.2 text = "ws://192.168.33.33:8123" [node name="LabelToken" type="Label3D" parent="Content"] @@ -54,7 +55,8 @@ horizontal_alignment = 0 [node name="InputToken" parent="Content" instance=ExtResource("4_q3x6k")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.18, 0, 0.07) -text = "paste token here" +width = 0.2 +text = "paste token here paste token here" [node name="LabelConnect" type="Label3D" parent="Content"] transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.15, 0, 0.12) From e1f99699863b2343b31b9b9a0efa1e9d12a3b2e4 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Thu, 23 Nov 2023 19:19:30 +0100 Subject: [PATCH 12/13] Finish caret moving on input --- content/ui/components/input/input.gd | 41 ++++++++++++++++++++- content/ui/components/input/text_handler.gd | 13 ++++--- content/ui/menu/room/room_menu.gd | 6 +-- content/ui/menu/settings/settings_menu.tscn | 4 +- 4 files changed, 51 insertions(+), 13 deletions(-) diff --git a/content/ui/components/input/input.gd b/content/ui/components/input/input.gd index f380a83..e5df071 100644 --- a/content/ui/components/input/input.gd +++ b/content/ui/components/input/input.gd @@ -26,6 +26,7 @@ var text_handler = preload("res://content/ui/components/input/text_handler.gd"). var keyboard_input: bool = false +var input_plane = Plane(Vector3.UP, Vector3.ZERO) func _ready(): text_handler.label = label @@ -71,15 +72,51 @@ func set_width(value: float): collision.shape.size.x = value label.position.x = -value / 2 + 0.002 -func _on_focus_in(event): - var pos_x = label.to_local(event.ray.get_collision_point()).x +func _on_press_move(event): + var ray_pos = event.ray.global_position + var ray_dir = -event.ray.global_transform.basis.z + var local_pos = label.to_local(ray_pos) + var local_dir = label.global_transform.basis.inverse() * ray_dir + + var intersection_point = input_plane.intersects_ray(local_pos, local_dir) + + if intersection_point == null: + return + + var pos_x = intersection_point.x text_handler.update_caret_position(pos_x) caret.position.x = text_handler.get_caret_position() + label.text = text_handler.get_display_text() + +func _on_focus_in(event): + var pos_x = label.to_local(event.ray.get_collision_point()).x + text_handler.update_caret_position(pos_x) + + caret.position.x = text_handler.get_caret_position() + label.text = text_handler.get_display_text() caret.show() animation.play("blink") +func update_caret_position(event): + var ray_pos = event.ray.global_position + var ray_dir = -event.ray.global_transform.basis.z + + var local_pos = label.to_local(ray_pos) + var local_dir = label.global_transform.basis.inverse() * ray_dir + + var intersection_point = input_plane.intersects_ray(local_pos, local_dir) + + if intersection_point == null: + return + + var pos_x = intersection_point.x + text_handler.update_caret_position(pos_x) + + caret.position.x = text_handler.get_caret_position() + + func _on_focus_out(_event): animation.stop() caret.hide() diff --git a/content/ui/components/input/text_handler.gd b/content/ui/components/input/text_handler.gd index 9b75af1..8a8bf35 100644 --- a/content/ui/components/input/text_handler.gd +++ b/content/ui/components/input/text_handler.gd @@ -44,17 +44,21 @@ func focus_caret(): while caret_position > overflow_index: char_offset += caret_position - overflow_index overflow_index = _calculate_overflow_index() + if overflow_index == -1: + break while caret_position < char_offset: - char_offset = caret_position# + char_offset = caret_position overflow_index = _calculate_overflow_index() + if overflow_index == -1: + break func get_caret_position(): return gap_offsets[caret_position] - gap_offsets[char_offset] func update_caret_position(click_pos_x: float): caret_position = _calculate_caret_position(click_pos_x) - print("caret_position: ", caret_position) + focus_caret() func _calculate_caret_position(click_pos_x: float): for i in range(1, gap_offsets.size()): @@ -79,11 +83,8 @@ func _calculate_text_gaps(): return offsets -## Returns the index of the last character that fits in the width. -## If all characters fit, returns -1. func _calculate_overflow_index(): - for i in range(char_offset, gap_offsets.size()): if gap_offsets[i] - gap_offsets[char_offset] >= width: return i - 1 - return -1 + return gap_offsets.size() - 1 diff --git a/content/ui/menu/room/room_menu.gd b/content/ui/menu/room/room_menu.gd index 144b8f2..df3f0e3 100644 --- a/content/ui/menu/room/room_menu.gd +++ b/content/ui/menu/room/room_menu.gd @@ -25,8 +25,8 @@ func _ready(): add_corner(event.ray.get_collision_point()) ) - toggle_edit_button.get_node("Clickable").on_click.connect(func(event): - edit_enabled = event.active + toggle_edit_button.get_node("Clickable").on_press_up.connect(func(event): + edit_enabled = event.target.active if edit_enabled == false: wall_corners.visible = false @@ -131,7 +131,7 @@ func add_corner(position: Vector3): if moving == null: return - var direction = (event.ray.to_global(event.ray.target_position) - event.ray.global_position).normalized() + var direction = -event.ray.global_transform.basis.z var new_position = ground_plane.intersects_ray(event.ray.global_position, direction) if new_position == null: diff --git a/content/ui/menu/settings/settings_menu.tscn b/content/ui/menu/settings/settings_menu.tscn index f7aa1f9..2587855 100644 --- a/content/ui/menu/settings/settings_menu.tscn +++ b/content/ui/menu/settings/settings_menu.tscn @@ -44,7 +44,7 @@ horizontal_alignment = 0 [node name="InputURL" parent="Content" instance=ExtResource("4_q3x6k")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.18, 0, 0.03) width = 0.2 -text = "ws://192.168.33.33:8123" +text = "" [node name="LabelToken" type="Label3D" parent="Content"] transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.01, 0, 0.07) @@ -56,7 +56,7 @@ horizontal_alignment = 0 [node name="InputToken" parent="Content" instance=ExtResource("4_q3x6k")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.18, 0, 0.07) width = 0.2 -text = "paste token here paste token here" +text = "" [node name="LabelConnect" type="Label3D" parent="Content"] transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.15, 0, 0.12) From eb327121b8ca5de9e023c5c75a87055897a23562 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Fri, 24 Nov 2023 01:36:31 +0100 Subject: [PATCH 13/13] rework to HomeApi and fix device loading --- README.md | 6 +- assets/icons/wifi_white_24dp.svg | 3 + assets/icons/wifi_white_24dp.svg.import | 39 ++++++++ content/entities/light/light.gd | 8 +- content/entities/sensor/sensor.gd | 4 +- content/entities/switch/switch.gd | 6 +- content/system/raycast/raycast.gd | 4 +- content/ui/components/input/input.gd | 3 +- content/ui/components/input/input.tscn | 8 +- content/ui/menu/edit/edit_menu.gd | 25 ++++- content/ui/menu/settings/ball.tscn | 2 +- content/ui/menu/settings/settings_menu.gd | 13 +-- content/ui/menu/settings/settings_menu.tscn | 26 +++-- lib/globals/home_adapters.gd | 11 --- lib/globals/home_api.gd | 94 +++++++++++++++++++ lib/home_adapters/adapter.gd | 61 ------------ lib/{home_adapters => home_apis}/hass/hass.gd | 4 +- .../hass/templates/devices.j2 | 0 .../hass_ws/callback_map.gd | 0 .../hass_ws/hass.gd | 27 ++---- .../hass_ws/templates/devices.j2 | 0 project.godot | 2 +- 22 files changed, 212 insertions(+), 134 deletions(-) create mode 100644 assets/icons/wifi_white_24dp.svg create mode 100644 assets/icons/wifi_white_24dp.svg.import delete mode 100644 lib/globals/home_adapters.gd create mode 100644 lib/globals/home_api.gd delete mode 100644 lib/home_adapters/adapter.gd rename lib/{home_adapters => home_apis}/hass/hass.gd (98%) rename lib/{home_adapters => home_apis}/hass/templates/devices.j2 (100%) rename lib/{home_adapters => home_apis}/hass_ws/callback_map.gd (100%) rename lib/{home_adapters => home_apis}/hass_ws/hass.gd (96%) rename lib/{home_adapters => home_apis}/hass_ws/templates/devices.j2 (100%) diff --git a/README.md b/README.md index b66e0f6..455dde4 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ In order to contribute to this project, you need the following to be setup befor ## Fundamentals -Communication with the Smart Home Environment is done using the `HomeAdapters` global. Each environment is made up of devices and entities. +Communication with the Smart Home Environment is done using the `HomeApi` global. Each environment is made up of devices and entities. A device is a collection of different entities and entities can represent many different things in a smart home. For example, the entity of name `lights.smart_lamp_1` would control the kitchen lamps while `state.smart_lamp_1_temp` would show the current temperature of the lamp. @@ -55,9 +55,9 @@ For example, the entity of name `lights.smart_lamp_1` would control the kitchen └── home_adapters (Code allowing control smart home entities) ``` -### Home Adapters +### Home Api -The `HomeAdapters` global allows to communicate with different backends and offers a set of fundamental functions allowing communication with the Smart Home. +The `HomeApi` global allows to communicate with different backends and offers a set of fundamental functions allowing communication with the Smart Home. ```python Device { diff --git a/assets/icons/wifi_white_24dp.svg b/assets/icons/wifi_white_24dp.svg new file mode 100644 index 0000000..0c6dad7 --- /dev/null +++ b/assets/icons/wifi_white_24dp.svg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:053d1e42a72e988fad4b2ef981eb72ec850f1f7da6963b257c8935e8392cb37b +size 324 diff --git a/assets/icons/wifi_white_24dp.svg.import b/assets/icons/wifi_white_24dp.svg.import new file mode 100644 index 0000000..60fc20e --- /dev/null +++ b/assets/icons/wifi_white_24dp.svg.import @@ -0,0 +1,39 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://co5fgm68t4j6o" +path.s3tc="res://.godot/imported/wifi_white_24dp.svg-9edf937c7c00e607b2e1a7211dd6ea49.s3tc.ctex" +path.etc2="res://.godot/imported/wifi_white_24dp.svg-9edf937c7c00e607b2e1a7211dd6ea49.etc2.ctex" +metadata={ +"imported_formats": ["s3tc_bptc", "etc2_astc"], +"vram_texture": true +} + +[deps] + +source_file="res://assets/icons/wifi_white_24dp.svg" +dest_files=["res://.godot/imported/wifi_white_24dp.svg-9edf937c7c00e607b2e1a7211dd6ea49.s3tc.ctex", "res://.godot/imported/wifi_white_24dp.svg-9edf937c7c00e607b2e1a7211dd6ea49.etc2.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 +svg/scale=8.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/content/entities/light/light.gd b/content/entities/light/light.gd index e3a768b..92f97ea 100644 --- a/content/entities/light/light.gd +++ b/content/entities/light/light.gd @@ -13,10 +13,10 @@ var brightness = 0 # 0-255 # Called when the node enters the scene tree for the first time. func _ready(): - var stateInfo = await HomeAdapters.adapter.get_state(entity_id) + var stateInfo = await HomeApi.get_state(entity_id) set_state(stateInfo["state"] == "on") - await HomeAdapters.adapter.watch_state(entity_id, func(new_state): + await HomeApi.watch_state(entity_id, func(new_state): if (new_state["state"] == "on") == state: return set_state(new_state["state"] == "on") @@ -44,7 +44,7 @@ func _on_click(event): if !state && brightness != null: attributes["brightness"] = int(brightness) - HomeAdapters.adapter.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) else: _on_clickable_on_click(event) @@ -71,5 +71,5 @@ func _on_clickable_on_click(event): slider_knob.position = new_pos - HomeAdapters.adapter.set_state(entity_id, "on" if state else "off", {"brightness": int(ratio * 255)}) + HomeApi.set_state(entity_id, "on" if state else "off", {"brightness": int(ratio * 255)}) set_state(state, ratio * 255) diff --git a/content/entities/sensor/sensor.gd b/content/entities/sensor/sensor.gd index 149d7bd..5af77dc 100644 --- a/content/entities/sensor/sensor.gd +++ b/content/entities/sensor/sensor.gd @@ -5,10 +5,10 @@ extends StaticBody3D # Called when the node enters the scene tree for the first time. func _ready(): - var stateInfo = await HomeAdapters.adapter.get_state(entity_id) + var stateInfo = await HomeApi.get_state(entity_id) set_text(stateInfo) - await HomeAdapters.adapter.watch_state(entity_id, func(new_state): + await HomeApi.watch_state(entity_id, func(new_state): set_text(new_state) ) diff --git a/content/entities/switch/switch.gd b/content/entities/switch/switch.gd index 3239121..c2df949 100644 --- a/content/entities/switch/switch.gd +++ b/content/entities/switch/switch.gd @@ -5,7 +5,7 @@ extends StaticBody3D # Called when the node enters the scene tree for the first time. func _ready(): - var stateInfo = await HomeAdapters.adapter.get_state(entity_id) + var stateInfo = await HomeApi.get_state(entity_id) if stateInfo == null: return @@ -14,7 +14,7 @@ func _ready(): else: sprite.set_frame(1) - await HomeAdapters.adapter.watch_state(entity_id, func(new_state): + await HomeApi.watch_state(entity_id, func(new_state): if new_state["state"] == "on": sprite.set_frame(0) else: @@ -23,7 +23,7 @@ func _ready(): func _on_click(event): - HomeAdapters.adapter.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on") + HomeApi.set_state(entity_id, "off" if sprite.get_frame() == 0 else "on") if sprite.get_frame() == 0: sprite.set_frame(1) else: diff --git a/content/system/raycast/raycast.gd b/content/system/raycast/raycast.gd index 3b9d6ce..5c4cca9 100644 --- a/content/system/raycast/raycast.gd +++ b/content/system/raycast/raycast.gd @@ -84,11 +84,11 @@ func _on_button_released(button: String): last_collided = null moved = false -func _emit_event(type: String, target: Object): +func _emit_event(type: String, target): var event = EventRay.new() event.controller = controller event.target = target event.ray = self event.is_right_controller = is_right - EventSystem.emit(type, event) \ No newline at end of file + EventSystem.emit(type, event) diff --git a/content/ui/components/input/input.gd b/content/ui/components/input/input.gd index e5df071..5633288 100644 --- a/content/ui/components/input/input.gd +++ b/content/ui/components/input/input.gd @@ -19,7 +19,8 @@ var text_handler = preload("res://content/ui/components/input/text_handler.gd"). get: return text_handler.text set(value): - text_handler.set_text(value, EventSystem.is_focused(self) == false) + var focused = Engine.is_editor_hint() == false && EventSystem.is_focused(self) == false + text_handler.set_text(value, focused) if label != null: label.text = text_handler.get_display_text() diff --git a/content/ui/components/input/input.tscn b/content/ui/components/input/input.tscn index 633210b..f5a390e 100644 --- a/content/ui/components/input/input.tscn +++ b/content/ui/components/input/input.tscn @@ -5,11 +5,11 @@ [sub_resource type="BoxMesh" id="BoxMesh_kjbca"] resource_local_to_scene = true -size = Vector3(0.15, 0.006, 0.03) +size = Vector3(0.2, 0.006, 0.03) [sub_resource type="BoxShape3D" id="BoxShape3D_x4yp8"] resource_local_to_scene = true -size = Vector3(0.15, 0.006, 0.03) +size = Vector3(0.2, 0.006, 0.03) [sub_resource type="SystemFont" id="SystemFont_nbea0"] @@ -55,7 +55,7 @@ _data = { [node name="Input" type="StaticBody3D" groups=["ui_focus"]] script = ExtResource("1_uml3t") -text = "Hello World" +width = 0.2 [node name="Box" type="MeshInstance3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.003, 0) @@ -67,7 +67,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.003, 0) shape = SubResource("BoxShape3D_x4yp8") [node name="Label" type="Label3D" parent="."] -transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -0.073, 0.00618291, 0) +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -0.098, 0.00618291, 0) pixel_size = 0.0004 text = "Hello World" font = SubResource("SystemFont_nbea0") diff --git a/content/ui/menu/edit/edit_menu.gd b/content/ui/menu/edit/edit_menu.gd index 04589eb..4804e7e 100644 --- a/content/ui/menu/edit/edit_menu.gd +++ b/content/ui/menu/edit/edit_menu.gd @@ -19,11 +19,6 @@ var pages = 0 var selected_device = null # Called when the node enters the scene tree for the first time. func _ready(): - HomeAdapters.adapter.adapter.on_connect.connect(func(): - devices = await HomeAdapters.adapter.get_devices() - render() - ) - next_page_button.get_node("Clickable").on_click.connect(func(_event): print("next page") next_page() @@ -33,6 +28,26 @@ func _ready(): previous_page() ) +func _enter_tree(): + if HomeApi.has_connected(): + load_devices() + else: + HomeApi.on_connect.connect(func(): + if is_inside_tree(): + load_devices() + ) + +func load_devices(): + if devices.size() == 0: + devices = await HomeApi.get_devices() + render() + + HomeApi.on_disconnect.connect(func(): + devices = [] + if is_inside_tree(): + render() + ) + func update_pages(): if selected_device == null: pages = ceil(float(devices.size()) / page_size) diff --git a/content/ui/menu/settings/ball.tscn b/content/ui/menu/settings/ball.tscn index 5158fa8..03a7b90 100644 --- a/content/ui/menu/settings/ball.tscn +++ b/content/ui/menu/settings/ball.tscn @@ -8,7 +8,7 @@ radius = 0.08 height = 0.16 [node name="Ball" type="RigidBody3D"] -angular_damp = 39.224 +angular_damp = 4.0 [node name="CollisionShape3D" type="CollisionShape3D" parent="."] shape = SubResource("SphereShape3D_orlq6") diff --git a/content/ui/menu/settings/settings_menu.gd b/content/ui/menu/settings/settings_menu.gd index 1eabd4d..1138dfd 100644 --- a/content/ui/menu/settings/settings_menu.gd +++ b/content/ui/menu/settings/settings_menu.gd @@ -25,22 +25,23 @@ func _ready(): input_token.text = config["token"] button_connect.on_button_down.connect(func(): - HomeAdapters.adapter.adapter.url = input_url.text + "/api/websocket" - HomeAdapters.adapter.adapter.token = input_token.text - HomeAdapters.adapter.adapter.retries = 5 - HomeAdapters.adapter.adapter.connect_ws() + var url = input_url.text + "/api/websocket" + var token = input_token.text + + HomeApi.start_adapter("hass_ws", url, token) ConfigData.save_config({ + "api_type": "hass_ws", "url": input_url.text, "token": input_token.text }) ) - HomeAdapters.adapter.adapter.on_connect.connect(func(): + HomeApi.on_connect.connect(func(): connection_status.text = "Connected" ) - HomeAdapters.adapter.adapter.on_disconnect.connect(func(): + HomeApi.on_disconnect.connect(func(): connection_status.text = "Disconnected" ) diff --git a/content/ui/menu/settings/settings_menu.tscn b/content/ui/menu/settings/settings_menu.tscn index 2587855..b4a7f00 100644 --- a/content/ui/menu/settings/settings_menu.tscn +++ b/content/ui/menu/settings/settings_menu.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=6 format=3 uid="uid://c6r4higceibif"] +[gd_scene load_steps=7 format=3 uid="uid://c6r4higceibif"] [ext_resource type="Script" path="res://content/ui/menu/settings/settings_menu.gd" id="1_0lte6"] [ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="1_faxng"] [ext_resource type="Script" path="res://content/functions/clickable.gd" id="3_qmg6q"] [ext_resource type="PackedScene" uid="uid://blrhy2uccrdn4" path="res://content/ui/components/input/input.tscn" id="4_q3x6k"] +[ext_resource type="Texture2D" uid="uid://co5fgm68t4j6o" path="res://assets/icons/wifi_white_24dp.svg" id="5_muw54"] [sub_resource type="BoxMesh" id="BoxMesh_e51x8"] size = Vector3(0.3, 0.01, 0.3) @@ -30,40 +31,45 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0458097, 0, 0.253575) script = ExtResource("3_qmg6q") [node name="ConnectionStatus" type="Label3D" parent="Content"] -transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.26, 0, 0.29) +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.250698, 0, 0.161303) pixel_size = 0.0003 text = "Disconnected" [node name="LabelURL" type="Label3D" parent="Content"] transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.01, 0, 0.03) pixel_size = 0.0005 -text = "URL" +text = "url: +" font_size = 36 horizontal_alignment = 0 [node name="InputURL" parent="Content" instance=ExtResource("4_q3x6k")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.18, 0, 0.03) -width = 0.2 -text = "" +text = "ws://192.168.33.33:8123" [node name="LabelToken" type="Label3D" parent="Content"] transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.01, 0, 0.07) pixel_size = 0.0005 -text = "TOKEN" +text = "token:" font_size = 36 horizontal_alignment = 0 [node name="InputToken" parent="Content" instance=ExtResource("4_q3x6k")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.18, 0, 0.07) -width = 0.2 -text = "" +text = "..." [node name="LabelConnect" type="Label3D" parent="Content"] -transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.15, 0, 0.12) +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.14, 0, 0.12) pixel_size = 0.0005 -text = "TOKEN" +text = "Connect" font_size = 36 horizontal_alignment = 0 [node name="Connect" parent="Content" instance=ExtResource("1_faxng")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.25, 0, 0.12) + +[node name="Sprite3D" type="Sprite3D" parent="Content"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.25, 0.012, 0.12) +pixel_size = 0.0002 +axis = 1 +texture = ExtResource("5_muw54") diff --git a/lib/globals/home_adapters.gd b/lib/globals/home_adapters.gd deleted file mode 100644 index 275327c..0000000 --- a/lib/globals/home_adapters.gd +++ /dev/null @@ -1,11 +0,0 @@ -extends Node - -const Adapter = preload("res://lib/home_adapters/adapter.gd") - -var adapter = Adapter.new(Adapter.ADAPTER_TYPES.HASS_WS) -# var adapter_http = Adapter.new(Adapter.ADAPTER_TYPES.HASS) - -func _ready(): - add_child(adapter) - # add_child(adapter_http) - diff --git a/lib/globals/home_api.gd b/lib/globals/home_api.gd new file mode 100644 index 0000000..245c052 --- /dev/null +++ b/lib/globals/home_api.gd @@ -0,0 +1,94 @@ +extends Node + +const Hass = preload("res://lib/home_apis/hass/hass.gd") +const HassWebSocket = preload("res://lib/home_apis/hass_ws/hass.gd") + + +const apis = { + "hass": Hass, + "hass_ws": HassWebSocket +} + +const methods = [ + "get_devices", + "get_device", + "get_state", + "set_state", + "watch_state" +] + +signal on_connect() +signal on_disconnect() +var api: Node + +func _ready(): + print("HomeApi ready") + + var config = ConfigData.load_config() + + if config.has("api_type") && config.has("url") && config.has("token"): + var type = config["api_type"] + var url = config["url"] + "/api/websocket" + var token = config["token"] + + start_adapter(type, url, token) + + +func start_adapter(type: String, url: String, token: String): + print("Starting adapter: %s" % type) + if api != null: + api.on_connect.disconnect(_on_connect) + api.on_disconnect.disconnect(_on_disconnect) + remove_child(api) + api.queue_free() + api = null + + api = apis[type].new(url, token) + add_child(api) + + api.on_connect.connect(func(): + on_connect.emit() + ) + + api.on_disconnect.connect(func(): + on_disconnect.emit() + ) + + for method in methods: + assert(api.has_method(method), "%s Api does not implement method: %s" % [type, method]) + +func _on_connect(): + on_connect.emit() + +func _on_disconnect(): + on_disconnect.emit() + +func has_connected(): + if api == null: + return false + return api.has_connected() + +## Get a list of all devices +func get_devices(): + assert(has_connected(), "Not connected") + return await api.get_devices() + +## Get a single device by id +func get_device(id: String): + assert(has_connected(), "Not connected") + return await api.get_device(id) + +## Returns the current state of an entity +func get_state(entity: String): + assert(has_connected(), "Not connected") + return await api.get_state(entity) + +## Updates the state of the entity and returns the resulting state +func set_state(entity: String, state: String, attributes: Dictionary = {}): + assert(has_connected(), "Not connected") + return await api.set_state(entity, state, attributes) + +## Watches the state and each time it changes, calls the callback with the changed state, returns a function to stop watching the state +func watch_state(entity: String, callback: Callable): + assert(has_connected(), "Not connected") + return api.watch_state(entity, callback) diff --git a/lib/home_adapters/adapter.gd b/lib/home_adapters/adapter.gd deleted file mode 100644 index 5942bda..0000000 --- a/lib/home_adapters/adapter.gd +++ /dev/null @@ -1,61 +0,0 @@ -extends Node - -const Hass = preload("res://lib/home_adapters/hass/hass.gd") -const HassWebSocket = preload("res://lib/home_adapters/hass_ws/hass.gd") - -enum ADAPTER_TYPES { - HASS, - HASS_WS -} - -const adapters = { - ADAPTER_TYPES.HASS: Hass, - ADAPTER_TYPES.HASS_WS: HassWebSocket -} - -const methods = [ - "get_devices", - "get_device", - "get_state", - "set_state", - "watch_state" -] - -var adapter: Node - -func _init(type: ADAPTER_TYPES): - - var url = "" - var token = "" - var config = ConfigData.load_config() - - if config.has("url"): - url = config["url"] + "/api/websocket" - if config.has("token"): - token = config["token"] - - adapter = adapters[type].new(url, token) - add_child(adapter) - - for method in methods: - assert(adapter.has_method(method), "Adapter does not implement method: " + method) - -## Get a list of all devices -func get_devices(): - return await adapter.get_devices() - -## Get a single device by id -func get_device(id: String): - return await adapter.get_device(id) - -## Returns the current state of an entity -func get_state(entity: String): - return await adapter.get_state(entity) - -## Updates the state of the entity and returns the resulting state -func set_state(entity: String, state: String, attributes: Dictionary = {}): - return await adapter.set_state(entity, state, attributes) - -## Watches the state and each time it changes, calls the callback with the changed state, returns a function to stop watching the state -func watch_state(entity: String, callback: Callable): - return adapter.watch_state(entity, callback) diff --git a/lib/home_adapters/hass/hass.gd b/lib/home_apis/hass/hass.gd similarity index 98% rename from lib/home_adapters/hass/hass.gd rename to lib/home_apis/hass/hass.gd index 3924522..88e101c 100644 --- a/lib/home_adapters/hass/hass.gd +++ b/lib/home_apis/hass/hass.gd @@ -4,7 +4,7 @@ var url: String = "http://192.168.33.33:8123" var token: String = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzZjQ0ZGM2N2Y3YzY0MDc1OGZlMWI2ZjJlNmIxZjRkNSIsImlhdCI6MTY5ODAxMDcyOCwiZXhwIjoyMDEzMzcwNzI4fQ.K6ydLUC-4Q7BNIRCU1nWlI2s6sg9UCiOu-Lpedw2zJc" var headers: PackedStringArray = PackedStringArray([]) -var devices_template = FileAccess.get_file_as_string("res://lib/home_adapters/hass/templates/devices.j2") +var devices_template = FileAccess.get_file_as_string("res://lib/home_apis/hass/templates/devices.j2") func _init(url := self.url, token := self.token): self.url = url @@ -32,8 +32,6 @@ func get_state(entity: String): return json - - func set_state(entity: String, state: String, attributes: Dictionary = {}): var type = entity.split('.')[0] var response diff --git a/lib/home_adapters/hass/templates/devices.j2 b/lib/home_apis/hass/templates/devices.j2 similarity index 100% rename from lib/home_adapters/hass/templates/devices.j2 rename to lib/home_apis/hass/templates/devices.j2 diff --git a/lib/home_adapters/hass_ws/callback_map.gd b/lib/home_apis/hass_ws/callback_map.gd similarity index 100% rename from lib/home_adapters/hass_ws/callback_map.gd rename to lib/home_apis/hass_ws/callback_map.gd diff --git a/lib/home_adapters/hass_ws/hass.gd b/lib/home_apis/hass_ws/hass.gd similarity index 96% rename from lib/home_adapters/hass_ws/hass.gd rename to lib/home_apis/hass_ws/hass.gd index 6406f13..b163183 100644 --- a/lib/home_adapters/hass_ws/hass.gd +++ b/lib/home_apis/hass_ws/hass.gd @@ -1,6 +1,10 @@ extends Node -var devices_template := FileAccess.get_file_as_string("res://lib/home_adapters/hass_ws/templates/devices.j2") +signal on_connect() +signal on_disconnect() +var connected := false + +var devices_template := FileAccess.get_file_as_string("res://lib/home_apis/hass_ws/templates/devices.j2") var socket := WebSocketPeer.new() # in seconds var request_timeout := 10.0 @@ -13,7 +17,7 @@ var token := "" var LOG_MESSAGES := false var authenticated := false -var loading := true + var id := 1 var entities: Dictionary = {} var retries := 5 @@ -21,9 +25,6 @@ var retries := 5 var entitiy_callbacks := CallbackMap.new() var packet_callbacks := CallbackMap.new() -signal on_connect() -signal on_disconnect() - func _init(url := self.url, token := self.token): self.url = url self.token = token @@ -114,7 +115,7 @@ func start_subscriptions(): "attributes": packet.event.a[entity]["a"] } entitiy_callbacks.call_key(entity, [entities[entity]]) - loading = false + connected = true on_connect.emit() if packet.event.has("c"): @@ -201,10 +202,10 @@ func decode_packet(packet: PackedByteArray): func encode_packet(packet: Dictionary): return JSON.stringify(packet) -func get_devices(): - if loading: - await on_connect +func has_connected(): + return connected +func get_devices(): var result = await send_request_packet({ "type": "render_template", "template": devices_template, @@ -218,18 +219,12 @@ func get_device(id: String): pass func get_state(entity: String): - if loading: - await on_connect - if entities.has(entity): return entities[entity] return null func watch_state(entity: String, callback: Callable): - if loading: - await on_connect - entitiy_callbacks.add(entity, callback) return func(): @@ -237,8 +232,6 @@ func watch_state(entity: String, callback: Callable): func set_state(entity: String, state: String, attributes: Dictionary = {}): - assert(!loading, "Still loading") - var domain = entity.split(".")[0] var service: String diff --git a/lib/home_adapters/hass_ws/templates/devices.j2 b/lib/home_apis/hass_ws/templates/devices.j2 similarity index 100% rename from lib/home_adapters/hass_ws/templates/devices.j2 rename to lib/home_apis/hass_ws/templates/devices.j2 diff --git a/project.godot b/project.godot index 03f59f8..69cb30a 100644 --- a/project.godot +++ b/project.godot @@ -20,7 +20,7 @@ config/icon="res://assets/logo.png" XRToolsUserSettings="*res://addons/godot-xr-tools/user_settings/user_settings.gd" ConfigData="*res://lib/globals/config_data.gd" Request="*res://lib/globals/request.gd" -HomeAdapters="*res://lib/globals/home_adapters.gd" +HomeApi="*res://lib/globals/home_api.gd" AudioPlayer="*res://lib/globals/audio_player.gd" EventSystem="*res://lib/globals/event_system.gd"