External Events
External events are a way to append event logic to already existing views. You don't need to implement a new View just to handle one click, one mouse move, one shortcut. Create view then chain call on_* methods, hand closure to Flor.
These on_* methods come from EventBuilder, import it before use:
Complete signature see Handler API. This page first learns by usage process: start from one click, then gradually use mouse position, keyboard, focus, lifecycle and drag-drop.
First Click Event
Most common usage is directly chaining .on_click(...) after view:
The complete on_click closure has three parameters:
At first you can write a no-argument closure like above and focus only on the business logic. When you need the event source, mouse position, or modifier key state, add the parameters back. Mouse events also support receiving only ViewId, or omitting ViewId and receiving only KeyState and MousePosition.
If different examples show different parameter counts, that does not mean the event has multiple dispatch rules. It is the same handler conversion mechanism. Event Builder converts the closure, function, or method item you pass into the target handler. Handlers with extra event parameters usually support four writing forms:
Events whose complete parameter list is only ViewId, such as on_mouse_enter and on_create, only support the "full arguments" and "no arguments" forms. For the underlying mechanism, see Mechanism Explanation. For complete signatures, see Handler API.
Event closures usually write signal, send command, open window, request redraw, or post business message to own task system. Just remember one thing: event closures will execute in GUI event processing flow, don't do long blocking work inside.
Any View Can Attach Events
EventBuilder is implemented for all types implementing ViewIdentity, so events are not button exclusive. A label, a container, or a custom view can attach external events as long as it can provide a ViewId. A function returning impl IntoView can also continue chaining event bindings:
The first complete argument is the ViewId of the view that triggered the event. It can be used to access view state, request redraw, set focus, capture mouse, and similar operations. Normal business code may not need it and can omit it directly; when you need to associate an event with a specific view, it is the most direct entry point.
Mouse Position and Button State
Mouse-type events mostly use same set of parameters:
key_state records mouse button and modifier key state when event occurs; mouse_position saves coordinates. For on_mouse_move, on_button_down, on_button_up, on_click, on_double_click — these kinds of mouse hit events, coordinates will be converted to target view's local coordinates, (0, 0) means view top-left corner.
Click event is synthetic event: only when left button down and up both land on same hit view, Flor will trigger on_click. If just want to know when button down or up, use on_button_down and on_button_up.
Right button also has corresponding events:
Middle button currently exposed on_middle_button_down, on_middle_button_up and on_middle_button_double_click. Source code has internal middle click synthesis logic, but external event API currently doesn't have on_middle_button_click.
Keyboard Events Need to Get Focus First
Keyboard events are dispatched to currently focused view. Normal views if want to participate in Tab focus order, need to pair with focus_index. More complete focus explanation see Focus Mechanism.
on_key_down and on_key_up both need to return HandleResult. Keyboard events can use the full arguments |view_id, code, is_alt, is_ctrl, is_shift|, or omit ViewId as above:
Flor internal view logic and external handler both may return Handled. As long as either side returns Handled, final result is Handled.
Focus Change
on_focus and on_blur are used to listen to focus enter and leave. Closure receives ViewId and a u16 type virtual focus number.
Most views only have one focus point, this number is usually 0. Few composite views can expose multiple virtual focus points through View::on_focus_count, then same view internally can distinguish different focus positions.
Create Event
on_create will trigger after view tree mounts to window. It's suitable for doing initialization work depending on ViewId already entering view tree.
If just initializing normal Rust data, usually can directly complete before creating view. Only when logic depends on view already registered, already has ViewId and window relationship, then consider on_create.
Wheel Event
Current wheel external event API name is on_wheel_settings_changed. It triggers in mouse wheel message path, closure receives scroll direction, scroll amount, button state and mouse position.
Wheel dispatch will from hit view upward look for scrollable ancestor; if not found, then post to window root view. Currently this path passes in window client area coordinates, not target view local coordinates.
Drag-Drop Events
Drag-drop events need to enable drag-drop feature. Common flow is:
on_drag_enterjudges dragged in data format, setsDropEffect;on_drag_overcontinuously updates effect during drag move;on_dropreads real data and completes business processing;on_drag_leavecleans up hover state.
on_drag_enter and on_drag_over receive available format list, haven't really extracted data; on_drop will receive DragData. Same as wheel, current drag-drop path passes in window client area coordinates.
Usage Suggestions
External events are suitable for application layer logic: click button modify signal, press shortcut trigger command, drag-drop files to panel, update preview state when mouse move.
If you are writing view library, and event logic is view itself inseparable part, prefer to implement View's internal on_* methods. For example how input field handles IME, cursor, selection and text editing, should belong to view internal logic; application side only needs to bind higher-level external events or the view's own exposed business callbacks.
Event closures need to satisfy Send + Sync + 'static. Common practice is capture signal handle, Arc, channel sender, or other data that can be safely stored long-term. Don't capture temporary references.
Mechanism Explanation
This section is for understanding external events' position in Flor. Normal use doesn't need to read here first.
When each ViewId is created, Flor will put a default ViewHandler in VIEW_STORAGE.handlers. ViewHandler is a set of optional handler slots, for example on_click_handler, on_key_down_handler, on_focus_handler.
When you call:
EventBuilder will take out this view's handler storage, convert the closure through IntoEventHandler into the corresponding handler wrapper type, and write to ViewHandler's on_click_handler slot. Method returns original self, so it can naturally continue chain calling.
This conversion is trait-driven: the full-argument signature first goes through the handler wrapper type's From<F> implementation, while simplified signatures are handled by other IntoEventHandler<T, Args> implementations that fill in the omitted arguments. Args is a marker type used for Rust trait inference. You usually do not write it when calling event methods; you only need to carry this generic parameter out when you write your own function that accepts an event handler. See Framework DSL: Method Composing Views for the forwarding pattern.
After event enters Flor from platform layer, approximate path is:
- Platform window procedure converts system message into
Message; WindowsProcHandlerhands message towindows::event;- Event bus processes by event type: mouse events first hit test, keyboard events find current focus, drag-drop events maintain current drag-drop target;
- After finding target
ViewIdcall correspondingcall_*method; call_*first executes view internalView::on_*, then executes external handler.
Most events are "internal logic + external logic" both will execute. Keyboard events will merge both sides' HandleResult: either side returns Handled, final is Handled. Drag-drop events will first use internal on_drag_* return value to initialize DropEffect, then hand &mut DropEffect to external handler to modify.
Tooltip is an internal special case: its call_tooltip_show and call_tooltip_hide use override mode, if external handler exists, only execute external handler; otherwise execute view internal method. However current EventBuilder doesn't expose the tooltip corresponding chain method, normal users temporarily don't need to depend on this detail.
There are also some handler slots already existing in API, but current event bus hasn't completely dispatched to external handler, for example on_context_menu, on_destroy, on_resize, on_close_requested, on_work_area_changed, on_dpi_change and on_theme_changed. When querying specific status, use Handler API's "Current Dispatch Status" as standard.

