1use crate::{
2 context::{DisposeCtx, MountCtx, RenderCtx, StartCtx, StopCtx, UpdateCtx},
3 event::Event,
4 host::HostRunner,
5 internal::{DispatchToken, Dispatcher, RuntimeEvent, RuntimeServices, WakeHandle},
6 widget::Widget,
7};
8use crossbeam_channel::{Receiver, TryRecvError, unbounded};
9use widgetkit_core::{Error, HostEvent, InstanceId, Result, Size, WidgetId};
10use widgetkit_render::{Canvas, RenderSurface, Renderer};
11
12pub struct WidgetApp<W = (), H = (), R = ()> {
15 widget_name: Option<String>,
16 widget: Option<W>,
17 host: Option<H>,
18 renderer: Option<R>,
19}
20
21impl WidgetApp<(), (), ()> {
22 pub fn new() -> Self {
23 Self {
24 widget_name: None,
25 widget: None,
26 host: None,
27 renderer: None,
28 }
29 }
30}
31
32impl<H, R> WidgetApp<(), H, R> {
33 pub fn widget<W>(self, name: impl Into<String>, widget: W) -> WidgetApp<W, H, R>
34 where
35 W: Widget,
36 {
37 WidgetApp {
38 widget_name: Some(name.into()),
39 widget: Some(widget),
40 host: self.host,
41 renderer: self.renderer,
42 }
43 }
44}
45
46impl<W, R> WidgetApp<W, (), R>
47where
48 W: Widget,
49{
50 pub fn host<H>(self, host: H) -> WidgetApp<W, H, R> {
51 WidgetApp {
52 widget_name: self.widget_name,
53 widget: self.widget,
54 host: Some(host),
55 renderer: self.renderer,
56 }
57 }
58}
59
60impl<W, H> WidgetApp<W, H, ()>
61where
62 W: Widget,
63{
64 pub fn renderer<R>(self, renderer: R) -> WidgetApp<W, H, R> {
65 WidgetApp {
66 widget_name: self.widget_name,
67 widget: self.widget,
68 host: self.host,
69 renderer: Some(renderer),
70 }
71 }
72}
73
74impl<W, H, R> WidgetApp<W, H, R>
75where
76 W: Widget,
77 H: HostRunner<W, R>,
78 R: Renderer,
79{
80 pub fn run(self) -> Result<()> {
81 let runner = AppRunner::new(
82 self.widget_name.expect("widget name must be configured"),
83 self.widget.expect("widget must be configured"),
84 self.renderer.expect("renderer must be configured"),
85 );
86 self.host.expect("host must be configured").run(runner)
87 }
88}
89
90impl Default for WidgetApp<(), (), ()> {
91 fn default() -> Self {
92 Self::new()
93 }
94}
95
96pub struct AppRunner<W, R>
99where
100 W: Widget,
101 R: Renderer,
102{
103 widget_name: String,
104 widget_id: WidgetId,
105 instance_id: InstanceId,
106 instance_generation: u64,
107 widget: W,
108 state: Option<W::State>,
109 renderer: R,
110 receiver: Receiver<RuntimeEvent<W::Message>>,
111 services: RuntimeServices<W::Message>,
112 surface_size: Size,
113 initialized: bool,
114 shut_down: bool,
115}
116
117impl<W, R> AppRunner<W, R>
118where
119 W: Widget,
120 R: Renderer,
121{
122 pub fn new(widget_name: impl Into<String>, widget: W, renderer: R) -> Self {
123 let (sender, receiver) = unbounded();
124 let wake = WakeHandle::default();
125 let instance_id = InstanceId::new();
126 let instance_generation = 1;
127 let dispatcher = Dispatcher {
128 sender,
129 wake,
130 token: DispatchToken::new(instance_id, instance_generation),
131 };
132 let services = RuntimeServices::new(dispatcher);
133 Self {
134 widget_name: widget_name.into(),
135 widget_id: WidgetId::new(),
136 instance_id,
137 instance_generation,
138 widget,
139 state: None,
140 renderer,
141 receiver,
142 services,
143 surface_size: Size::new(320.0, 120.0),
144 initialized: false,
145 shut_down: false,
146 }
147 }
148
149 pub fn widget_name(&self) -> &str {
150 &self.widget_name
151 }
152
153 pub fn surface_size(&self) -> Size {
154 self.surface_size
155 }
156
157 pub fn set_surface_size(&mut self, size: Size) {
158 if !size.is_empty() {
159 self.surface_size = size;
160 self.request_render();
161 }
162 }
163
164 pub fn attach_waker<F>(&mut self, wake: F)
165 where
166 F: Fn() + Send + Sync + 'static,
167 {
168 self.services.dispatcher.wake.set(wake);
169 }
170
171 pub fn initialize(&mut self, surface_size: Size) -> Result<()> {
172 if self.shut_down {
173 return Err(Error::message(
174 "widgetkit v0.1 AppRunner supports a single widget instance lifetime",
175 ));
176 }
177 if self.initialized {
178 return Ok(());
179 }
180 if !surface_size.is_empty() {
181 self.surface_size = surface_size;
182 }
183
184 let mut mount_ctx = MountCtx::new(self.widget_id, self.instance_id);
185 let state = self.widget.mount(&mut mount_ctx);
186 self.state = Some(state);
187 self.with_state_mut(|widget, state, services, widget_id, instance_id| {
188 let mut ctx = StartCtx::new(widget_id, instance_id, std::ptr::NonNull::from(services));
189 widget.start(state, &mut ctx);
190 });
191 self.initialized = true;
192 self.request_render();
193 self.process_pending()
194 }
195
196 pub fn process_pending(&mut self) -> Result<()> {
197 loop {
198 match self.receiver.try_recv() {
199 Ok(RuntimeEvent::Message(envelope)) => {
200 if self.accepts_token(envelope.token) {
201 self.dispatch_event(Event::Message(envelope.message));
202 }
203 }
204 Ok(RuntimeEvent::TaskFinished { token, task_id }) => {
205 if self.matches_token(token) {
206 self.services.tasks.reap(task_id);
207 }
208 }
209 Ok(RuntimeEvent::TimerFinished { token, timer_id }) => {
210 if self.matches_token(token) {
211 self.services.scheduler.reap(timer_id);
212 }
213 }
214 Err(TryRecvError::Empty) => break,
215 Err(TryRecvError::Disconnected) => break,
216 }
217 }
218 Ok(())
219 }
220
221 pub fn handle_host_event(&mut self, event: HostEvent) -> Result<()> {
222 if let HostEvent::Resized(size) = event.clone() {
223 self.set_surface_size(size);
224 }
225 self.dispatch_event(Event::Host(event));
226 self.process_pending()
227 }
228
229 pub fn needs_redraw(&self) -> bool {
230 self.services.render_requested
231 }
232
233 pub fn render(&mut self, surface: &mut dyn RenderSurface) -> Result<()> {
234 let (width, height) = surface.size();
235 self.surface_size = Size::new(width as f32, height as f32);
236 if let Some(state) = self.state.as_ref() {
237 let mut canvas = Canvas::new(self.surface_size);
238 let ctx = RenderCtx::new(self.widget_id, self.instance_id, self.surface_size);
239 self.widget.render(state, &mut canvas, &ctx);
240 self.renderer.render_canvas(canvas, surface)?;
241 self.services.render_requested = false;
242 }
243 Ok(())
244 }
245
246 pub fn shutdown(&mut self) -> Result<()> {
247 if self.shut_down {
248 return Ok(());
249 }
250 self.with_state_mut(|widget, state, services, widget_id, instance_id| {
251 let mut ctx = StopCtx::new(widget_id, instance_id, std::ptr::NonNull::from(services));
252 widget.stop(state, &mut ctx);
253 });
254 self.services.scheduler.shutdown();
255 self.services.tasks.shutdown();
256 if let Some(state) = self.state.take() {
257 let mut ctx = DisposeCtx::new(self.widget_id, self.instance_id);
258 self.widget.dispose(state, &mut ctx);
259 }
260 self.instance_generation += 1;
261 self.initialized = false;
262 self.shut_down = true;
263 Ok(())
264 }
265
266 fn request_render(&mut self) {
267 self.services.render_requested = true;
268 self.services.dispatcher.wake.wake();
269 }
270
271 fn dispatch_event(&mut self, event: Event<W::Message>) {
272 self.with_state_mut(|widget, state, services, widget_id, instance_id| {
273 let mut ctx = UpdateCtx::new(widget_id, instance_id, std::ptr::NonNull::from(services));
274 widget.update(state, event, &mut ctx);
275 });
276 }
277
278 fn accepts_token(&self, token: DispatchToken) -> bool {
279 self.initialized && !self.shut_down && self.matches_token(token)
280 }
281
282 fn matches_token(&self, token: DispatchToken) -> bool {
283 token == self.current_dispatch_token()
284 }
285
286 fn current_dispatch_token(&self) -> DispatchToken {
287 DispatchToken::new(self.instance_id, self.instance_generation)
288 }
289
290 #[cfg(test)]
291 pub(crate) fn scheduler_active_count(&self) -> usize {
292 self.services.scheduler.active_count()
293 }
294
295 #[cfg(test)]
296 pub(crate) fn task_active_count(&self) -> usize {
297 self.services.tasks.active_count()
298 }
299
300 #[cfg(test)]
301 pub(crate) fn test_token(&self) -> DispatchToken {
302 self.services.dispatcher.token
303 }
304
305 #[cfg(test)]
306 pub(crate) fn dispatch_test_message(&self, token: DispatchToken, message: W::Message) {
307 let _ = self
308 .services
309 .dispatcher
310 .sender
311 .send(RuntimeEvent::Message(crate::internal::MessageEnvelope { token, message }));
312 }
313
314 fn with_state_mut(
315 &mut self,
316 f: impl FnOnce(&mut W, &mut W::State, &mut RuntimeServices<W::Message>, WidgetId, InstanceId),
317 ) {
318 if let Some(state) = self.state.as_mut() {
319 f(&mut self.widget, state, &mut self.services, self.widget_id, self.instance_id);
320 }
321 }
322}
323
324impl<W, R> Drop for AppRunner<W, R>
325where
326 W: Widget,
327 R: Renderer,
328{
329 fn drop(&mut self) {
330 let _ = self.shutdown();
331 }
332}