Merge pull request #43 from Nitwel/testing
Add keyboard, UI Inputs and a lot of bug fixes
This commit is contained in:
commit
affbb7e91d
3
.gitattributes
vendored
3
.gitattributes
vendored
|
@ -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
|
||||
|
|
1
.github/workflows/build-prod.yml
vendored
1
.github/workflows/build-prod.yml
vendored
|
@ -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
|
||||
|
|
24
.github/workflows/release.yml
vendored
Normal file
24
.github/workflows/release.yml
vendored
Normal file
|
@ -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 }}
|
48
README.md
48
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 {
|
||||
|
@ -95,25 +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
|
||||
}
|
||||
```
|
||||
|
||||
| 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_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 |
|
||||
| `ui_focus_skip` | The focus will not be reset. Useful for keyboard |
|
||||
|
||||
|
||||
### Functions
|
||||
|
||||
|
|
21
addons/debug_draw_3d/LICENSE
Normal file
21
addons/debug_draw_3d/LICENSE
Normal file
|
@ -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.
|
183
addons/debug_draw_3d/README.md
Normal file
183
addons/debug_draw_3d/README.md
Normal file
|
@ -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)
|
||||
|
||||
[<img src="https://upload.wikimedia.org/wikipedia/commons/8/8f/QIWI_logo.svg" alt="qiwi" width=90px/>](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
|
||||
```
|
55
addons/debug_draw_3d/debug_draw_3d.gdextension
Normal file
55
addons/debug_draw_3d/debug_draw_3d.gdextension
Normal file
|
@ -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"
|
0
addons/debug_draw_3d/libs/.gdignore
Normal file
0
addons/debug_draw_3d/libs/.gdignore
Normal file
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0fb41898f762f555f379bc351a0d2d82bc9cbf3b701a1f9fe8035646ddad44b5
|
||||
size 2928328
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1dfdb52ffe2e428ea0a0b9ff951b986546291f383f37c957d7de4c367f79892f
|
||||
size 2993032
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5c580b7b4d44342c56d1990ab09358b63fa60cf99846a355c86ceb2cb5023b14
|
||||
size 3057172
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b6c2b95269938e60e709539c8166cd9e5f450bd3fbd2b6bdfdf652fca3f594db
|
||||
size 2977744
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1c40f93bd53bc4b132d075f18814b1198e71df6b9f2601c11f0cfbc2494698cf
|
||||
size 1804756
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0181cc7c1a08ecc39de983b1b1e35f66572479da533f744190fa803cbde44612
|
||||
size 2001576
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:24497f5a2ad8a7d99e2acb62d6c526017d7c365f8fd7fd6ae3042d96bd6e980a
|
||||
size 1996072
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6eab3ff00fc2d7347a6b166e67a3800d6117fa84bcfa3317429d6e0bff896654
|
||||
size 2028472
|
3
addons/debug_draw_3d/libs/libdd3d.linux.editor.x86_64.so
Normal file
3
addons/debug_draw_3d/libs/libdd3d.linux.editor.x86_64.so
Normal file
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9b39c1ca40f0a22e66e742daba8e050884caa7a3ff959824b0c99a7e061fd869
|
||||
size 3806144
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4bfbec2037276c6858cbfc4019bb0381eb9bd83cdcdfcfa3cc802bfc0d5f427b
|
||||
size 3072224
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7efcb3de6de7d5a878c4a221931a94a74f3741a6aeb7d7e9a4f19fb07bf1115c
|
||||
size 2193840
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:818647a1234354725573918ce71972193812dd19d9ffb4bbe35f4a17ff48341f
|
||||
size 5739144
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5bbf87c77f9c9fe6643948257e91f271825a2874172c97cdbd915a6203a6eff3
|
||||
size 3339680
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b0f280b00b057266534e477e4b33e476f108b41d1927d4db39e8d1e3a781adc5
|
||||
size 4689496
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ac3ac3647171c53e2f47564df2e25eff19fbfab8bf763d0078fe1c82476d8761
|
||||
size 1291264
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3d3113daaa53fe431ca8ce55f127c0e41ef4243ab5aadd359dc6d2c63b31f057
|
||||
size 680448
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:af5ae3a80d09e6212d21e4cf608aa38f711d230914918d0f2f427bdcc5488e24
|
||||
size 862208
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8bc1c66cc868d68efe828c304c6ed348136cdfd8b814fe0f243954f328648997
|
||||
size 3586528
|
||||
oid sha256:84daccd9540dfcf964fc4ec224ed88238f3a9fac55a30d1f48161d1ede0a7907
|
||||
size 2804209
|
||||
|
|
3
assets/icons/wifi_white_24dp.svg
Normal file
3
assets/icons/wifi_white_24dp.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:053d1e42a72e988fad4b2ef981eb72ec850f1f7da6963b257c8935e8392cb37b
|
||||
size 324
|
39
assets/icons/wifi_white_24dp.svg.import
Normal file
39
assets/icons/wifi_white_24dp.svg.import
Normal file
|
@ -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
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6f10630e063d8d85238a4487595ec7869c79798ae7547ea0f636d6c78e0dc178
|
||||
size 85601
|
||||
oid sha256:fc77fb979fd1cec813e7bf6002ad05da443097ca5e04712f36f65a1dbc17cef2
|
||||
size 85732
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:55d5f30db336a8f8f7633743c23e41077d5c1a1b900c475a6d3811746eba3d1b
|
||||
size 141
|
3
assets/materials/ui_element.material
Normal file
3
assets/materials/ui_element.material
Normal file
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:57f8bfca31acba3de3a15a2b9a88b8afd12f8db9c356f14d906840dcee48d6f6
|
||||
size 1021
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:451fcd3ec43720477df80b56031118ea0b947cfa32198bf47ef095b53beadf95
|
||||
size 1656729
|
||||
oid sha256:8da6b8e647a21f76a72096dad5eabd775f668ab0e45bdf8321633d99f894b175
|
||||
size 1656450
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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/system/keyboard/keyboard.tscn" id="9_e5n3p"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_m58yb"]
|
||||
ao_enabled = true
|
||||
|
@ -33,33 +33,20 @@ 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"
|
||||
|
||||
[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.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)
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
@ -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")]
|
101
content/system/keyboard/keyboard.gd
Normal file
101
content/system/keyboard/keyboard.gd
Normal file
|
@ -0,0 +1,101 @@
|
|||
@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
|
||||
@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],
|
||||
[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 :
|
||||
set(value):
|
||||
caps = value
|
||||
update_labels()
|
||||
|
||||
func _ready():
|
||||
for row in key_list:
|
||||
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)
|
||||
)
|
||||
button.on_button_up.connect(func():
|
||||
_emit_event("key_up", key)
|
||||
)
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
backspace_button.on_button_up.connect(func():
|
||||
_emit_event("key_up", KEY_BACKSPACE)
|
||||
)
|
||||
|
||||
caps_button.on_button_down.connect(func():
|
||||
caps = true
|
||||
_emit_event("key_down", KEY_CAPSLOCK)
|
||||
)
|
||||
|
||||
caps_button.on_button_up.connect(func():
|
||||
caps = false
|
||||
_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()
|
||||
|
||||
var label = Label3D.new()
|
||||
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_to_group("ui_focus_skip")
|
||||
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))
|
||||
|
52
content/system/keyboard/keyboard.tscn
Normal file
52
content/system/keyboard/keyboard.tscn
Normal file
|
@ -0,0 +1,52 @@
|
|||
[gd_scene load_steps=6 format=3 uid="uid://lrehk38exd5n"]
|
||||
|
||||
[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"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_k5ib7"]
|
||||
size = Vector3(0.84, 0.0402036, 0.296009)
|
||||
|
||||
[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")
|
||||
|
||||
[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
|
||||
|
||||
[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="." 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
|
||||
|
||||
[node name="Label3D" type="Label3D" parent="Caps"]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.012, 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
|
||||
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.296719, -0.025645, 0.0928761)
|
||||
shape = SubResource("BoxShape3D_k5ib7")
|
94
content/system/raycast/raycast.gd
Normal file
94
content/system/raycast/raycast.gd
Normal file
|
@ -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):
|
||||
var event = EventRay.new()
|
||||
event.controller = controller
|
||||
event.target = target
|
||||
event.ray = self
|
||||
event.is_right_controller = is_right
|
||||
|
||||
EventSystem.emit(type, event)
|
16
content/system/raycast/raycast.tscn
Normal file
16
content/system/raycast/raycast.tscn
Normal file
|
@ -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
|
|
@ -1,12 +1,15 @@
|
|||
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 external_state: 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,45 +20,43 @@ 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
|
||||
|
||||
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
|
||||
|
||||
AudioPlayer.play_effect("click")
|
||||
|
||||
if external_state || toggleable:
|
||||
return
|
||||
|
||||
active = true
|
||||
on_button_down.emit()
|
||||
|
||||
|
||||
|
||||
func _on_press_up(event):
|
||||
if disabled:
|
||||
event.bubbling = false
|
||||
return
|
||||
|
||||
if external_state:
|
||||
return
|
||||
|
||||
if toggleable:
|
||||
active = !active
|
||||
AudioPlayer.play_effect("click")
|
||||
|
||||
return {
|
||||
"active": active
|
||||
}
|
||||
|
||||
func _on_press_down(_event):
|
||||
if disabled:
|
||||
return false
|
||||
|
||||
if toggleable:
|
||||
return
|
||||
|
||||
AudioPlayer.play_effect("click")
|
||||
animation_player.play("down")
|
||||
|
||||
func _on_press_up(_event):
|
||||
if disabled:
|
||||
return false
|
||||
|
||||
if toggleable:
|
||||
return
|
||||
|
||||
animation_player.play_backwards("down")
|
||||
if active:
|
||||
on_button_down.emit()
|
||||
else:
|
||||
on_button_up.emit()
|
||||
else:
|
||||
active = false
|
||||
on_button_up.emit()
|
||||
|
|
|
@ -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"]
|
||||
|
@ -81,7 +79,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="."]
|
||||
|
|
135
content/ui/components/input/input.gd
Normal file
135
content/ui/components/input/input.gd
Normal file
|
@ -0,0 +1,135 @@
|
|||
@tool
|
||||
extends StaticBody3D
|
||||
|
||||
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
|
||||
|
||||
@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):
|
||||
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()
|
||||
|
||||
var keyboard_input: bool = false
|
||||
|
||||
var input_plane = Plane(Vector3.UP, Vector3.ZERO)
|
||||
|
||||
func _ready():
|
||||
text_handler.label = label
|
||||
text = text
|
||||
width = width
|
||||
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
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, 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):
|
||||
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, 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():
|
||||
return
|
||||
|
||||
if get_tree().debug_collisions_hint && OS.get_name() != "Android":
|
||||
_draw_debug_text_gaps()
|
||||
|
||||
func set_width(value: float):
|
||||
text_handler.width = value
|
||||
|
||||
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_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()
|
||||
|
||||
func _draw_debug_text_gaps():
|
||||
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) if i != text_handler.overflow_index else Color(0, 1, 0)
|
||||
)
|
84
content/ui/components/input/input.tscn
Normal file
84
content/ui/components/input/input.tscn
Normal file
|
@ -0,0 +1,84 @@
|
|||
[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"]
|
||||
resource_local_to_scene = true
|
||||
size = Vector3(0.2, 0.006, 0.03)
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_x4yp8"]
|
||||
resource_local_to_scene = true
|
||||
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": [false]
|
||||
}
|
||||
|
||||
[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")
|
||||
width = 0.2
|
||||
|
||||
[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="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.098, 0.00618291, 0)
|
||||
pixel_size = 0.0004
|
||||
text = "Hello World"
|
||||
font = SubResource("SystemFont_nbea0")
|
||||
horizontal_alignment = 0
|
||||
|
||||
[node name="Caret" type="MeshInstance3D" parent="Label"]
|
||||
visible = false
|
||||
mesh = SubResource("BoxMesh_2736g")
|
||||
skeleton = NodePath("../..")
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_1sy4t")
|
||||
}
|
90
content/ui/components/input/text_handler.gd
Normal file
90
content/ui/components/input/text_handler.gd
Normal file
|
@ -0,0 +1,90 @@
|
|||
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()
|
||||
if overflow_index == -1:
|
||||
break
|
||||
|
||||
while caret_position < char_offset:
|
||||
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)
|
||||
focus_caret()
|
||||
|
||||
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
|
||||
|
||||
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 gap_offsets.size() - 1
|
|
@ -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"]
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
extends Node3D
|
||||
|
||||
const button_scene = preload("res://content/ui/components/button/button.tscn")
|
||||
|
||||
@onready var keys = $Keys
|
||||
@onready var caps_button = $Caps
|
||||
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", ",", ".", "-"]
|
||||
]
|
||||
|
||||
var caps = false
|
||||
|
||||
func _ready():
|
||||
for row in key_list:
|
||||
for key in row:
|
||||
print(key)
|
||||
var button = create_key(key)
|
||||
keys.add_child(button)
|
||||
|
||||
keys.columns = key_list[0].size()
|
||||
|
||||
func _on_click(event):
|
||||
if event.target == caps_button:
|
||||
caps = event.active
|
||||
return
|
||||
|
||||
var code = event.target.get_children()[event.target.get_child_count() - 1].text
|
||||
|
||||
if caps:
|
||||
code = code.to_upper()
|
||||
else:
|
||||
code = code.to_lower()
|
||||
|
||||
Events.typed.emit(code)
|
||||
print(code)
|
||||
|
||||
func create_key(key: String):
|
||||
var button = button_scene.instantiate()
|
||||
|
||||
var label = Label3D.new()
|
||||
label.text = key
|
||||
label.pixel_size = 0.001
|
||||
label.position = Vector3(0, 0.012, 0)
|
||||
label.rotate_x(deg_to_rad(-90))
|
||||
|
||||
button.add_child(label)
|
||||
|
||||
return button
|
|
@ -1,23 +0,0 @@
|
|||
[gd_scene load_steps=4 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"]
|
||||
|
||||
[node name="Keyboard" type="Node3D"]
|
||||
script = ExtResource("1_maojw")
|
||||
|
||||
[node name="Caps" parent="." instance=ExtResource("1_xdpwr")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0668889, 0, 0.03)
|
||||
toggleable = true
|
||||
|
||||
[node name="Label3D" type="Label3D" parent="Caps"]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.012, 0)
|
||||
pixel_size = 0.001
|
||||
text = "caps"
|
||||
|
||||
[node name="Keys" type="Node3D" parent="."]
|
||||
script = ExtResource("3_mx544")
|
||||
columns = 1
|
||||
depth_gap = 0.06
|
||||
size = Vector3(0.6, 1, 1)
|
|
@ -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,8 +19,6 @@ 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()
|
||||
|
||||
next_page_button.get_node("Clickable").on_click.connect(func(_event):
|
||||
print("next page")
|
||||
next_page()
|
||||
|
@ -30,8 +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)
|
||||
|
@ -62,6 +78,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 +110,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 +177,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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 _is_valid_nav(nav):
|
||||
return nav == nav_view || nav == nav_edit || nav == nav_room || nav == nav_automate || nav == nav_settings
|
||||
|
||||
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 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
|
||||
|
|
|
@ -168,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)
|
||||
|
@ -178,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)
|
||||
|
@ -189,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)
|
||||
|
@ -199,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)
|
||||
|
@ -209,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)
|
||||
|
@ -220,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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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,11 +17,31 @@ func _ready():
|
|||
get_tree().root.add_child(ball)
|
||||
)
|
||||
|
||||
HomeAdapters.adapter.adapter.on_connect.connect(func():
|
||||
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():
|
||||
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
|
||||
})
|
||||
)
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
[gd_scene load_steps=5 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)
|
||||
|
@ -18,17 +20,56 @@ 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")
|
||||
|
||||
[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:
|
||||
"
|
||||
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 = "..."
|
||||
|
||||
[node name="LabelConnect" type="Label3D" parent="Content"]
|
||||
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 = "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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
9
lib/events/event.gd
Normal file
9
lib/events/event.gd
Normal file
|
@ -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))
|
5
lib/events/event_bubble.gd
Normal file
5
lib/events/event_bubble.gd
Normal file
|
@ -0,0 +1,5 @@
|
|||
extends Event
|
||||
class_name EventBubble
|
||||
|
||||
var bubbling := true
|
||||
var target: Node
|
6
lib/events/event_focus.gd
Normal file
6
lib/events/event_focus.gd
Normal file
|
@ -0,0 +1,6 @@
|
|||
extends Event
|
||||
class_name EventFocus
|
||||
|
||||
var ray: RayCast3D
|
||||
var target: Node
|
||||
var previous_target: Node
|
22
lib/events/event_key.gd
Normal file
22
lib/events/event_key.gd
Normal file
|
@ -0,0 +1,22 @@
|
|||
extends EventWithModifiers
|
||||
class_name EventKey
|
||||
|
||||
var key: Key
|
||||
var echo: bool
|
||||
|
||||
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
|
6
lib/events/event_ray.gd
Normal file
6
lib/events/event_ray.gd
Normal file
|
@ -0,0 +1,6 @@
|
|||
extends EventBubble
|
||||
class_name EventRay
|
||||
|
||||
var controller: XRController3D
|
||||
var is_right_controller: bool
|
||||
var ray: RayCast3D
|
7
lib/events/event_with_modifiers.gd
Normal file
7
lib/events/event_with_modifiers.gd
Normal file
|
@ -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
|
23
lib/globals/config_data.gd
Normal file
23
lib/globals/config_data.gd
Normal file
|
@ -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
|
89
lib/globals/event_system.gd
Normal file
89
lib/globals/event_system.gd
Normal file
|
@ -0,0 +1,89 @@
|
|||
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)
|
||||
|
||||
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 type == "press_down":
|
||||
_handle_focus(event)
|
||||
else:
|
||||
_root_call(type, event)
|
||||
|
||||
func is_focused(node: Node):
|
||||
return _active_node == node
|
||||
|
||||
func _handle_focus(event: EventRay):
|
||||
if event.target != null && event.target.is_in_group("ui_focus_skip"):
|
||||
return
|
||||
|
||||
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.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:
|
||||
return false
|
||||
|
||||
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 false
|
||||
|
||||
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)
|
||||
|
||||
return true
|
||||
|
||||
|
||||
func _root_call(type: String, event: Event):
|
||||
get(SIGNAL_PREFIX + type).emit(event)
|
|
@ -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)
|
|
@ -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)
|
||||
|
94
lib/globals/home_api.gd
Normal file
94
lib/globals/home_api.gd
Normal file
|
@ -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)
|
|
@ -1,60 +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 clipboard := DisplayServer.clipboard_get()
|
||||
|
||||
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()
|
||||
|
||||
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)
|
|
@ -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
|
|
@ -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,16 +17,14 @@ var token := ""
|
|||
var LOG_MESSAGES := false
|
||||
|
||||
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()
|
||||
|
||||
signal on_connect()
|
||||
signal on_disconnect()
|
||||
|
||||
func _init(url := self.url, token := self.token):
|
||||
self.url = url
|
||||
self.token = token
|
||||
|
@ -31,6 +33,14 @@ 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)
|
||||
return
|
||||
|
||||
print("Connecting to %s" % self.url)
|
||||
socket.connect_to_url(self.url)
|
||||
set_process(true)
|
||||
|
@ -105,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"):
|
||||
|
@ -192,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,
|
||||
|
@ -209,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():
|
||||
|
@ -228,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
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
<h1>Privacy Policy</h1>
|
||||
<p>Last updated: November 21, 2023</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
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
|
||||
<a
|
||||
href="https://www.privacypolicies.com/privacy-policy-generator/"
|
||||
target="_blank"
|
||||
>Privacy Policy Generator</a
|
||||
>.
|
||||
</p>
|
||||
<h2>Interpretation and Definitions</h2>
|
||||
<h3>Interpretation</h3>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<h3>Definitions</h3>
|
||||
<p>For the purposes of this Privacy Policy:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
<strong>Account</strong> means a unique account created for You to
|
||||
access our Service or parts of our Service.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<strong>Affiliate</strong> 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.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<strong>Application</strong> refers to Immersive Home, the software
|
||||
program provided by the Company.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<strong>Company</strong> (referred to as either "the
|
||||
Company", "We", "Us" or "Our" in
|
||||
this Agreement) refers to Immersive Home.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Country</strong> refers to: Sachsen, Germany</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<strong>Device</strong> means any device that can access the Service
|
||||
such as a computer, a cellphone or a digital tablet.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<strong>Personal Data</strong> is any information that relates to an
|
||||
identified or identifiable individual.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Service</strong> refers to the Application.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<strong>Service Provider</strong> 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.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<strong>Usage Data</strong> 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).
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<strong>You</strong> 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.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>Collecting and Using Your Personal Data</h2>
|
||||
<h3>Types of Data Collected</h3>
|
||||
<p>
|
||||
<strong
|
||||
>We do not collect any data about you under any circumstances.</strong
|
||||
>
|
||||
</p>
|
||||
<h2>Links to Other Websites</h2>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
We have no control over and assume no responsibility for the content,
|
||||
privacy policies or practices of any third party sites or services.
|
||||
</p>
|
||||
<h2>Changes to this Privacy Policy</h2>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<h2>Contact Us</h2>
|
||||
<p>If you have any questions about this Privacy Policy, You can contact us:</p>
|
||||
<ul>
|
||||
<li>
|
||||
By visiting this page on our website:
|
||||
<a
|
||||
href="https://github.com/Nitwel/Immersive-Home"
|
||||
rel="external nofollow noopener"
|
||||
target="_blank"
|
||||
>https://github.com/Nitwel/Immersive-Home</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
|
@ -18,10 +18,11 @@ 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"
|
||||
HomeApi="*res://lib/globals/home_api.gd"
|
||||
AudioPlayer="*res://lib/globals/audio_player.gd"
|
||||
Events="*res://lib/globals/events.gd"
|
||||
EventSystem="*res://lib/globals/event_system.gd"
|
||||
|
||||
[editor_plugins]
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user