Events
The Nova event system provides a variety of UI-related events, so custom components can react to data-driven content changes and/or user input.
Connected UIBlock Hierarchies
Rather than exposing the provided events as direct properties across a range of components, Nova's event system is unique in that it treats each active UIBlock in a connected UIBlock hierarchy as a node in a hierarchical event graph.
A connected UIBlock hierarchy is an enabled hierarchy of UIBlocks such that each parent Transform of a UIBlock (i.e. UIBlock.transform.parent
) also has a UIBlock on its GameObject – in the context of Nova, while UIBlock.Parent != null
.
If a given UIBlock's parent Transform doesn't have a UIBlock attached to its GameObject, that top-level UIBlock is considered a root. A UIBlock's root can always be queried via the UIBlock.Root property.
Note
All UIBlocks on enabled GameObjects within a connected UIBlock hierarchy participate in the event propagation step. Event propagation stops at root UIBlocks. Even if there is a UIBlock higher in the Transform hierarchy, if it's disconnected from the UIBlock firing the event, it will not receive that particular event.
Event Propagation
When a Nova component is ready to trigger an event, it sends the relevant event data via the UIBlock on its GameObject, allowing the event to propogate upwards through the UIBlock's ancestral hierarchy. Once the event data reaches an ancestor listening for the propagating event type, the event callbacks registered on that UIBlock are invoked, and the propagation continues up.
The EventHandler
referenced in the root of the diagram would be a user-written component. A simple example:
using Nova;
using UnityEngine;
public class EventHandler : MonoBehaviour
{
// Serialize and assign in Unity Editor
public UIBlock Root = null;
private void Start()
{
Root.AddGestureHandler<Gesture.OnClick>(Click);
}
private void Click(Gesture.OnClick evt)
{
Debug.Log("Clicked!");
}
}
In the case where an event handler wishes to prevent the event it's handling from propagating further up the UIBlock hierarchy, the Consume()
method is available.
private void Click(Gesture.OnClick evt)
{
Debug.Log("Clicked!");
// Stop hierarchical event propogation
evt.Consume();
}
Note
A call to Consume()
may not immediately stop triggering event callbacks, if there are other event handlers registered at the current hierarchical propagation level. It simply prevents further propagation up the hierarchy.
See Input to learn more about various gestures.
Event Target Types
The hierarchical event propagation provides a number of benefits, but perhaps most significant is enabling Event + Event Target type-matching. In other words, being able to handle specific events, such as clicks, that occur on specific Event Target types, including user-defined target types. Let's look at a concrete example:
Say you've created a ButtonVisuals
class to group the visuals associated with a Button control (see the ItemView article to learn more about creating ItemVisuals):
public class ButtonVisuals : ItemVisuals
{
public TextBlock Label;
public UIBlock2D Icon;
}
Using Nova's Event + Event Target type-matching, you can now create an EventHandler
to handle Gesture.OnClick
events which occur to ButtonVisuals
, like so:
using Nova;
using UnityEngine;
public class EventHandler : MonoBehaviour
{
// Serialize and assign in Unity Editor
public UIBlock Root = null;
private void Start()
{
Root.AddGestureHandler<Gesture.OnClick, ButtonVisuals>(ButtonClicked);
}
private void ButtonClicked(Gesture.OnClick evt, ButtonVisuals button)
{
Debug.Log("Button Clicked!");
}
}
A Gesture.OnClick
event that is fired on or below the ItemView containing our ButtonVisuals
type will traverse up as a Gesture.OnClick
+ ButtonVisuals
pair. Anything on or above the ItemView in the UIBlock hierarchy could then receive the Gesture.OnClick event on the ButtonVisuals
type.
This type-matching gives you the flexibility to filter or handle events based on the type of object the event occurred to. For example, if in addition to ButtonVisuals
we also had a ToggleVisuals
class:
public class ToggleVisuals : ItemVisuals
{
public TextBlock Label;
public UIBlock2D IsOnIndicator;
}
We could modify our EventHandler
to handle clicks differently for buttons and toggles:
// Register both Button and Toggle
Root.AddGestureHandler<Gesture.OnClick, ButtonVisuals>(ButtonClicked);
Root.AddGestureHandler<Gesture.OnClick, ToggleVisuals>(ToggleClicked);
// ...
// Handle button clicks
private void ButtonClicked(Gesture.OnClick evt, ButtonVisuals button)
{
Debug.Log("Button Clicked!");
}
// Handle toggle clicks
private void ToggleClicked(Gesture.OnClick evt, ToggleVisuals toggle)
{
Debug.Log("Toggle Clicked!");
}
Note
The Event Target type-matching handles inheritance, so a GestureHandler
registered for Animal
will also be invoked for Dog
, assuming Dog
inherits from Animal
.
Multiple Event Target Types
It is worth noting that as an event traverses up the UIBlock hierarchy, it can have multiple Event Target types associated with it. In other words, the process of "picking up" Event Target types is additive.
For example, in the Multiple Type Targeted Events
diagram, there are two ItemViews between the Interactable on which a Gesture.OnClick
was created, and the EventHandler
. One ItemView has a ButtonVisuals
type, and another has a FooVisuals
type. If the EventHandler
has registered for Gesture.OnClick
events for both ButtonVisuals
and FooVisuals
, both callbacks would be invoked for a single Gesture.OnClick
event.
Event Types
Event | Description | Triggered By |
---|---|---|
Data.OnBind<T> | A data-model, T , to ItemVisuals bind event. |
ListView |
Data.OnUnbind<T> | A data-model, T , to ItemVisuals unbind event. |
ListView |
Gesture.OnHover | A pointer enter input event. | Interactable & Scroller |
Gesture.OnUnhover | A pointer exit input event. | Interactable & Scroller |
Gesture.OnMove | A pointer move input event. | Interactable & Scroller |
Gesture.OnPress | A pointer down input event. | Interactable & Scroller |
Gesture.OnRelease | A pointer up input event. | Interactable & Scroller |
Gesture.OnClick | A pointer click pattern was detected. | Interactable & Scroller |
Gesture.OnDrag | A pointer move while down input event. | Interactable |
Gesture.OnScroll | Corresponds to a pointer move while down input event or an explicit call to Scroll. | Scroller |
Gesture.OnCancel | Corresponds to a higher-priority gesture being detected, the GestureRecognizer component capturing the active gesture being disabled, or an explicit call to Cancel. | Interactable & Scroller |
Navigate.OnDirection | Navigated in a given direction without moving navigation focus. | Interactable & Scroller |
Navigate.OnMoveTo | Navigation focus moved to a new GestureRecognizer. | Interactable & Scroller |
Navigate.OnMoveFrom | Navigation focus moved from a GestureRecognizer. | Interactable & Scroller |
Navigate.OnSelect | The GestureRecognizer with navigation focus was selected. | Interactable & Scroller |
Navigate.OnDeselect | The selected GestureRecognizer was deselected. | Interactable & Scroller |