use iris_core::*; use iris_macro::*; use std::sync::Arc; use crate::task::{TaskCtx, TaskUpdate, Tasks}; /// A field's Enter key (without a shift, in a multi-line field). Backend /// input handling raises it directly rather than through `on`, since a /// field does not know ahead of time whether anything is listening. #[derive(Eq, PartialEq, Hash, Clone)] pub struct Submit; impl Event for Submit {} /// A field's content changed as a result of input the backend applied /// directly to it (a keystroke, an IME commit) rather than through a /// widget event handler. #[derive(Eq, PartialEq, Hash, Clone)] pub struct Edited; impl Event for Edited {} pub trait Eventable: WidgetLike { fn on( self, event: E, f: impl for<'a> WidgetEventFn::Data<'a>, Self::Widget>, ) -> impl WidgetIdFn { move |rsc| { let id = self.add(rsc); rsc.register_event(id, event.into_event(), move |ctx, rsc| { f( EventIdCtx { widget: id, state: ctx.state, data: ctx.data, }, rsc, ); }); id } } } impl, Rsc: HasEvents, Tag> Eventable for WL {} widget_trait! { pub trait TaskEventable; /// No `Data: Send` bound, deliberately: the registered handler below /// takes `|_, rsc|` and the event's data never crosses into the /// spawned future -- `AsyncEventIdCtx` carries the widget id and the /// task handle and nothing else. The bound used to be here anyway, and /// it was the whole reason `CursorData`'s pointer state was behind a /// `Mutex` rather than owned by the input handler (Iris, 2026-09-08: /// never reach for a lock first). fn task_on>( self, event: E, f: F, ) -> impl WidgetIdFn where for<'b> F::CallRefFuture<'b>: Send, { let f = Arc::new(f); move |rsc| { let id = self.add(rsc); rsc.register_event(id, event.into_event(), move |_, rsc| { let f = f.clone(); rsc.tasks_mut().spawn(async move |task| { f(AsyncEventIdCtx { widget: id, task, }).await; }); }); id } } } pub trait HasTasks: Sized + HasState + HasEvents { fn tasks_mut(&mut self) -> &mut Tasks; fn spawn_task) + 'static + std::marker::Send>(&mut self, task: F) where F::CallOnceFuture: Send, { self.tasks_mut().spawn(task); } } pub trait AsyncWidgetEventFn: AsyncFn(AsyncEventIdCtx) + Send + Sync + 'static { } impl) + Send + Sync + 'static, W: ?Sized> AsyncWidgetEventFn for F { } pub struct AsyncEventIdCtx { pub widget: WeakWidget, task: TaskCtx, } impl AsyncEventIdCtx { pub fn update(&mut self, f: impl TaskUpdate + 'static) { self.task.update(f); } }