| title | Component Event Handling | ||||
|---|---|---|---|---|---|
| document_id | component-event-handling-2026-01-10 | ||||
| status | draft | ||||
| created | 2026-01-10T00:00:00Z | ||||
| last_updated | 2026-02-07T00:00:00Z | ||||
| version | 0.1.2 | ||||
| engine_workspace_version | 2023.1.30 | ||||
| wgpu_version | 26.0.1 | ||||
| shader_backend_default | naga | ||||
| winit_version | 0.29.10 | ||||
| repo_commit | 544444652b4dc3639f8b3e297e56c302183a7a0b | ||||
| owners |
|
||||
| reviewers |
|
||||
| tags |
|
- Summary
- Scope
- Terminology
- Architecture Overview
- Design
- Constraints and Rules
- Performance Considerations
- Requirements Checklist
- Verification and Testing
- Compatibility and Migration
- Changelog
- Replace the monolithic
Component::on_evententry point with granular, opt-inon_*_eventhandlers that default to a no-op. - Introduce
EventMask, a compact bitmask used by runtimes to filter event dispatch to only components that declare interest in an event category. - Reduce boilerplate pattern matching in components and reduce runtime work by skipping dispatch for uninterested components.
- Introduce
EventMaskas an O(1) event category filter inlambda-rs. - Add granular
Componentevent handler methods with default no-op behavior. - Update
ApplicationRuntimeto skip dispatch for uninterested components. - Remove
Component::on_eventfrom the public API. - Update all examples to use the new event handling API.
- Add unit tests for
EventMaskbehavior. - Update user-facing documentation for component event handling.
- Generic
Handles<E>trait systems for static event typing. - Dynamic event subscription or unsubscription at runtime.
- Event prioritization, re-ordering, or new ordering guarantees.
- Changes to component lifecycle callbacks (
on_attach,on_detach,on_update,on_render) beyond event system refactoring.
- Event category: a coarse grouping of events mapped 1:1 to
Eventsvariants (for example, window events and keyboard events). - Event mask: a bitmask (
EventMask) describing a set of event categories. - Dispatch: invoking a component callback in response to an incoming event.
- Component stack: the ordered list of components stored by a runtime.
- Crate
lambda-rsevents.rsdefinesEventsand its sub-event types (WindowEvent,Key,Mouse,RuntimeEvent,ComponentEvent).component.rsdefinesComponent, the trait implemented by user components.runtimes/application.rsdefinesApplicationRuntime, which maps platform events intoEventsand dispatches them to components.
Data flow
winit event loop
└── ApplicationRuntime event mapping
└── Events (category + payload)
├── event.mask() -> EventMask::<CATEGORY>
└── for component in component_stack:
if component.event_mask().contains(event.mask()):
component.on_<category>_event(&payload)
-
EventMask(crates/lambda-rs/src/events.rs)EventMaskMUST be aCopynewtype around an integer bitmask.EventMaskMUST define category constants:NONEWINDOWKEYBOARDMOUSERUNTIMECOMPONENT
EventMaskMUST providecontains(self, other) -> bool.EventMaskMUST provide a union operation viaunion(self, other) -> Self.EventMaskMAY additionally implement operator traits (for example,BitOr) as sugar forunion.
-
Events::mask(crates/lambda-rs/src/events.rs)EventsMUST exposemask(&self) -> EventMask.maskMUST return the category mask for the event variant:Events::Window { .. }->EventMask::WINDOWEvents::Keyboard { .. }->EventMask::KEYBOARDEvents::Mouse { .. }->EventMask::MOUSEEvents::Runtime { .. }->EventMask::RUNTIMEEvents::Component { .. }->EventMask::COMPONENT
-
Component(crates/lambda-rs/src/component.rs)ComponentMUST remove theon_eventmethod.ComponentMUST addevent_mask(&self) -> EventMaskwith a default implementation returningEventMask::NONE.ComponentMUST add granular event handlers with default no-op behavior:on_window_event(&mut self, _event: &WindowEvent) -> Result<(), E>on_keyboard_event(&mut self, _event: &Key) -> Result<(), E>on_mouse_event(&mut self, _event: &Mouse) -> Result<(), E>on_runtime_event(&mut self, _event: &RuntimeEvent) -> Result<(), E>on_component_event(&mut self, _event: &ComponentEvent) -> Result<(), E>
- The signatures above MUST be used so default no-op implementations can
return
Ok(())without constrainingR.
-
Dispatch filtering
- Runtimes that dispatch
Eventsto components (for example,ApplicationRuntime) MUST computeevent_mask = event.mask()once per incoming event. - Runtimes MUST skip dispatch to a component when
!component.event_mask().contains(event_mask). - A component that overrides an
on_*_eventhandler MUST include the corresponding category bit inevent_mask. Otherwise, the handler is not invoked.
- Runtimes that dispatch
-
Dispatch mapping
- When dispatch occurs, the runtime MUST call exactly one handler based on
the event variant:
Events::Window { event, .. }->Component::on_window_event(&event)Events::Keyboard { event, .. }->Component::on_keyboard_event(&event)Events::Mouse { event, .. }->Component::on_mouse_event(&event)Events::Runtime { event, .. }->Component::on_runtime_event(&event)Events::Component { event, .. }->Component::on_component_event(&event)
- When dispatch occurs, the runtime MUST call exactly one handler based on
the event variant:
-
Ordering
- The runtime MUST preserve the existing component stack iteration order.
- The runtime MUST preserve the existing event ordering from the platform event loop.
Events::maskMUST NOT return unknown or reserved category bits.EventMaskbits beyond the defined categories are reserved for future expansion and MUST NOT be relied upon by components.- Runtime handling of event handler failures
- When a handler returns
Err(e), the runtime MUST treat it as a fatal component failure for the current run and MUST publish aRuntimeEvent::ComponentPanic(or the runtime-equivalent error signal) with a descriptive message that includes the formatted error value.
- When a handler returns
- This change introduces no new Cargo features.
EventMaskMUST remain small and cheap to copy.u8is sufficient for the initial set of categories.- If more than 8 categories are required, the mask storage type MUST be
expanded (for example,
u16oru32) without changing the semantics ofcontainsandunion.
- Each
Eventsvariant MUST map to exactly one category bit. - Category bits MUST remain stable within a major version of
lambda-rs.
- Recommendations
- Runtimes SHOULD avoid cloning
Eventsper component when dispatching. - Runtimes SHOULD compute the event category mask once per event.
- Components SHOULD keep
event_maskside-effect free and allocation free.
- Runtimes SHOULD avoid cloning
- Rationale
- Skipping uninterested components reduces work on hot event dispatch paths.
- Avoiding per-component
Eventsclones reduces allocation and copy work.
- Functionality
-
EventMaskdefined incrates/lambda-rs/src/events.rs(Ref:crates/lambda-rs/src/events.rs) -
Events::mask()implemented for allEventsvariants (Ref:crates/lambda-rs/src/events.rs) -
Componentupdated withevent_maskandon_*_eventhandlers (Ref:crates/lambda-rs/src/component.rs) -
Component::on_eventremoved from the public API (Ref:crates/lambda-rs/src/component.rs) -
ApplicationRuntimedispatch filters components byEventMask(Ref:crates/lambda-rs/src/runtimes/application.rs)
-
- API Surface
- Public
EventMaskconstants documented (Ref:crates/lambda-rs/src/events.rs) -
Componenttrait documentation updated (Ref:docs/rendering.md) - Backwards compatibility assessed and migration documented (Ref:
docs/specs/runtime/component-event-handling.md)
- Public
- Validation and Errors
- Error behavior specified for handler failures (Ref:
docs/specs/runtime/component-event-handling.md) - Runtime publishes
RuntimeEvent::ComponentPanicon handler errors (Ref:crates/lambda-rs/src/runtimes/application.rs)
- Error behavior specified for handler failures (Ref:
- Performance
- Per-event dispatch avoids per-component
Eventsclones (Ref:crates/lambda-rs/src/runtimes/application.rs) - Filtering implemented before dispatch match (Ref:
crates/lambda-rs/src/runtimes/application.rs)
- Per-event dispatch avoids per-component
- Documentation and Examples
- Examples updated to implement
event_maskand granular handlers (Ref:demos/render/src/bin/) -
docs/features.mdchecked for relevance (no new features expected) (Ref:docs/features.md) - Migration notes added to component documentation (Ref:
docs/specs/runtime/component-event-handling.md)
- Examples updated to implement
For each checked item, include a reference to a commit, pull request, or file path that demonstrates the implementation.
-
Unit Tests
EventMask::containsandEventMask::unioncover:EventMask::NONEbehavior- union associativity for multiple categories
- contains behavior for present and absent categories
Events::maskcovers one representative value per variant.- Commands:
cargo test -p lambda-rs -- --nocapture
-
Integration Tests
- A runnable scenario SHOULD validate that an event triggers a handler only
on components whose
event_maskincludes the event category. - Commands:
cargo test --workspace
- A runnable scenario SHOULD validate that an event triggers a handler only
on components whose
-
Breaking changes
Component::on_eventis removed.- Components MUST migrate to
event_maskand granular handlers.
-
Migration steps
- Replace
fn on_event(&mut self, event: Events) -> Result<R, E>with:fn event_mask(&self) -> EventMaskreturning the relevant categories.- One or more
on_*_eventhandlers for the event payload types previously matched insideon_event.
- Pattern matching moves from
Eventsto payload types:match event { Events::Window { event, .. } => ... }becomesfn on_window_event(&mut self, event: &WindowEvent) -> Result<(), E> { .. }.
- Replace
- 2026-02-05 (v0.1.2) — Update example references to demo crates under
demos/. - 2026-01-16 (v0.1.1) — Update checklist references and align docs.
- 2026-01-10 (v0.1.0) — Initial draft.