From 93c82f17909ba1f54eeafc4531dd6f5359f0ecc8 Mon Sep 17 00:00:00 2001 From: Nitwel Date: Wed, 22 Nov 2023 17:35:54 +0100 Subject: [PATCH] add focus event --- README.md | 43 +++++++++++------------- content/ui/components/button/button.tscn | 2 +- lib/events/event_focus.gd | 5 +++ lib/globals/event_system.gd | 29 ++++++++++++++-- 4 files changed, 53 insertions(+), 26 deletions(-) create mode 100644 lib/events/event_focus.gd diff --git a/README.md b/README.md index c4be6d2..61b6c7b 100644 --- a/README.md +++ b/README.md @@ -95,36 +95,33 @@ In case that an event of a specific node has to be reacted on, use the `Clickabl It is also possible to bubble up information by returning a dictionary from a function like `_on_click`. -```python -InteractionEvent { - "controller": XRController3D, # The controller that triggered the event - "ray": RayCast3D, # The ray-cast that triggered the event - "target": Node3D, # The node that was hit by the ray-cast -} - -KeyEvent { - "key": String, # The key that was pressed -} -``` - | Function called | Args | Description | | -- | -- | -- | -| `_on_click` | `[event: InteractionEvent]` | The back trigger button has been pressed and released | -| `_on_press_down` | `[event: InteractionEvent]` | The back trigger button has been pressed down | -| `_on_press_move` | `[event: InteractionEvent]` | The back trigger button has been moved while pressed down | -| `_on_press_up` | `[event: InteractionEvent]` | The back trigger button has been released | -| `_on_grab_down` | `[event: InteractionEvent]` | The side grab button been pressed down | -| `_on_grab_move` | `[event: InteractionEvent]` | The side grab button been pressed down | -| `_on_grab_up` | `[event: InteractionEvent]` | The side grab button been released | -| `_on_ray_enter` | `[event: InteractionEvent]` | The ray-cast enters the the collision body | -| `_on_ray_leave` | `[event: InteractionEvent]` | The ray-cast leaves the the collision body | -| `_on_key_down` | `[event: KeyEvent]` | The ray-cast leaves the the collision body | -| `_on_key_up` | `[event: KeyEvent]` | The ray-cast leaves the the collision body | +| `_on_click` | `[event: EventRay]` | The back trigger button has been pressed and released | +| `_on_press_down` | `[event: EventRay]` | The back trigger button has been pressed down | +| `_on_press_move` | `[event: EventRay]` | The back trigger button has been moved while pressed down | +| `_on_press_up` | `[event: EventRay]` | The back trigger button has been released | +| `_on_grab_down` | `[event: EventRay]` | The side grab button been pressed down | +| `_on_grab_move` | `[event: EventRay]` | The side grab button been pressed down | +| `_on_grab_up` | `[event: EventRay]` | The side grab button been released | +| `_on_ray_enter` | `[event: EventRay]` | The ray-cast enters the the collision body | +| `_on_ray_leave` | `[event: EventRay]` | The ray-cast leaves the the collision body | +| `_on_key_down` | `[event: EventKey]` | The ray-cast leaves the the collision body | +| `_on_key_up` | `[event: EventKey]` | The ray-cast leaves the the collision body | +| `_on_focus_in` | `[event: EventFocus]` | The node is got focused | +| `_on_focus_out` | `[event: EventFocus]` | The node lost focus | After considering using the build in godot event system, I've decided that it would be better to use a custom event system. The reason being that we would have to check each tick if the event matches the desired one which seems very inefficient compared to using signals like the browser does. Thus I've decided to use a custom event system that is similar to the one used in the browser. +### UI Groups + +| Group | Description | +| -- | -- | +| `ui_focus` | The element can be focused | + + ### Functions In order to implement generic features, a set of functions is available to be used in the project. diff --git a/content/ui/components/button/button.tscn b/content/ui/components/button/button.tscn index 11dae36..6b1eff7 100644 --- a/content/ui/components/button/button.tscn +++ b/content/ui/components/button/button.tscn @@ -81,7 +81,7 @@ _data = { "down": SubResource("Animation_iu2ed") } -[node name="Button" type="StaticBody3D"] +[node name="Button" type="StaticBody3D" groups=["ui_focus"]] script = ExtResource("1_74x7g") [node name="MeshInstance3D" type="MeshInstance3D" parent="."] diff --git a/lib/events/event_focus.gd b/lib/events/event_focus.gd new file mode 100644 index 0000000..24b0449 --- /dev/null +++ b/lib/events/event_focus.gd @@ -0,0 +1,5 @@ +extends Event +class_name EventFocus + +var target: Node +var previous_target: Node \ No newline at end of file diff --git a/lib/globals/event_system.gd b/lib/globals/event_system.gd index 8e3d9e6..0c3c606 100644 --- a/lib/globals/event_system.gd +++ b/lib/globals/event_system.gd @@ -17,16 +17,39 @@ signal on_ray_leave(event: EventRay) signal on_key_down(event: EventKey) signal on_key_up(event: EventKey) +signal on_focus_in(event: EventFocus) +signal on_focus_out(event: EventFocus) + +var active_node: Node = null + func emit(type: String, event: Event): if event is EventBubble: _bubble_call(type, event.target, event) + if event.target.is_in_group("ui_focus"): + _handle_focus(event.target) + else: + _handle_focus(null) else: _root_call(type, event) +func _handle_focus(node: Node): + var event = EventFocus.new() + event.previous_target = active_node + event.target = node + + if active_node != null && active_node.has_method(FN_PREFIX + "focus_in"): + active_node.call(FN_PREFIX + "focus_out", event) + on_focus_out.emit(event) + + active_node = node + + if active_node != null: + active_node.call(FN_PREFIX + "focus_in", event) + on_focus_in.emit(event) func _bubble_call(type: String, target: Variant, event: EventBubble): if target == null: - return + return false if target.has_method(FN_PREFIX + type): var updated_event = target.call(FN_PREFIX + type, event) @@ -36,7 +59,7 @@ func _bubble_call(type: String, target: Variant, event: EventBubble): event = updated_event if event.bubbling == false: - return + return false for child in target.get_children(): if child is Function && child.has_method(FN_PREFIX + type): @@ -50,6 +73,8 @@ func _bubble_call(type: String, target: Variant, event: EventBubble): # in case the top has been reached _root_call(type, event) + return true + func _root_call(type: String, event: Event): get(SIGNAL_PREFIX + type).emit(event)