crux_core/capabilities/
render.rs

1//! Built-in capability used to notify the Shell that a UI update is necessary.
2
3use std::future::Future;
4
5use facet::Facet;
6use serde::{Deserialize, Serialize};
7
8#[expect(deprecated)]
9use crate::{Capability, capability::CapabilityContext};
10use crate::{Command, Request, capability::Operation, command::NotificationBuilder};
11
12/// Use an instance of `Render` to notify the Shell that it should update the user
13/// interface. This assumes a declarative UI framework is used in the Shell, which will
14/// take the `ViewModel` provided by [`Core::view`](crate::Core::view) and reconcile the new UI state based
15/// on the view model with the previous one.
16///
17/// For imperative UIs, the Shell will need to understand the difference between the two
18/// view models and update the user interface accordingly.
19#[deprecated(
20    since = "0.16.0",
21    note = "The Render capability has been deprecated. Use render::render() with the Command API instead."
22)]
23pub struct Render<Ev> {
24    #[expect(deprecated)]
25    context: CapabilityContext<RenderOperation, Ev>,
26}
27
28#[expect(deprecated)]
29impl<Ev> Clone for Render<Ev> {
30    fn clone(&self) -> Self {
31        Self {
32            context: self.context.clone(),
33        }
34    }
35}
36
37/// The single operation `Render` implements.
38#[derive(Facet, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
39pub struct RenderOperation;
40
41impl Operation for RenderOperation {
42    type Output = ();
43}
44
45/// Public API of the capability, called by `App::update`.
46#[expect(deprecated)]
47impl<Ev> Render<Ev>
48where
49    Ev: 'static,
50{
51    #[must_use]
52    pub fn new(context: CapabilityContext<RenderOperation, Ev>) -> Self {
53        Self { context }
54    }
55
56    /// Call `render` from [`App::update`](crate::App::update) to signal to the Shell that
57    /// UI should be re-drawn.
58    pub fn render(&self) {
59        let ctx = self.context.clone();
60        self.context.spawn(async move {
61            ctx.notify_shell(RenderOperation).await;
62        });
63    }
64}
65
66#[expect(deprecated)]
67impl<Ev> Capability<Ev> for Render<Ev> {
68    type Operation = RenderOperation;
69    type MappedSelf<MappedEv> = Render<MappedEv>;
70
71    fn map_event<F, NewEv>(&self, f: F) -> Self::MappedSelf<NewEv>
72    where
73        F: Fn(NewEv) -> Ev + Send + Sync + 'static,
74        Ev: 'static,
75        NewEv: 'static,
76    {
77        Render::new(self.context.map_event(f))
78    }
79}
80
81/// Signal to the shell that the UI should be redrawn.
82/// Returns a [`NotificationBuilder`].
83///
84/// ### Examples:
85/// To use in a sync context:
86/// ```
87///# use crux_core::{Command, render::{render_builder, Render, RenderOperation}};
88///# #[crux_core::macros::effect]pub enum Effect {Render(RenderOperation)}
89///# enum Event {None}
90/// let command: Command<Effect, Event> =
91///     render_builder().into(); // or use `render_command()`
92/// ```
93/// To use in an async context:
94/// ```
95///# use crux_core::{Command, render::{render_builder, Render, RenderOperation}};
96///# #[crux_core::macros::effect]pub enum Effect {Render(RenderOperation)}
97///# enum Event {None}
98///# let command: Command<Effect, Event> = Command::new(|ctx| async move {
99/// render_builder().into_future(ctx).await;
100///# });
101/// ```
102#[must_use]
103pub fn render_builder<Effect, Event>()
104-> NotificationBuilder<Effect, Event, impl Future<Output = ()>>
105where
106    Effect: From<Request<RenderOperation>> + Send + 'static,
107    Event: Send + 'static,
108{
109    Command::notify_shell(RenderOperation)
110}
111
112/// Signal to the shell that the UI should be redrawn.
113/// Returns a [`Command`].
114pub fn render<Effect, Event>() -> Command<Effect, Event>
115where
116    Effect: From<Request<RenderOperation>> + Send + 'static,
117    Event: Send + 'static,
118{
119    render_builder().into()
120}