Modifiers

Listeners

DOM-named event listeners on every element, each with its event payload.

FNview::Listeners
Usage
use crownui::prelude::*;
 
let count = cx.signal(0);
 
button("Add")
    .on_click(move |cx, event| {
        let step = if event.modifiers.shift_key { 10 } else { 1 };
        count.update(cx, |n| *n += step);
    })
    .on_key_down(|_, event| {
        if event.repeat {
            event.prevent_default();
        }
    })
Signature
pub trait Listeners: Styled {
    fn on_click(self, handler: impl FnMut(&mut Cx, &mut Event<ClickEvent>) + 'static) -> Self;
    // one method per listener, listed below
    fn shortcut(self, keybind: Keybind) -> Self;
}
 
impl<T: Styled> Listeners for T {}

The Listeners trait adds DOM-named event listeners to every element. It is implemented for every Styled type and is in the prelude.

Every listener method takes a handler and returns the element, so calls chain. A handler is FnMut(&mut Cx, &mut Event<T>) + 'static. It gets the context, which derefs to the reactive runtime, and the event. An element can have several listeners of the same kind. They run in the order they were added.

Parameters

handlerimpl FnMut(&mut Cx, &mut Event<T>) + 'staticrequired

Runs when the event reaches this element. T depends on the listener.

keybindKeybindrequired

For shortcut only. The key combination that clicks this element.

Returns

SelfSelf

The same element with the listener added.

Every listener

MethodDOM eventEvent payloadFires when
on_clickclickClickEventPrimary button click or tap. Also Enter or Space on the focused element, and .shortcut.
on_double_clickdblclickClickEventThe second click of a double click, before its click.
on_context_menucontextmenuClickEventSecondary button click.
on_long_pressnoneLongPressEventA press held in place for 500 ms.
on_pointer_downpointerdownPointerEventA pointer button goes down over the element.
on_pointer_uppointerupPointerEventA pointer button goes up over the element.
on_pointer_movepointermovePointerEventThe pointer moves over the element.
on_pointer_enterpointerenterPointerEventThe pointer enters the element. Does not bubble.
on_pointer_leavepointerleavePointerEventThe pointer leaves the element. Does not bubble.
on_pointer_cancelpointercancelPointerEventThe platform takes the pointer away mid-press.
on_wheelwheelWheelEventA wheel or touchpad scroll over the element.
on_scrollscrollScrollEventA scroll container's offset changed.
on_key_downkeydownKeyboardEventA key goes down while the element or a descendant has focus.
on_key_upkeyupKeyboardEventA key goes up while the element or a descendant has focus.
on_inputinputInputEventEvery edit of a text field.
on_changechangeInputEventA text field's value is committed with Enter or on blur.
on_submitsubmitInputEventEnter in a text field.
on_text_inputcompositionupdate, beforeinputTextInputEventInput method composition, committed text or a paste for the focused element.
on_focusfocusFocusEventThe element gains focus.
on_blurblurFocusEventThe element loses focus.
on_drag_startdragstartDragEventA pointer drag crosses the 6 px slop.
on_dragdragDragEventEach move during a pointer drag.
on_drag_enddragendDragEventThe pointer drag ends, with the release velocity.
on_pinchnonePinchEventEach step of a touchpad pinch.
on_drag_overdragenter, dragoverDragOverEventA system drag from any app enters or moves over the element.
on_drag_leavedragleaveDragLeaveEventThe system drag leaves the element without dropping.
on_dropdropDropEventData is dropped on the element in the type a dragover handler accepted.

shortcut(keybind) is the one method that takes no handler. While the element is mounted and enabled, the key combination clicks it, whatever has focus.

Event wrapper

Event<T> derefs to T, so event.position reads the payload directly.

MethodDescription
detail(&self) -> &TThe payload.
stop_propagation(&mut self)Stops the event from reaching ancestors.
prevent_default(&mut self)Cancels the default action: focus on pointer down, scrolling on wheel, activation and focus movement on key down, closing on a close request.
is_propagation_stopped(&self) -> boolWhether a handler stopped propagation.
is_default_prevented(&self) -> boolWhether a handler prevented the default.
is_synthetic(&self) -> boolWhether a test, an MCP agent or assistive technology injected the event.

Event payloads

ClickEvent, DragEvent, DragOverEvent, DragLeaveEvent, DropEvent, FocusEvent and InputEvent are exported from crownui::ext. So are KeyboardEvent, PointerEvent, WheelEvent, Key, NamedKey, Keybind and Modifiers. ScrollEvent, LongPressEvent and PinchEvent come from the view crate and TextInputEvent from the input crate. Handlers rarely name these types, because the closure infers them.

ClickEvent

FieldTypeDescription
positionPointRelative to the target's top-left corner.
countu32Consecutive clicks: 1, 2 or 3.
modifiersModifiersModifier keys held at the time.

PointerEvent

FieldTypeDescription
pointer_idPointerIdWhich pointer. PointerId::MOUSE is the mouse.
pointer_typePointerTypeMouse, Touch or Pen.
positionPointLogical pixels from the window's top-left corner.
buttonOption<PointerButton>Primary, Secondary, Auxiliary, Back, Forward or Other(u32).
modifiersModifiersModifier keys held.
timestampDurationMonotonic event time.

KeyboardEvent

FieldTypeDescription
keyKeyLogical key, like DOM key: Character(char), Named(NamedKey) or Unidentified.
codePhysicalKeyPhysical key, like DOM code. Holds the evdev scancode.
textOption<String>Text the key types, if any.
repeatboolWhether the key is auto-repeating.
modifiersModifiersModifier keys held.

Modifiers

ItemDescription
shift_key, ctrl_key, alt_key, meta_keybool fields, named as in the DOM.
Modifiers::NONENo modifiers held.
is_empty(self) -> boolWhether no modifier is held.

WheelEvent

FieldTypeDescription
deltaWheelDeltaPixels(Vector) from touchpads or Lines(Vector) from notched wheels. to_pixels(line_height) converts either.
positionPointPointer position in window coordinates.
modifiersModifiersModifier keys held.

ScrollEvent

FieldTypeDescription
offsetVectorThe scroll container's new content offset.

InputEvent

FieldTypeDescription
valueStringThe full new value of the field.

TextInputEvent

VariantDescription
Preedit { text: String, cursor: Option<(usize, usize)> }Composition in progress.
Commit(String)Text to insert. Pastes arrive this way too.
DeleteSurrounding { before: usize, after: usize }Bytes to delete around the caret.

FocusEvent

FieldTypeDescription
visibleboolTrue when focus arrived by keyboard, like CSS :focus-visible.

LongPressEvent

FieldTypeDescription
positionPointRelative to the target.

PinchEvent

FieldTypeDescription
scalef32Scale relative to the start of the gesture.
rotationf32Rotation in degrees since the previous event.

DragEvent

FieldTypeDescription
positionPointPointer position relative to the target.
deltaVectorMovement since the previous drag event.
totalVectorMovement since the drag started.
velocityVectorLogical pixels per second. Set on on_drag_end only.

DragOverEvent

MemberDescription
position: PointPointer position in window coordinates.
mime_types: Vec<String>Types the dragged data can be read as.
source_actions: DndActionsWhat the source allows.
modifiers: ModifiersModifier keys held, as this window last saw them.
offers(&self, mime_type: &str) -> boolWhether the drag can be read as mime_type.
accept(&mut self, mime_type: impl Into<String>)Lets it drop here as mime_type, preferring a copy.
accept_with(&mut self, mime_type: impl Into<String>, action: DndAction)Lets it drop here, preferring action.
is_accepted(&self) -> boolWhether a handler along the path already accepted it.

DragLeaveEvent

A unit struct. It carries no data.

DropEvent

FieldTypeDescription
positionPointDrop position in window coordinates.
mime_typeStringThe accepted type.
dataVec<u8>The dropped bytes.
actionDndActionThe action the source and window settled on.
modifiersModifiersModifier keys held at the drop.

Examples

Stop a click from reaching the card

vstack((
    text("Invoice 2041"),
    button("Pay").on_click(move |cx, event| {
        pay(cx);
        event.stop_propagation();
    }),
))
.on_click(move |cx, _| open_invoice(cx))

Close on Escape

use crownui::ext::{Key, NamedKey};
 
panel.on_key_down(move |cx, event| {
    if event.key == Key::Named(NamedKey::Escape) {
        open.set(cx, false);
        event.stop_propagation();
    }
})

Keyboard shortcut

use crownui::ext::Keybind;
 
let undo: Keybind = "Ctrl+Z".parse()?;
button("Undo").shortcut(undo).on_click(move |cx, _| history.undo(cx))

Keep focus in a field

button("Bold")
    .on_pointer_down(|_, event| event.prevent_default())
    .on_click(move |cx, _| toggle_bold(cx))

Ctrl+wheel zoom

canvas(painter)
    .on_wheel(move |cx, event| {
        if event.modifiers.ctrl_key {
            let step = event.delta.to_pixels(40.0).y;
            zoom.update(cx, |zoom| *zoom = (*zoom - step / 400.0).clamp(0.25, 8.0));
            event.prevent_default();
        }
    })

Drag to move

let offset = cx.signal(Vector::ZERO);
 
card.translate(offset).on_drag(move |cx, event| {
    let delta = event.delta;
    offset.update(cx, |offset| *offset = *offset + delta);
})

Accept a file drop

zone.on_drag_over(|_, event| {
    if event.offers("text/uri-list") {
        event.accept("text/uri-list");
    }
})
.on_drop(move |cx, event| {
    let uris = String::from_utf8_lossy(&event.data).into_owned();
    import(cx, &uris);
})

Listeners from a Kind

A widget's Kind::mount adds listeners to its node with cx.add_listener(node, Listener::Click(Box::new(handler))). Listener has one variant per method above, each holding a Handler<T>, which is Box<dyn FnMut(&mut Cx, &mut Event<T>)>. Listener::kind() returns its ListenerKind. Listener, ListenerKind and NodeId are in crownui::ext. See custom components.

See also