Overview
Refactor the Component trait's event handling system to replace the monolithic
on_event method with granular, opt-in event handlers and a bitmask-based
filtering system. This change eliminates boilerplate pattern matching in
components, improves runtime performance by skipping uninterested components,
and provides a more ergonomic API for component authors.
Current State
The current Component trait requires all components to implement on_event,
which receives every event and forces exhaustive pattern matching:
// crates/lambda-rs/src/component.rs:30-33fnon_event(&mutself,event:Events) -> Result<R,E>;
Components must match against all event variants even when they only care about
a subset:
// Example from crates/lambda-rs/examples/textured_cube.rs:370-385fnon_event(&mutself,event:Events) -> Result<ComponentResult,String>{match event {Events::Window{ event, .. } => match event {WindowEvent::Resize{ width, height } => {self.width = width;self.height = height;}
_ => {}},
_ => {}// Must handle all other variants}returnOk(ComponentResult::Success);}This approach has several limitations:
- Boilerplate: Every component must write
_ => {} arms for unused events - Poor branch prediction: Runtime iterates all components for every event
- No filtering: Components receive events they never handle
Scope
Goals:
- Introduce
EventMask bitmask type for O(1) event category filtering - Add granular
on_*_event methods with default no-op implementations - Update
ApplicationRuntime to skip components based on their event mask - Remove the
on_event method from the Component trait - Update all examples to use the new event handling API
Non-Goals:
- Generic
Handles<E> trait system (adds complexity without runtime benefit
when using trait objects) - Dynamic event subscription/unsubscription at runtime
- Event prioritization or ordering guarantees beyond current behavior
Proposed API
EventMask (events.rs)
/// Bitmask for O(1) event category filtering.#[derive(Clone,Copy,Default,Debug,PartialEq,Eq)]pubstructEventMask(u8);implEventMask{pubconstNONE:Self = Self(0);pubconstWINDOW:Self = Self(1 << 0);pubconstKEYBOARD:Self = Self(1 << 1);pubconstMOUSE:Self = Self(1 << 2);pubconstRUNTIME:Self = Self(1 << 3);pubconstCOMPONENT:Self = Self(1 << 4);/// Check if this mask contains the given event category.#[inline(always)]pubconstfncontains(self,other:Self) -> bool{(self.0& other.0) != 0}/// Combine two masks.#[inline(always)]pubconstfnunion(self,other:Self) -> Self{Self(self.0 | other.0)}}implEvents{/// Return the mask for this event's category.pubconstfnmask(&self) -> EventMask{matchself{Events::Window{ .. } => EventMask::WINDOW,Events::Keyboard{ .. } => EventMask::KEYBOARD,Events::Mouse{ .. } => EventMask::MOUSE,Events::Runtime{ .. } => EventMask::RUNTIME,Events::Component{ .. } => EventMask::COMPONENT,}}}Updated Component Trait (component.rs)
pubtraitComponent<R,E>whereR:Sized + Debug,E:Sized + Debug,{/// Return which event categories this component handles./// The runtime skips dispatch for events not in this mask.fnevent_mask(&self) -> EventMask{EventMask::NONE}/// Called when a window event occurs. Override to handle.fnon_window_event(&mutself,_event:&WindowEvent){}/// Called when a keyboard event occurs. Override to handle.fnon_keyboard_event(&mutself,_event:&Key){}/// Called when a mouse event occurs. Override to handle.fnon_mouse_event(&mutself,_event:&Mouse){}/// Called when a runtime event occurs. Override to handle.fnon_runtime_event(&mutself,_event:&RuntimeEvent){}/// Called when a component event occurs. Override to handle.fnon_component_event(&mutself,_event:&ComponentEvent){}// Existing lifecycle methods remain unchangedfnon_attach(&mutself,render_context:&mutRenderContext) -> Result<R,E>;fnon_detach(&mutself,render_context:&mutRenderContext) -> Result<R,E>;fnon_update(&mutself,last_frame:&Duration) -> Result<R,E>;fnon_render(&mutself,render_context:&mutRenderContext) -> Vec<RenderCommand>;}Example Usage
implComponent<ComponentResult,String>forPlayerController{fnevent_mask(&self) -> EventMask{EventMask::WINDOW.union(EventMask::KEYBOARD).union(EventMask::MOUSE)}fnon_window_event(&mutself,event:&WindowEvent){ifletWindowEvent::Resize{ width, height } = event {self.width = *width;self.height = *height;}}fnon_keyboard_event(&mutself,event:&Key){ifletKey::Pressed{virtual_key:Some(VirtualKey::Escape), .. } = event {self.paused = true;}}fnon_mouse_event(&mutself,event:&Mouse){ifletMouse::Moved{ x, y, .. } = event {self.cursor_position = (*x,*y);}}// on_runtime_event and on_component_event use default no-opfnon_attach(&mutself,_ctx:&mutRenderContext) -> Result<ComponentResult,String>{returnOk(ComponentResult::Success);}// ... other lifecycle methods}Acceptance Criteria
Affected Crates
lambda-rs
Notes
- This change is breaking and requires updates to all existing components
- The bitmask approach allows future extension (up to 8 event categories with
u8, expandable to u16 or u32 if needed)
Overview
Refactor the
Componenttrait's event handling system to replace the monolithicon_eventmethod with granular, opt-in event handlers and a bitmask-basedfiltering system. This change eliminates boilerplate pattern matching in
components, improves runtime performance by skipping uninterested components,
and provides a more ergonomic API for component authors.
Current State
The current
Componenttrait requires all components to implementon_event,which receives every event and forces exhaustive pattern matching:
Components must match against all event variants even when they only care about
a subset:
This approach has several limitations:
_ => {}arms for unused eventsScope
Goals:
EventMaskbitmask type for O(1) event category filteringon_*_eventmethods with default no-op implementationsApplicationRuntimeto skip components based on their event maskon_eventmethod from theComponenttraitNon-Goals:
Handles<E>trait system (adds complexity without runtime benefitwhen using trait objects)
Proposed API
EventMask (events.rs)
Updated Component Trait (component.rs)
Example Usage
Acceptance Criteria
EventMasktype added toevents.rswithNONE,WINDOW,KEYBOARD,MOUSE,RUNTIME,COMPONENTconstantsEvents::mask()method returns the appropriateEventMaskfor each variantComponenttrait updated withevent_mask()andon_*_event()methodson_eventmethod removed fromComponenttraitApplicationRuntimeupdated to filter components by mask before dispatchEventMaskoperationsComponenttrait and event handlingAffected Crates
lambda-rs
Notes
u8, expandable tou16oru32if needed)