Merge pull request #80 from Nitwel/notification-system
Add Notification system
This commit is contained in:
commit
27a5034d34
|
@ -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.
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3782adb5391dac4872890ccd7c7820c6c4eb20d56031598c1f3c4d978438a914
|
||||
size 12684397
|
||||
oid sha256:5a691fa84af96f6cb9243d817266e0a4064cee5384eb97bf2bf45ce799b859ae
|
||||
size 16818156
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:92f36b94bc49caee6ea06bae49983840ce37d27c0e38309038c20163c8b1c7b4
|
||||
size 1035
|
||||
oid sha256:130236cec1c40b3b471db1c7099d86383a83b28d79316dd2ac45bc445e4a9bc5
|
||||
size 1038
|
||||
|
|
|
@ -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"]
|
||||
|
|
54
content/ui/components/notification/notification.gd
Normal file
54
content/ui/components/notification/notification.gd
Normal 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"
|
202
content/ui/components/notification/notification.tscn
Normal file
202
content/ui/components/notification/notification.tscn
Normal 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
|
|
@ -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"]
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -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
|
||||
|
|
12
lib/events/event_notify.gd
Normal file
12
lib/events/event_notify.gd
Normal file
|
@ -0,0 +1,12 @@
|
|||
extends Event
|
||||
class_name EventNotify
|
||||
|
||||
enum Type {
|
||||
INFO,
|
||||
SUCCESS,
|
||||
WARNING,
|
||||
DANGER
|
||||
}
|
||||
|
||||
var message: String
|
||||
var type: Type
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user