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