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