Skip to main content

iced_core/
shell.rs

1//! Communicate with the iced runtime from widgets.
2use crate::clipboard;
3use crate::event;
4use crate::window;
5use crate::{Clipboard, InputMethod, Window};
6
7use std::rc::{self, Rc};
8use std::sync::Arc;
9use std::vec;
10
11/// A connection to the state of a shell.
12///
13/// A [`Widget`] can leverage a [`Shell`] to trigger changes in an application,
14/// like publishing messages or invalidating the current layout.
15///
16/// [`Widget`]: crate::Widget
17#[derive(Debug)]
18pub struct Shell<'a, Message> {
19    window: &'a dyn Window,
20    bus: &'a mut Bus<Message>,
21    waker: Waker,
22    event_status: event::Status,
23    redraw_request: window::RedrawRequest,
24    input_method: InputMethod,
25    invalidation: Invalidation,
26    clipboard: Clipboard,
27}
28
29/// The most severe invalidation requested by a [`Shell`] while
30/// processing an event.
31///
32/// Variants are ordered by severity and [`Shell::invalidate`]
33/// accumulates the maximum: a higher variant subsumes the lower ones.
34#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
35pub enum Invalidation {
36    /// Nothing needs to be rebuilt.
37    None,
38    /// The application overlays need to be recreated.
39    Overlay,
40    /// The application layout needs to be recomputed, following the
41    /// given [`Diff`] strategy.
42    Layout(Diff),
43    /// The entire application widget tree needs to be rebuilt and
44    /// relaid out.
45    Widgets,
46}
47
48impl<'a, Message> Shell<'a, Message> {
49    /// Creates a new [`Shell`] with the provided buffer of messages.
50    pub fn new(window: &'a dyn Window, waker: Waker, bus: &'a mut Bus<Message>) -> Self {
51        Self {
52            window,
53            bus,
54            waker,
55            event_status: event::Status::Ignored,
56            redraw_request: window::RedrawRequest::Wait,
57            invalidation: Invalidation::None,
58            input_method: InputMethod::Disabled,
59            clipboard: Clipboard {
60                reads: Vec::new(),
61                write: None,
62            },
63        }
64    }
65
66    /// Creates a new [`Shell`] from the current one with the given list of local messages.
67    pub fn local<'b, A>(&self, bus: &'b mut Bus<A>) -> Shell<'b, A>
68    where
69        'a: 'b,
70    {
71        Shell::new(self.window, self.waker.clone(), bus)
72    }
73
74    /// Returns the [`Window`] of the [`Shell`].
75    pub fn window(&self) -> &'a dyn Window {
76        self.window
77    }
78
79    /// Returns the [`Waker`] of the [`Shell`].
80    pub fn waker(&self) -> &Waker {
81        &self.waker
82    }
83
84    /// Returns true if the [`Shell`] contains no published messages
85    #[must_use]
86    pub fn is_empty(&self) -> bool {
87        self.bus.messages.is_empty()
88    }
89
90    /// Publish the given `Message` for an application to process it.
91    pub fn publish(&mut self, message: Message) {
92        let _ = self.publish_and_track(message);
93    }
94
95    /// Publish the given `Message` for an application to process it.
96    ///
97    /// The returned [`Tracking`] can be used to determine if the message
98    /// was processed.
99    pub fn publish_and_track(&mut self, message: Message) -> Tracking {
100        self.bus.push(message)
101    }
102
103    /// Forwards the given `Message` and fulfills the given [`Receipt`]
104    /// once processed.
105    pub fn forward(&mut self, message: Message, receipt: Receipt) {
106        self.bus.forward(message, receipt);
107    }
108
109    /// Marks the current event as captured. Prevents "event bubbling".
110    ///
111    /// A widget should capture an event when no ancestor should
112    /// handle it.
113    pub fn capture_event(&mut self) {
114        self.event_status = event::Status::Captured;
115    }
116
117    /// Returns the current [`event::Status`] of the [`Shell`].
118    #[must_use]
119    pub fn event_status(&self) -> event::Status {
120        self.event_status
121    }
122
123    /// Returns whether the current event has been captured.
124    #[must_use]
125    pub fn is_event_captured(&self) -> bool {
126        self.event_status == event::Status::Captured
127    }
128
129    /// Requests a new frame to be drawn as soon as possible.
130    pub fn request_redraw(&mut self) {
131        self.redraw_request = window::RedrawRequest::NextFrame;
132    }
133
134    /// Requests a new frame to be drawn at the given [`window::RedrawRequest`].
135    pub fn request_redraw_at(&mut self, redraw_request: impl Into<window::RedrawRequest>) {
136        self.redraw_request = self.redraw_request.min(redraw_request.into());
137    }
138
139    /// Returns the request a redraw should happen, if any.
140    #[must_use]
141    pub fn redraw_request(&self) -> window::RedrawRequest {
142        self.redraw_request
143    }
144
145    /// Replaces the redraw request of the [`Shell`]; without conflict resolution.
146    ///
147    /// This is useful if you want to overwrite the redraw request to a previous value.
148    /// Since it's a fairly advanced use case and should rarely be used, it is a static
149    /// method.
150    pub fn replace_redraw_request(shell: &mut Self, redraw_request: window::RedrawRequest) {
151        shell.redraw_request = redraw_request;
152    }
153
154    /// Requests the runtime to read the clipboard contents expecting the given [`clipboard::Kind`].
155    ///
156    /// The runtime will produce a [`clipboard::Event::Read`] when the contents have been read.
157    pub fn read_clipboard(&mut self, kind: clipboard::Kind) {
158        self.clipboard.reads.push(kind);
159    }
160
161    /// Requests the runtime to write the given [`clipboard::Content`] to the clipboard.
162    ///
163    /// The runtime will produce a [`clipboard::Event::Written`] when the contents have been written.
164    pub fn write_clipboard(&mut self, content: impl Into<clipboard::Content>) {
165        self.clipboard.write = Some(content.into());
166    }
167
168    /// Returns the [`Clipboard`] requests of the [`Shell`], mutably.
169    pub fn clipboard_mut(&mut self) -> &mut Clipboard {
170        &mut self.clipboard
171    }
172
173    /// Requests the current [`InputMethod`] strategy.
174    ///
175    /// __Important__: This request will only be honored by the
176    /// [`Shell`] only during a [`window::Event::RedrawRequested`].
177    pub fn request_input_method<T: AsRef<str>>(&mut self, ime: &InputMethod<T>) {
178        self.input_method.merge(ime);
179    }
180
181    /// Returns the current [`InputMethod`] strategy.
182    #[must_use]
183    pub fn input_method(&self) -> &InputMethod {
184        &self.input_method
185    }
186
187    /// Returns the current [`InputMethod`] strategy.
188    #[must_use]
189    pub fn input_method_mut(&mut self) -> &mut InputMethod {
190        &mut self.input_method
191    }
192
193    /// Invalidates the current application overlay.
194    ///
195    /// The shell will recreate the application overlays.
196    pub fn invalidate_overlay(&mut self) {
197        self.invalidate(Invalidation::Overlay);
198    }
199
200    /// Invalidates the current application layout.
201    ///
202    /// The shell will relayout the application widgets.
203    pub fn invalidate_layout(&mut self) {
204        self.invalidate_layout_with(Diff::Skip);
205    }
206
207    /// Invalidates the current application layout with the following [`Diff`] strategy.
208    pub fn invalidate_layout_with(&mut self, diff: Diff) {
209        self.invalidate(Invalidation::Layout(diff));
210    }
211
212    /// Invalidates the current application widgets.
213    ///
214    /// The shell will rebuild and relayout the widget tree.
215    pub fn invalidate_widgets(&mut self) {
216        self.invalidate(Invalidation::Widgets);
217    }
218
219    /// Sets the [`Invalidation`] of the [`Shell`].
220    pub fn invalidate(&mut self, invalidation: Invalidation) {
221        self.invalidation = self.invalidation.max(invalidation);
222    }
223
224    /// Returns the [`Invalidation`] of the [`Shell`].
225    pub fn invalidation(&self) -> Invalidation {
226        self.invalidation
227    }
228
229    /// Merges the current [`Shell`] with another one by applying the given
230    /// function to the messages of the latter.
231    ///
232    /// This method is useful for composition.
233    pub fn merge<B>(&mut self, mut other: Shell<'_, B>, f: impl Fn(B) -> Message) {
234        self.bus.messages.extend(
235            other
236                .bus
237                .messages
238                .drain(..)
239                .map(|(message, receipt)| (f(message), receipt)),
240        );
241
242        self.invalidation = self.invalidation.max(other.invalidation);
243        self.redraw_request = self.redraw_request.min(other.redraw_request);
244        self.event_status = self.event_status.merge(other.event_status);
245
246        self.input_method.merge(&other.input_method);
247        self.clipboard.merge(&mut other.clipboard);
248    }
249}
250
251/// A waker can be used to wake up the iced runtime and, consequently, trigger
252/// wake events concurrently from widget logic.
253#[derive(Clone)]
254pub struct Waker {
255    wake: Arc<dyn Fn() + Send + Sync + 'static>,
256}
257
258impl Waker {
259    /// Creates a new [`Waker`] with the given `wake` function.
260    pub fn new(wake: impl Fn() + Send + Sync + 'static) -> Self {
261        Self {
262            wake: Arc::new(wake),
263        }
264    }
265
266    /// Creates a new [`Waker`] that does nothing.
267    pub fn noop() -> Self {
268        Self::new(|| {})
269    }
270
271    /// Wakes up the iced runtime as soon as possible.
272    ///
273    /// You normally want to call this concurrently (e.g. from a different thread).
274    pub fn wake(&self) {
275        (self.wake)();
276    }
277}
278
279impl std::fmt::Debug for Waker {
280    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281        f.debug_struct("Waker").finish()
282    }
283}
284
285/// The diffing strategy to follow when invalidating some layout.
286#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
287pub enum Diff {
288    /// Skips the diffing step.
289    Skip,
290    /// Performs diffing again before layouting.
291    Perform,
292}
293
294/// A channel of messages published by a [`Shell`].
295#[derive(Debug)]
296pub struct Bus<T> {
297    messages: Vec<(T, Receipt)>,
298}
299
300impl<T> Bus<T> {
301    /// Creates an empty [`Bus`].
302    pub fn new() -> Self {
303        Self {
304            messages: Vec::new(),
305        }
306    }
307
308    /// Returns `true` if the [`Bus`] has no messages pending.
309    pub fn is_empty(&self) -> bool {
310        self.messages.is_empty()
311    }
312
313    /// Returns the amount of messages pending in the [`Bus`].
314    pub fn len(&self) -> usize {
315        self.messages.len()
316    }
317
318    /// Pushes a new message to the [`Bus`].
319    ///
320    /// The returned [`Tracking`] can be used to determine if the message
321    /// was processed.
322    pub fn push(&mut self, message: T) -> Tracking {
323        let receipt = Receipt::new();
324        let tracking = receipt.tracking();
325
326        self.messages.push((message, receipt));
327
328        tracking
329    }
330
331    /// Forward a new message to the [`Bus`] with the given [`Receipt`].
332    pub fn forward(&mut self, message: T, receipt: Receipt) {
333        self.messages.push((message, receipt));
334    }
335
336    /// Drains the [`Bus`].
337    pub fn drain(&mut self) -> impl Iterator<Item = (T, Receipt)> {
338        self.messages.drain(..)
339    }
340}
341
342impl<T> Default for Bus<T> {
343    fn default() -> Self {
344        Self::new()
345    }
346}
347
348impl<T> IntoIterator for Bus<T> {
349    type Item = T;
350    type IntoIter = IntoIter<T>;
351
352    fn into_iter(self) -> Self::IntoIter {
353        IntoIter {
354            iter: self.messages.into_iter(),
355        }
356    }
357}
358
359/// An iterator returned by the implementation of [`IntoIterator`] for [`Bus`].
360pub struct IntoIter<T> {
361    iter: vec::IntoIter<(T, Receipt)>,
362}
363
364impl<T> Iterator for IntoIter<T> {
365    type Item = T;
366
367    fn next(&mut self) -> Option<Self::Item> {
368        Some(self.iter.next()?.0)
369    }
370}
371
372/// Proof that a message has been received.
373#[derive(Debug)]
374pub struct Receipt(Rc<()>);
375
376impl Receipt {
377    fn new() -> Self {
378        Self(Rc::new(()))
379    }
380
381    fn tracking(&self) -> Tracking {
382        Tracking(Rc::downgrade(&self.0))
383    }
384}
385
386/// A message tracking returned by [`Shell::publish`].
387#[derive(Debug, Clone)]
388pub struct Tracking(rc::Weak<()>);
389
390impl Tracking {
391    /// Returns `true` if the message of this [`Tracking`] has been processed
392    /// by `update` logic.
393    pub fn is_processed(&self) -> bool {
394        self.0.strong_count() == 0
395    }
396}