pub struct EventSpec {Show 19 fields
pub on_click: Option<Value>,
pub on_drag: Option<Value>,
pub on_key: Option<Value>,
pub key_up: bool,
pub modifier_keys: bool,
pub on_context_menu: Option<Value>,
pub on_force_click: Option<Value>,
pub on_button: Option<Value>,
pub buttons: Buttons,
pub on_scroll: Option<Value>,
pub scroll_axes: ScrollAxes,
pub scroll_mods: KeyMods,
pub on_hover: Option<Value>,
pub on_drop: Option<Value>,
pub on_layout: Option<Value>,
pub on_change: Option<Value>,
pub modal: Option<Value>,
pub keep_focus: bool,
pub on_focus: Option<Value>,
}Expand description
Event payloads a node declares. Boxed on NodeSpec because most
nodes declare none.
Fields§
§on_click: Option<Value>Payload emitted as a UiEvent when this node is clicked.
on_drag: Option<Value>Makes this node draggable: pressing it starts a pointer-captured
drag, and cursor motion until release emits UiEvents of the form
{kind="drag", phase="start"|"move"|"end", x, y, dx, dy, tag} with
this payload merged in under tag. A drag past the click slop
suppresses the node’s on_click, so both can coexist.
on_key: Option<Value>Marks this node as a key sink: while it holds key focus, key
presses arrive as UiEvents on it as {kind="key", phase="down", code, ...}, with this payload merged in under tag. Clicking the
node takes key focus. Releases are not delivered unless the sink
also declares key_up: a keymap is the common
case, and a keymap that heard both halves would run every binding
twice.
key_up: boolWith on_key: the sink hears releases too, as the same payload
with phase="up" (text null, repeat false). For a held-key
interaction — WASD, press-and-hold to preview, a key that arms a
mode while it is down. A key only comes up where it went down: a
release whose press the sink never got is dropped, and focus
leaving while a key is held delivers the up first, so nothing is
left stuck down. Without it a sink hears presses only, which is
what a keymap wants.
modifier_keys: boolWith on_key: the modifier and lock keys themselves arrive too
(Shift, Ctrl, Alt, Super, Caps Lock, Num Lock, Scroll Lock), as
codes of their own with their side in location.
Without it a modifier is only ever held — in the next key’s
shift / ctrl / … and the modifiers event — so a keymap
mid-sequence does not read Shift as a key between two others. A
terminal speaking kitty’s keyboard protocol asks, and so does a
game that binds a lone Shift.
Asks for a context menu: a secondary-button press over this node
emits {kind="contextmenu", x, y, tag} on it with this payload
under tag, and does nothing else — the press moves no focus,
places no caret and produces no click, so right-clicking a
selection leaves it selected. x/y are the press in logical
viewport coordinates, which is where the menu goes. Asked of the
topmost node under the pointer; when that node offers no menu the
press reaches the nearest enclosing node that does, the way an
unclaimed key reaches the enclosing sink:
the event carries the owner’s key and tag, a nested declaration
wins over its ancestor’s, a disabled node’s own is skipped, and
the walk stops at the modal boundary. Null = the behaviour without
a tag.
on_force_click: Option<Value>Force-click events: a press that deepened past the second stage of
a Force Touch trackpad over this node emits {kind="forceclick", x, y, tag} with this payload under tag. Routed as a secondary
press is — no focus moved, no caret, no click — but asked of the
topmost node only, with no walk to an enclosing declaration — and
the ordinary click the press produces still follows,
which is what macOS does.
Text does not need this: a force click over an editor or a
selectable scope selects the word and asks the host to look it
up, which is what the gesture means on that platform. This is for
what the core cannot guess — a force click on a chart, a map, a
timeline.
The non-primary buttons as events: a press of a
button in buttons over this node emits
{kind="button", phase="press", button, x, y, clicks, tag} on it
with this payload under tag, and the button is then captured by
the node — every pointer move while it is held arrives as
phase="move" and its release as phase="release", on this node
wherever the pointer is. button is "secondary", "middle" or,
for a button past those, its crate::MouseButton::code; x/y
are logical viewport coordinates, and on a cells grid the events
carry cell: {row, col} as a click does. Several buttons may be
held at once, each its own capture; a primary drag is untouched.
Asked of the topmost node under the pointer, and when that node
claims no such button the press reaches the nearest enclosing node
that does, as a context menu’s does: a disabled node’s own is
skipped and the walk stops at the modal boundary. A claimed
secondary press is this event instead of a contextmenu event
and the stock menu — a nearer on_context_menu still wins, being
the nested declaration. Like every non-primary press it moves no
focus, places no caret and touches no selection or scrollbar. What
it is for: a terminal’s middle-click paste, and the mouse reports a
program in it asked for. Null = the behaviour without a tag.
Which non-primary buttons on_button claims:
all three kinds unless the node says otherwise. A pane that wants
the middle button and leaves the secondary one to its context menu
says Buttons::MIDDLE.
on_scroll: Option<Value>Scroll events: the wheel over this node emits {kind="scroll", x, y, dx, dy, lines, tag} with this payload under tag — the delta
in logical px as the driver reported it (positive dy is the wheel
rolling up, toward earlier content), the pointer’s position, and
on a cells grid the whole lines the delta covers (null on any
other node), the fraction carried to the next notch. The node
takes the wheel on the axes scroll_axes
names: a scroll gesture that starts over it is its own, and stays
its own until it ends wherever the pointer goes, except on an axis
it also scrolls as a container (scroll_x, scroll_y, the offset
the app sets from what it hears), where it is answered by its room
as a container is: at its edge, a gesture that way passes to the
scroller around it; it
reaches no scroll container above it, and a container inside it
still takes the axes it scrolls while it can move that way,
passing this node the rest: the other axis, and a gesture that
begins with the container at its limit (unless it says
Overscroll::Contain). The core moves nothing — a grid’s
origin_line and a canvas’s zoom are the app’s to change. A
drag-select held past a grid’s top or bottom edge arrives here
too, as the lines the frame scrolled by.
scroll_axes: ScrollAxesWhich axes on_scroll takes: both unless the
node says otherwise. A scroll gesture on an axis
the node does not take passes it by, to the scroller around it —
a terminal that scrolls its history on y says
ScrollAxes::Y, and a sideways swipe that meets it goes on
moving the strip it sits in. Meaningless without on_scroll.
scroll_mods: KeyModsThe modifiers on_scroll is for (backlog
F122): with any named, the node hears only a scroll gesture that
began with one of them held — and hears it first, wherever in
the tree under the pointer the gesture began: ahead of every
scroll container and every handler that names none, the innermost
such node winning. A wheel with none of them held passes it by as
if it declared no on_scroll — a node that is a scroll container
too scrolls for it as any other does. What a Ctrl-wheel zoom is declared
with, on the window’s root or on the one canvas it zooms: a
modified wheel is the more specific ask, and no list under the
pointer scrolls for it. Its events carry mods, the modifiers
held when the gesture began — the gesture stays this node’s to
its end, glide included, whatever is let go meanwhile. None named
(the default): a handler like any other. Meaningless without
on_scroll.
on_hover: Option<Value>Hover events: the pointer entering or leaving this node emits
{kind="hover", phase="enter"|"leave", tag} with this payload under
tag — for hover-dependent layout (a close button that appears)
where a color swap isn’t enough. Implies hover tracking.
on_drop: Option<Value>Drop-zone events: files dragged in from the OS over this node emit
{kind="drop", phase="enter"|"move"|"leave"|"drop", paths, x, y, tag} with this payload under tag — paths the OS paths as
strings, x/y the pointer in viewport coordinates (absent on
leave). The zone under the files is the topmost zone by paint
order: a node inside a zone resolves to it, and a node that is no
zone and has none enclosing it is looked past, so an overlay shown
on enter cannot make the zone lose the files. No leave follows
a drop. Implies hover tracking.
on_layout: Option<Value>Layout events: the rect layout gave this node arrives as
{kind="layout", x, y, w, h, parent: {x, y, w, h}, tag} (logical px,
viewport coordinates, after scrolling and position easing) — on the
node’s first frame and again whenever the rect changes, never on a
frame that left it alone. The view reads the numbers layout already
produced instead of re-deriving them; a transition that moves the
node reports every frame it moves. Needs a stable key across frames.
on_change: Option<Value>Slider changes: on a node whose role is Slider, the core turns a
press into the value under the pointer, a drag into the value under
it, the arrows and assistive technology’s Increment / Decrement
into one value_step, PageUp / PageDown into ten, Home / End into
the range’s ends — clamped to value_min..value_max, snapped to the
step — and emits {kind="change", value, phase="move"|"end", tag}
with this payload under tag. The value is proposed, never
applied: nothing moves until the view declares it as value_now.
Without it a slider’s keys reach the app as the access nudge.
Ignored on any other role.
modal: Option<Value>Modal: while this node is declared, the Tab ring is its subtree,
everything outside it is inert to the pointer, the wheel and
assistive technology, and Escape or a press outside emits
{kind="dismiss", reason, tag} on it with this payload under
tag. The last node declaring it in tree order is the one in
effect (a confirm inside a dialog). Null = modal without a tag.
keep_focus: boolA press on this node, or anywhere inside it, leaves keyboard focus
where it was (keepFocus): a toolbar button, a tab, a divider
that acts without taking the keyboard from the editor beside it.
Its click, drag and hover are unchanged, and Tab
and assistive technology still reach a focusable node in it — it
is the pointer’s press alone that stops moving focus.
on_focus: Option<Value>Keyboard focus entering or leaving this node’s subtree — the node
itself or anything focused inside it — emits {kind="focus", phase="in"|"out", by, tag} with this payload under tag, where
by is "pointer", "keyboard", "assistive" or "program":
what moved it. Reported once the move has settled —
after the input that made it, or at the end of the frame that
declared it — so a view reads a change instead of diffing
key_focus every frame. Declares nothing interactive.
Implementations§
Trait Implementations§
impl StructuralPartialEq for EventSpec
Auto Trait Implementations§
impl Freeze for EventSpec
impl RefUnwindSafe for EventSpec
impl Send for EventSpec
impl Sync for EventSpec
impl Unpin for EventSpec
impl UnsafeUnpin for EventSpec
impl UnwindSafe for EventSpec
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.