app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit edea0bec57287f95dc080863ea0076eef9ad690a
parent 99c644eb61fc8cb0c987318c5bafa61200e425ef
Author: triesap <triesap@radroots.dev>
Date:   Wed, 21 Jan 2026 19:54:11 +0000

ui: add event composition helpers

- add compose helpers with default-prevented predicate

- add unchecked compose helper for simple handlers

- add unit tests for ordering and prevention

- export event helpers from ui-core

Diffstat:
Acrates/ui-core/src/event.rs | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/ui-core/src/lib.rs | 5+++++
2 files changed, 88 insertions(+), 0 deletions(-)

diff --git a/crates/ui-core/src/event.rs b/crates/ui-core/src/event.rs @@ -0,0 +1,83 @@ +pub fn radroots_app_ui_compose_event_handlers<T, A, B, P>( + mut first: Option<A>, + mut second: Option<B>, + mut is_prevented: P, +) -> impl FnMut(&T) +where + A: FnMut(&T), + B: FnMut(&T), + P: FnMut(&T) -> bool, +{ + move |event| { + if let Some(handler) = first.as_mut() { + handler(event); + } + if is_prevented(event) { + return; + } + if let Some(handler) = second.as_mut() { + handler(event); + } + } +} + +pub fn radroots_app_ui_compose_event_handlers_unchecked<T, A, B>( + first: Option<A>, + second: Option<B>, +) -> impl FnMut(&T) +where + A: FnMut(&T), + B: FnMut(&T), +{ + radroots_app_ui_compose_event_handlers(first, second, |_| false) +} + +#[cfg(test)] +mod tests { + use super::{ + radroots_app_ui_compose_event_handlers, + radroots_app_ui_compose_event_handlers_unchecked, + }; + + #[derive(Default)] + struct TestEvent { + calls: core::cell::Cell<usize>, + prevented: core::cell::Cell<bool>, + } + + impl TestEvent { + fn mark_called(&self) { + self.calls.set(self.calls.get() + 1); + } + + fn prevent(&self) { + self.prevented.set(true); + } + } + + #[test] + fn compose_calls_handlers_in_order() { + let event = TestEvent::default(); + let mut handler = radroots_app_ui_compose_event_handlers_unchecked( + Some(|evt: &TestEvent| evt.mark_called()), + Some(|evt: &TestEvent| evt.mark_called()), + ); + handler(&event); + assert_eq!(event.calls.get(), 2); + } + + #[test] + fn compose_skips_second_when_prevented() { + let event = TestEvent::default(); + let mut handler = radroots_app_ui_compose_event_handlers( + Some(|evt: &TestEvent| { + evt.mark_called(); + evt.prevent(); + }), + Some(|evt: &TestEvent| evt.mark_called()), + |evt: &TestEvent| evt.prevented.get(), + ); + handler(&event); + assert_eq!(event.calls.get(), 1); + } +} diff --git a/crates/ui-core/src/lib.rs b/crates/ui-core/src/lib.rs @@ -4,5 +4,10 @@ extern crate alloc; mod id; +mod event; +pub use event::{ + radroots_app_ui_compose_event_handlers, + radroots_app_ui_compose_event_handlers_unchecked, +}; pub use id::{RadrootsAppUiId, RadrootsAppUiIdSequence};