add focus event

This commit is contained in:
Nitwel 2023-11-22 17:35:54 +01:00
parent ca294d3d88
commit 93c82f1790
4 changed files with 53 additions and 26 deletions

View File

@ -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`. 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 | | Function called | Args | Description |
| -- | -- | -- | | -- | -- | -- |
| `_on_click` | `[event: InteractionEvent]` | The back trigger button has been pressed and released | | `_on_click` | `[event: EventRay]` | The back trigger button has been pressed and released |
| `_on_press_down` | `[event: InteractionEvent]` | The back trigger button has been pressed down | | `_on_press_down` | `[event: EventRay]` | 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_move` | `[event: EventRay]` | The back trigger button has been moved while pressed down |
| `_on_press_up` | `[event: InteractionEvent]` | The back trigger button has been released | | `_on_press_up` | `[event: EventRay]` | The back trigger button has been released |
| `_on_grab_down` | `[event: InteractionEvent]` | The side grab button been pressed down | | `_on_grab_down` | `[event: EventRay]` | The side grab button been pressed down |
| `_on_grab_move` | `[event: InteractionEvent]` | The side grab button been pressed down | | `_on_grab_move` | `[event: EventRay]` | The side grab button been pressed down |
| `_on_grab_up` | `[event: InteractionEvent]` | The side grab button been released | | `_on_grab_up` | `[event: EventRay]` | The side grab button been released |
| `_on_ray_enter` | `[event: InteractionEvent]` | The ray-cast enters the the collision body | | `_on_ray_enter` | `[event: EventRay]` | The ray-cast enters the the collision body |
| `_on_ray_leave` | `[event: InteractionEvent]` | The ray-cast leaves the the collision body | | `_on_ray_leave` | `[event: EventRay]` | The ray-cast leaves the the collision body |
| `_on_key_down` | `[event: KeyEvent]` | 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: KeyEvent]` | 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. 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. 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. 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 ### Functions
In order to implement generic features, a set of functions is available to be used in the project. In order to implement generic features, a set of functions is available to be used in the project.

View File

@ -81,7 +81,7 @@ _data = {
"down": SubResource("Animation_iu2ed") "down": SubResource("Animation_iu2ed")
} }
[node name="Button" type="StaticBody3D"] [node name="Button" type="StaticBody3D" groups=["ui_focus"]]
script = ExtResource("1_74x7g") script = ExtResource("1_74x7g")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."] [node name="MeshInstance3D" type="MeshInstance3D" parent="."]

View File

@ -0,0 +1,5 @@
extends Event
class_name EventFocus
var target: Node
var previous_target: Node

View File

@ -17,16 +17,39 @@ signal on_ray_leave(event: EventRay)
signal on_key_down(event: EventKey) signal on_key_down(event: EventKey)
signal on_key_up(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): func emit(type: String, event: Event):
if event is EventBubble: if event is EventBubble:
_bubble_call(type, event.target, event) _bubble_call(type, event.target, event)
if event.target.is_in_group("ui_focus"):
_handle_focus(event.target)
else:
_handle_focus(null)
else: else:
_root_call(type, event) _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): func _bubble_call(type: String, target: Variant, event: EventBubble):
if target == null: if target == null:
return return false
if target.has_method(FN_PREFIX + type): if target.has_method(FN_PREFIX + type):
var updated_event = target.call(FN_PREFIX + type, event) 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 event = updated_event
if event.bubbling == false: if event.bubbling == false:
return return false
for child in target.get_children(): for child in target.get_children():
if child is Function && child.has_method(FN_PREFIX + type): 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 # in case the top has been reached
_root_call(type, event) _root_call(type, event)
return true
func _root_call(type: String, event: Event): func _root_call(type: String, event: Event):
get(SIGNAL_PREFIX + type).emit(event) get(SIGNAL_PREFIX + type).emit(event)