Skip to main content

purview_gpui/
handler.rs

1//! Action handlers that drive a gpui [`Entity`] on the main thread, removing
2//! the per-action `entity.clone()` + `entity.update(app, …)` boilerplate.
3
4use gpui::{App, Context, Entity};
5use purview::{ActionError, OnUi, Outcome, on_ui};
6
7/// Build a parameterless action that mutates a gpui [`Entity`] on the main
8/// thread, exactly like a button's `cx.listener`.
9///
10/// The returned value is what [`ActionBuilder::on`](purview::ActionBuilder::on)
11/// accepts (it builds on `on_ui(|cx|)`).
12///
13/// ```ignore
14/// win.actions().add("increment", "Increment").on(on_entity(&counter, |c, cx| {
15///     c.bump(1, cx);
16///     Ok::<_, ActionError>(Outcome::message(format!("count = {}", c.count)))
17/// }));
18/// ```
19pub fn on_entity<V, O, F>(
20    entity: &Entity<V>,
21    f: F,
22) -> OnUi<impl Fn(&mut App) -> Result<O, ActionError> + Send + Sync + 'static>
23where
24    V: 'static,
25    O: Into<Outcome> + Send + 'static,
26    F: Fn(&mut V, &mut Context<V>) -> Result<O, ActionError> + Send + Sync + 'static,
27{
28    let entity = entity.clone();
29    on_ui(move |cx: &mut App| entity.update(cx, |v, c| f(v, c)))
30}
31
32/// Like [`on_entity`], but the action takes a typed parameter `P` (builds on
33/// `on_ui(|p, cx|)`). Declare the schema with
34/// [`ActionBuilder::params`](purview::ActionBuilder::params).
35pub fn on_entity_with<V, P, O, F>(
36    entity: &Entity<V>,
37    f: F,
38) -> OnUi<impl Fn(P, &mut App) -> Result<O, ActionError> + Send + Sync + 'static>
39where
40    V: 'static,
41    P: for<'de> serde::Deserialize<'de> + Send + 'static,
42    O: Into<Outcome> + Send + 'static,
43    F: Fn(P, &mut V, &mut Context<V>) -> Result<O, ActionError> + Send + Sync + 'static,
44{
45    let entity = entity.clone();
46    on_ui(move |p: P, cx: &mut App| entity.update(cx, |v, c| f(p, v, c)))
47}