Routes an event through a widget hierarchy and collects the actions it produced.
handle_event/2 is the entry point. Mouse events go to the mouse router,
which picks a widget by position. Everything else is treated as a key event and is
handled in one of three ways:
:tabandShift+Tabmove focus, unless the focused widget has both:focusedand:trap_focusset in its state, in which case it receives the key.- Arrow keys are offered to directional focus navigation first.
- Anything else goes to the focused widget.
{:key, _},{:key, _, _},{:char, _}and{:bracketed_paste, _}are dispatched; every other shape is dropped. Paste text is passed throughDrafter.Clipboard.sanitize/1.
Dispatch phases
A dispatched event travels the ancestor path root-first in the :capture phase,
where an ancestor exporting handle_event_capture/2 may rewrite it, consume it or
prevent its default. It is then delivered to the target in the :target phase. If
the target does not consume it, it walks back up in the :bubble phase, one
ancestor at a time, until an ancestor consumes it or the root is reached.
The event is an Drafter.Event.Object while it travels, so phase, target and
propagation flags are available to capture handlers; it is converted back to a
tuple before each widget's handle_event/2 is called.
Actions returned by widgets are gathered in the order they were produced and returned alongside the updated hierarchy.
A widget backed by a process is asked through the widget's process; one without a
process has its module's handle_event/2 called directly, and the result is
normalised by Drafter.EventResult.parse/2.
Summary
Functions
Offer event to every widget in the hierarchy, without phases.
Walk an unhandled event up the ancestor chain.
The chain of widget ids from the root down to widget_id, inclusive.
Normalise what a handle_event_capture/2 returned.
Walk event down path, offering it to each widget's capture handler.
Send semantic_event through the phase pipeline with the focused widget as target.
Dispatch event to the focused widget, or drop it.
Offer event to a widget whose registration record is already in hand.
Route event through hierarchy and return {hierarchy, actions}.
Route an event and report whether anything in the hierarchy acted on it.
Run tuple_event through the capture and target phases with target_id as target.
Route a non-mouse event: focus movement first, then the focused widget.
Offer event to the widget registered as widget_id.
Run one step of the capture walk over the widget widget_id.
Offer event to a single widget by id, without phases.
Refresh the cached copy of the focused widget's state from its process.
Ask one widget to handle event, in whichever way that widget is backed.
Offer event to one widget's handle_event_capture/2.
Functions
Offer event to every widget in the hierarchy, without phases.
Widgets are visited in map order, which is not the tree order, and each one may
bubble the event to its parent. Returns {hierarchy, actions} with the actions
concatenated in visit order.
@spec bubble_to_parent(Drafter.WidgetHierarchy.t(), term(), term(), [term()]) :: {Drafter.WidgetHierarchy.t(), [term()]}
Walk an unhandled event up the ancestor chain.
Each ancestor is dispatched with :phase set to :bubble and
:current_target set to itself. The walk ends at the root, or at the first
ancestor that stops propagation. Returns the updated hierarchy and the actions
gathered along the way, in the order they were produced.
The chain of widget ids from the root down to widget_id, inclusive.
This is the order the capture phase visits. Returns [] for an unknown id.
acc is the accumulator of the walk and callers pass nothing for it.
Normalise what a handle_event_capture/2 returned.
Accepted returns and their results:
{:continue, event, state}→ passed through; the event carries on down{:stop, event, state, actions}→{:stop, event_with_propagation_stopped, state, actions}{:prevent, event, state}→{:stop, event_with_default_prevented_and_ propagation_stopped, state, []}
Anything else yields {:continue, event, fallback_state}, discarding whatever the
handler returned. event and fallback_state are the values to fall back to.
Walk event down path, offering it to each widget's capture handler.
path runs root-first, as build_ancestor_path/3 returns it. The walk ends early
when a handler stops propagation or has already stopped immediate propagation.
Returns {hierarchy, event, actions} with the event as the handlers left it.
Send semantic_event through the phase pipeline with the focused widget as target.
Returns {hierarchy, []} when no widget is focused.
Dispatch event to the focused widget, or drop it.
{:key, _}, {:key, _, _}, {:char, _} and {:bracketed_paste, _} are
dispatched; paste text is sanitized first. Every other shape returns the
hierarchy unchanged with no actions.
Offer event to a widget whose registration record is already in hand.
A :stop result stores the widget's new state and marks the hierarchy's event as
consumed. A :bubble result stores the state and continues up the ancestor chain.
:not_handled leaves the state alone and continues up. Returns
{hierarchy, actions}.
@spec handle_event(Drafter.WidgetHierarchy.t(), term()) :: {Drafter.WidgetHierarchy.t(), [term()]}
Route event through hierarchy and return {hierarchy, actions}.
Mouse events are resolved by position; everything else is treated as a key event.
Actions are in the order the widgets produced them. Use
handle_event_consumed/2 when the caller needs to know whether anything acted.
@spec handle_event_consumed(Drafter.WidgetHierarchy.t(), term()) :: {Drafter.WidgetHierarchy.t(), [term()], boolean()}
Route an event and report whether anything in the hierarchy acted on it.
Returns {hierarchy, actions, consumed?}. The event counts as consumed if it
produced actions, moved focus, or was marked consumed by a widget that stopped
propagation.
Run tuple_event through the capture and target phases with target_id as target.
The event is converted to a Drafter.Event.Object, timestamped, and offered to
each ancestor from the root down. If a capture handler stopped propagation the
target is never called and only the capture actions are returned. Otherwise the
target handles it, which may start the bubble phase. Returns
{hierarchy, actions} with capture actions before target actions.
Route a non-mouse event: focus movement first, then the focused widget.
Tab and Shift+Tab, in both their :tab and ?\t spellings, cycle focus
forwards and backwards unless the focused widget traps focus. Arrow keys are
offered to directional navigation, which falls back to the focused widget when no
neighbour is found. Returns {hierarchy, actions}.
Offer event to the widget registered as widget_id.
Returns {hierarchy, []} for an unknown id. Otherwise see
dispatch_widget_event/4.
Run one step of the capture walk over the widget widget_id.
Sets the event's :current_target and :phase before calling the widget's
capture handler. Returns a Enum.reduce_while/3 instruction: {:cont, acc} to
keep walking down, {:halt, acc} when the widget stopped the event, where acc
is {hierarchy, event, actions}. An unknown id is skipped.
Offer event to a single widget by id, without phases.
Refresh the cached copy of the focused widget's state from its process.
Returns the hierarchy unchanged when nothing is focused, when the focused widget has no process, or when that process does not answer.
Ask one widget to handle event, in whichever way that widget is backed.
A widget with a process is asked through the widget's process, which owns its
state; the cached copy in the hierarchy is returned unchanged. A widget without
one has its module's handle_event/2 called and the return normalised by
Drafter.EventResult.parse/2.
Returns {new_state, actions, :stop | :bubble}, or :not_handled when the widget
did not act or exports no handle_event/2.
Offer event to one widget's handle_event_capture/2.
event is a Drafter.Event.Object. A widget that does not export the callback
returns {:continue, event, state} unchanged. See classify_capture_result/3 for
the shapes a capture handler may return.