Merge pull request #80 from Nitwel/notification-system

Add Notification system
This commit is contained in:
Nitwel 2023-12-13 22:40:32 +01:00 committed by GitHub
commit 27a5034d34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 317 additions and 21 deletions

View File

@ -135,6 +135,7 @@ It is also possible to bubble up information by returning a dictionary from a fu
| `_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 |
| `_on_notify` | `[event: EventNotify]` | The ui notification system |
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.

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3782adb5391dac4872890ccd7c7820c6c4eb20d56031598c1f3c4d978438a914
size 12684397
oid sha256:5a691fa84af96f6cb9243d817266e0a4064cee5384eb97bf2bf45ce799b859ae
size 16818156

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:92f36b94bc49caee6ea06bae49983840ce37d27c0e38309038c20163c8b1c7b4
size 1035
oid sha256:130236cec1c40b3b471db1c7099d86383a83b28d79316dd2ac45bc445e4a9bc5
size 1038

View File

@ -2,7 +2,7 @@
[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/ui/components/grid_container/grid_container.gd" id="3_mx544"]
[ext_resource type="Script" path="res://content/functions/movable.gd" id="4_86fct"]
[ext_resource type="Material" uid="uid://bnwimm214q67g" path="res://assets/materials/sec-500.material" id="5_8c8rc"]
[ext_resource type="Script" path="res://content/functions/occludable.gd" id="6_y4sdl"]

View File

@ -0,0 +1,54 @@
@tool
extends Node3D
@onready var label: Label3D = $AnimationNode/Text
@onready var icon_label: Label3D = $AnimationNode/Icon
@onready var mesh: MeshInstance3D = $AnimationNode/MeshInstance3D
@onready var collision: CollisionShape3D = $AnimationNode/CollisionShape3D
@onready var animation_player: AnimationPlayer = $AnimationNode/AnimationPlayer
@onready var button = $AnimationNode/Button
@onready var timer = $AnimationNode/Timer
@export var type: EventNotify.Type = EventNotify.Type.INFO:
set(value):
type = value
if !is_node_ready(): await ready
print(value, " ", _type_to_string(value))
icon_label.text = _type_to_string(value)
@export var text: String = "":
set(value):
text = value
if !is_node_ready(): await ready
label.text = value
func _ready():
button.on_button_down.connect(fade_out)
fade_in()
timer.timeout.connect(func():
fade_out()
)
func fade_in():
if !is_node_ready(): await ready
animation_player.play("fade_in")
func fade_out():
animation_player.play_backwards("fade_in")
await animation_player.animation_finished
queue_free()
func _type_to_string(type: EventNotify.Type) -> String:
match type:
EventNotify.Type.INFO:
return "info"
EventNotify.Type.SUCCESS:
return "check_circle"
EventNotify.Type.WARNING:
return "error"
EventNotify.Type.DANGER:
return "warning"
_:
return "info"

View File

@ -0,0 +1,202 @@
[gd_scene load_steps=10 format=3 uid="uid://bqj7qwj5mgd30"]
[ext_resource type="Script" path="res://content/ui/components/notification/notification.gd" id="1_yw3yb"]
[ext_resource type="Material" uid="uid://bujy3egn1oqac" path="res://assets/materials/pri-500.material" id="2_5b8oo"]
[ext_resource type="FontVariation" uid="uid://sshfnckriqxn" path="res://assets/icons/icons.tres" id="3_1ljpc"]
[ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="4_ocg5j"]
[sub_resource type="BoxMesh" id="BoxMesh_s37oj"]
size = Vector3(0.25, 0.01, 0.05)
[sub_resource type="BoxShape3D" id="BoxShape3D_m4d21"]
size = Vector3(0.25, 0.01, 0.05)
[sub_resource type="Animation" id="Animation_bkual"]
length = 0.001
tracks/0/type = "bezier"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:scale:x")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"handle_modes": PackedInt32Array(0),
"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0)
}
tracks/1/type = "bezier"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath(".:scale:y")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"handle_modes": PackedInt32Array(0),
"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0)
}
tracks/2/type = "bezier"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath(".:scale:z")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"handle_modes": PackedInt32Array(0),
"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0)
}
tracks/3/type = "bezier"
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/path = NodePath(".:position:x")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
"handle_modes": PackedInt32Array(0),
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0)
}
tracks/4/type = "bezier"
tracks/4/imported = false
tracks/4/enabled = true
tracks/4/path = NodePath(".:position:y")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/keys = {
"handle_modes": PackedInt32Array(0),
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0)
}
tracks/5/type = "bezier"
tracks/5/imported = false
tracks/5/enabled = true
tracks/5/path = NodePath(".:position:z")
tracks/5/interp = 1
tracks/5/loop_wrap = true
tracks/5/keys = {
"handle_modes": PackedInt32Array(0),
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0)
}
[sub_resource type="Animation" id="Animation_r1tka"]
resource_name = "fade_in"
length = 0.3
step = 0.01
tracks/0/type = "bezier"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:scale:x")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"handle_modes": PackedInt32Array(0, 0),
"points": PackedFloat32Array(0.01, -0.25, 0, 0.25, 0, 1, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0, 0.3)
}
tracks/1/type = "bezier"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath(".:scale:y")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"handle_modes": PackedInt32Array(0, 0),
"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0, 1, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0, 0.3)
}
tracks/2/type = "bezier"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath(".:scale:z")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"handle_modes": PackedInt32Array(0, 0),
"points": PackedFloat32Array(1, -0.25, 0, 0.25, 0, 1, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0, 0.3)
}
tracks/3/type = "bezier"
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/path = NodePath(".:position:x")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
"handle_modes": PackedInt32Array(0, 0),
"points": PackedFloat32Array(-0.13, -0.25, 0, 0.25, 0, 0, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0, 0.3)
}
tracks/4/type = "bezier"
tracks/4/imported = false
tracks/4/enabled = true
tracks/4/path = NodePath(".:position:y")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/keys = {
"handle_modes": PackedInt32Array(0, 0),
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0, 0, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0, 0.3)
}
tracks/5/type = "bezier"
tracks/5/imported = false
tracks/5/enabled = true
tracks/5/path = NodePath(".:position:z")
tracks/5/interp = 1
tracks/5/loop_wrap = true
tracks/5/keys = {
"handle_modes": PackedInt32Array(0, 0),
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0, 0, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0, 0.3)
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_kbhuj"]
_data = {
"RESET": SubResource("Animation_bkual"),
"fade_in": SubResource("Animation_r1tka")
}
[node name="Notification" type="Node3D"]
script = ExtResource("1_yw3yb")
[node name="AnimationNode" type="StaticBody3D" parent="."]
[node name="MeshInstance3D" type="MeshInstance3D" parent="AnimationNode"]
material_override = ExtResource("2_5b8oo")
mesh = SubResource("BoxMesh_s37oj")
[node name="CollisionShape3D" type="CollisionShape3D" parent="AnimationNode"]
shape = SubResource("BoxShape3D_m4d21")
[node name="Text" type="Label3D" parent="AnimationNode"]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -0.08, 0.006, 0)
pixel_size = 0.001
text = "Example Text"
font_size = 10
outline_size = 0
horizontal_alignment = 0
autowrap_mode = 3
width = 190.0
[node name="Icon" type="Label3D" parent="AnimationNode"]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -0.1, 0.006, 0)
pixel_size = 0.001
text = "check_circle"
font = ExtResource("3_1ljpc")
font_size = 24
outline_size = 0
[node name="AnimationPlayer" type="AnimationPlayer" parent="AnimationNode"]
libraries = {
"": SubResource("AnimationLibrary_kbhuj")
}
[node name="Button" parent="AnimationNode" instance=ExtResource("4_ocg5j")]
transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0.12, -0.003, -0.02)
label = "close"
icon = true
[node name="Timer" type="Timer" parent="AnimationNode"]
wait_time = 3.0
autostart = true

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=4 format=3 uid="uid://crrb0l3ekuotj"]
[ext_resource type="Script" path="res://content/ui/menu/edit/edit_menu.gd" id="1_34cbn"]
[ext_resource type="Script" path="res://content/ui/menu/grid.gd" id="3_0xvyw"]
[ext_resource type="Script" path="res://content/ui/components/grid_container/grid_container.gd" id="3_0xvyw"]
[ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="4_tvimg"]
[node name="EditMenu" type="Node3D"]

View File

@ -1,6 +1,7 @@
extends Node3D
const Proxy = preload("res://lib/utils/proxy.gd")
const Notification = preload("res://content/ui/components/notification/notification.tscn")
@onready var _controller := XRHelpers.get_xr_controller(self)
@ -17,6 +18,7 @@ const Proxy = preload("res://lib/utils/proxy.gd")
@onready var content = $AnimationContainer/Content
@onready var nav = $AnimationContainer/Navigation
@onready var animation_player = $AnimationPlayer
@onready var notify_place = $AnimationContainer/NotifyPlace
var selected_nav = null
@ -34,6 +36,21 @@ func _ready():
_controller.button_pressed.connect(func(button):
if button == "by_button":
show_menu = !show_menu
)
EventSystem.on_notify.connect(func(event: EventNotify):
var notification_node = Notification.instantiate()
notification_node.text = event.message
notification_node.type = event.type
for child in notify_place.get_children():
child.position += Vector3(0, 0, -0.06)
notify_place.add_child(notification_node)
)
var nav_buttons = [

View File

@ -213,6 +213,9 @@ visible = false
[node name="SettingsMenu" parent="AnimationContainer/Content" instance=ExtResource("11_7wm6b")]
visible = false
[node name="NotifyPlace" type="Marker3D" parent="AnimationContainer"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.2, 0, -0.05)
[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)
visible = false

View File

@ -0,0 +1,12 @@
extends Event
class_name EventNotify
enum Type {
INFO,
SUCCESS,
WARNING,
DANGER
}
var message: String
var type: Type

View File

@ -24,6 +24,8 @@ signal on_touch_enter(event: EventTouch)
signal on_touch_move(event: EventTouch)
signal on_touch_leave(event: EventTouch)
signal on_notify(event: EventNotify)
var _active_node: Node = null
func emit(type: String, event: Event):
@ -32,6 +34,12 @@ func emit(type: String, event: Event):
else:
_root_call(type, event)
func notify(message: String, type := EventNotify.Type.INFO):
var event = EventNotify.new()
event.message = message
event.type = type
emit("notify", event)
func is_focused(node: Node):
return _active_node == node

View File

@ -3,7 +3,7 @@ extends Node
const VariantSerializer = preload("res://lib/utils/variant_serializer.gd")
func clear():
_clear_save_tree(get_tree().root.get_node("Main"))
await _clear_save_tree(get_tree().root.get_node("Main"))
func save():
if HomeApi.has_connected() == false:
@ -22,7 +22,7 @@ func save():
save_file.store_line(json_text)
func load():
clear()
await clear()
if HomeApi.has_connected() == false:
return
@ -45,10 +45,11 @@ func load():
func _clear_save_tree(node: Node):
for child in node.get_children():
_clear_save_tree(child)
await _clear_save_tree(child)
if node.has_method("_save"):
node.queue_free()
await node.tree_exited
func _generate_save_tree(node: Node):
var children = []

View File

@ -20,8 +20,6 @@ var authenticated := false
var id := 1
var entities: Dictionary = {}
var retries := 5
var entitiy_callbacks := CallbackMap.new()
var packet_callbacks := CallbackMap.new()
@ -36,18 +34,13 @@ 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)
# https://github.com/godotengine/godot/issues/84423
# Otherwise the WebSocketPeer will crash when receiving large packets
socket.set_inbound_buffer_size(65535 * 2)
socket.set_inbound_buffer_size(65535 * 4)
func _process(delta):
socket.poll()
@ -66,7 +59,13 @@ func _process(delta):
elif state == WebSocketPeer.STATE_CLOSED:
var code = socket.get_close_code()
var reason = socket.get_close_reason()
print("WS connection closed with code: %s, reason: %s" % [code, reason])
if reason == "":
reason = "Invalid URL"
var message = "WS connection closed with code: %s, reason: %s" % [code, reason]
EventSystem.notify(message, EventNotify.Type.DANGER)
print(message)
handle_disconnect()
func handle_packet(packet: Dictionary):
@ -83,6 +82,8 @@ func handle_packet(packet: Dictionary):
start_subscriptions()
elif packet.type == "auth_invalid":
EventSystem.notify("Failed to authenticate, invalid auth token", EventNotify.Type.DANGER)
print("Failed to authenticate, invalid auth token")
handle_disconnect()
else:
packet_callbacks.call_key(int(packet.id), [packet])
@ -136,9 +137,6 @@ func handle_disconnect():
set_process(false)
on_disconnect.emit()
# Reconnect
connect_ws()
func send_subscribe_packet(packet: Dictionary, callback: Callable):
packet.id = id
id += 1