use std::sync::Arc;
use super::builders::TabId;
use crate::controls::date_picker::CivilDate;
use crate::events::EventPayload;
pub(super) type EventCallback = Arc<dyn Fn(EventPayload) + Send + Sync>;
pub(super) fn on_click(handler: Arc<dyn Fn() + Send + Sync>) -> EventCallback {
Arc::new(move |payload| {
if matches!(payload, EventPayload::Click) {
handler();
}
})
}
pub(super) fn on_toggled(handler: Arc<dyn Fn(bool) + Send + Sync>) -> EventCallback {
Arc::new(move |payload| {
if let EventPayload::Toggled(checked) = payload {
handler(checked);
}
})
}
pub(super) fn on_value_changed(handler: Arc<dyn Fn(i32) + Send + Sync>) -> EventCallback {
Arc::new(move |payload| {
if let EventPayload::ValueChanged { value, .. } = payload {
handler(value);
}
})
}
pub(super) fn on_selected(handler: Arc<dyn Fn(usize) + Send + Sync>) -> EventCallback {
Arc::new(move |payload| {
if let EventPayload::Selected(index) = payload {
handler(index);
}
})
}
pub(super) fn on_date(handler: Arc<dyn Fn(CivilDate) + Send + Sync>) -> EventCallback {
Arc::new(move |payload| {
if let EventPayload::Date(date) = payload {
handler(date);
}
})
}
pub(super) type TabHandler = Option<Arc<dyn Fn(TabId) + Send + Sync>>;
pub(super) fn on_tab_bar(
ids: Arc<[TabId]>,
on_select: TabHandler,
on_reselect: TabHandler,
) -> EventCallback {
Arc::new(move |payload| {
let (handler, index) = match payload {
EventPayload::Selected(index) => (&on_select, index),
EventPayload::Reselected(index) => (&on_reselect, index),
_ => return,
};
if let (Some(handler), Some(id)) = (handler, ids.get(index)) {
handler(id.clone());
}
})
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
#[test]
fn on_tab_bar_maps_indices_to_ids_and_splits_select_from_reselect() {
let selected = Arc::new(Mutex::new(Vec::new()));
let reselected = Arc::new(Mutex::new(Vec::new()));
let (sel, resel) = (Arc::clone(&selected), Arc::clone(&reselected));
let ids: Arc<[TabId]> = vec![TabId::new("home"), TabId::new("inbox")].into();
let cb = on_tab_bar(
ids,
Some(Arc::new(move |id: TabId| sel.lock().unwrap().push(id))),
Some(Arc::new(move |id: TabId| resel.lock().unwrap().push(id))),
);
cb(EventPayload::Selected(1));
cb(EventPayload::Reselected(0));
cb(EventPayload::Selected(2)); cb(EventPayload::Click);
assert_eq!(*selected.lock().unwrap(), vec![TabId::new("inbox")]);
assert_eq!(*reselected.lock().unwrap(), vec![TabId::new("home")]);
}
#[test]
fn on_tab_bar_without_a_reselect_handler_ignores_reselects() {
let selected = Arc::new(Mutex::new(Vec::new()));
let sel = Arc::clone(&selected);
let cb = on_tab_bar(
vec![TabId::new("a")].into(),
Some(Arc::new(move |id: TabId| sel.lock().unwrap().push(id))),
None,
);
cb(EventPayload::Reselected(0));
assert!(selected.lock().unwrap().is_empty());
}
#[test]
fn on_click_fires_only_for_click() {
let seen = Arc::new(Mutex::new(0));
let recorder = Arc::clone(&seen);
let cb = on_click(Arc::new(move || *recorder.lock().unwrap() += 1));
cb(EventPayload::Click);
cb(EventPayload::Toggled(true));
cb(EventPayload::DragStart);
assert_eq!(*seen.lock().unwrap(), 1);
}
#[test]
fn on_toggled_forwards_the_checked_state() {
let seen = Arc::new(Mutex::new(Vec::new()));
let recorder = Arc::clone(&seen);
let cb = on_toggled(Arc::new(move |checked| {
recorder.lock().unwrap().push(checked)
}));
cb(EventPayload::Toggled(true));
cb(EventPayload::Click);
cb(EventPayload::Toggled(false));
assert_eq!(*seen.lock().unwrap(), vec![true, false]);
}
#[test]
fn on_value_changed_forwards_the_app_space_value_only() {
let seen = Arc::new(Mutex::new(Vec::new()));
let recorder = Arc::clone(&seen);
let cb = on_value_changed(Arc::new(move |value| recorder.lock().unwrap().push(value)));
cb(EventPayload::ValueChanged {
value: 42,
from_user: true,
});
cb(EventPayload::DragEnd);
assert_eq!(*seen.lock().unwrap(), vec![42]);
}
#[test]
fn on_selected_forwards_the_requested_index_only() {
let seen = Arc::new(Mutex::new(Vec::new()));
let recorder = Arc::clone(&seen);
let cb = on_selected(Arc::new(move |index| recorder.lock().unwrap().push(index)));
cb(EventPayload::Selected(2));
cb(EventPayload::Toggled(true));
cb(EventPayload::Click);
cb(EventPayload::Selected(0));
assert_eq!(*seen.lock().unwrap(), vec![2, 0]);
}
#[test]
fn on_date_forwards_the_requested_date_only() {
let seen = Arc::new(Mutex::new(Vec::new()));
let recorder = Arc::clone(&seen);
let cb = on_date(Arc::new(move |date| recorder.lock().unwrap().push(date)));
let picked = CivilDate::new(2026, 10, 3).unwrap();
cb(EventPayload::Date(picked));
cb(EventPayload::Selected(1));
cb(EventPayload::ValueChanged {
value: 3,
from_user: true,
});
cb(EventPayload::Date(CivilDate::MIN));
assert_eq!(*seen.lock().unwrap(), vec![picked, CivilDate::MIN]);
}
}