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::sync::Arc;
8
9/// A connection to the state of a shell.
10///
11/// A [`Widget`] can leverage a [`Shell`] to trigger changes in an application,
12/// like publishing messages or invalidating the current layout.
13///
14/// [`Widget`]: crate::Widget
15#[derive(Debug)]
16pub struct Shell<'a, Message> {
17    window: &'a dyn Window,
18    messages: &'a mut Vec<Message>,
19    waker: Waker,
20    event_status: event::Status,
21    redraw_request: window::RedrawRequest,
22    input_method: InputMethod,
23    is_layout_invalid: Option<Diff>,
24    are_widgets_invalid: bool,
25    clipboard: Clipboard,
26}
27
28impl<'a, Message> Shell<'a, Message> {
29    /// Creates a new [`Shell`] with the provided buffer of messages.
30    pub fn new(window: &'a dyn Window, waker: Waker, messages: &'a mut Vec<Message>) -> Self {
31        Self {
32            window,
33            messages,
34            waker,
35            event_status: event::Status::Ignored,
36            redraw_request: window::RedrawRequest::Wait,
37            is_layout_invalid: None,
38            are_widgets_invalid: false,
39            input_method: InputMethod::Disabled,
40            clipboard: Clipboard {
41                reads: Vec::new(),
42                write: None,
43            },
44        }
45    }
46
47    /// Creates a new [`Shell`] from the current one with the given list of local messages.
48    pub fn local<'b, A>(&self, messages: &'b mut Vec<A>) -> Shell<'b, A>
49    where
50        'a: 'b,
51    {
52        Shell::new(self.window, self.waker.clone(), messages)
53    }
54
55    /// Returns the [`Window`] of the [`Shell`].
56    pub fn window(&self) -> &'a dyn Window {
57        self.window
58    }
59
60    /// Returns the [`Waker`] of the [`Shell`].
61    pub fn waker(&self) -> &Waker {
62        &self.waker
63    }
64
65    /// Returns true if the [`Shell`] contains no published messages
66    #[must_use]
67    pub fn is_empty(&self) -> bool {
68        self.messages.is_empty()
69    }
70
71    /// Publish the given `Message` for an application to process it.
72    pub fn publish(&mut self, message: Message) {
73        self.messages.push(message);
74    }
75
76    /// Marks the current event as captured. Prevents "event bubbling".
77    ///
78    /// A widget should capture an event when no ancestor should
79    /// handle it.
80    pub fn capture_event(&mut self) {
81        self.event_status = event::Status::Captured;
82    }
83
84    /// Returns the current [`event::Status`] of the [`Shell`].
85    #[must_use]
86    pub fn event_status(&self) -> event::Status {
87        self.event_status
88    }
89
90    /// Returns whether the current event has been captured.
91    #[must_use]
92    pub fn is_event_captured(&self) -> bool {
93        self.event_status == event::Status::Captured
94    }
95
96    /// Requests a new frame to be drawn as soon as possible.
97    pub fn request_redraw(&mut self) {
98        self.redraw_request = window::RedrawRequest::NextFrame;
99    }
100
101    /// Requests a new frame to be drawn at the given [`window::RedrawRequest`].
102    pub fn request_redraw_at(&mut self, redraw_request: impl Into<window::RedrawRequest>) {
103        self.redraw_request = self.redraw_request.min(redraw_request.into());
104    }
105
106    /// Returns the request a redraw should happen, if any.
107    #[must_use]
108    pub fn redraw_request(&self) -> window::RedrawRequest {
109        self.redraw_request
110    }
111
112    /// Replaces the redraw request of the [`Shell`]; without conflict resolution.
113    ///
114    /// This is useful if you want to overwrite the redraw request to a previous value.
115    /// Since it's a fairly advanced use case and should rarely be used, it is a static
116    /// method.
117    pub fn replace_redraw_request(shell: &mut Self, redraw_request: window::RedrawRequest) {
118        shell.redraw_request = redraw_request;
119    }
120
121    /// Requests the runtime to read the clipboard contents expecting the given [`clipboard::Kind`].
122    ///
123    /// The runtime will produce a [`clipboard::Event::Read`] when the contents have been read.
124    pub fn read_clipboard(&mut self, kind: clipboard::Kind) {
125        self.clipboard.reads.push(kind);
126    }
127
128    /// Requests the runtime to write the given [`clipboard::Content`] to the clipboard.
129    ///
130    /// The runtime will produce a [`clipboard::Event::Written`] when the contents have been written.
131    pub fn write_clipboard(&mut self, content: clipboard::Content) {
132        self.clipboard.write = Some(content);
133    }
134
135    /// Returns the [`Clipboard`] requests of the [`Shell`], mutably.
136    pub fn clipboard_mut(&mut self) -> &mut Clipboard {
137        &mut self.clipboard
138    }
139
140    /// Requests the current [`InputMethod`] strategy.
141    ///
142    /// __Important__: This request will only be honored by the
143    /// [`Shell`] only during a [`window::Event::RedrawRequested`].
144    pub fn request_input_method<T: AsRef<str>>(&mut self, ime: &InputMethod<T>) {
145        self.input_method.merge(ime);
146    }
147
148    /// Returns the current [`InputMethod`] strategy.
149    #[must_use]
150    pub fn input_method(&self) -> &InputMethod {
151        &self.input_method
152    }
153
154    /// Returns the current [`InputMethod`] strategy.
155    #[must_use]
156    pub fn input_method_mut(&mut self) -> &mut InputMethod {
157        &mut self.input_method
158    }
159
160    /// Returns whether the current layout is invalid or not.
161    #[must_use]
162    pub fn is_layout_invalid(&self) -> Option<Diff> {
163        self.is_layout_invalid
164    }
165
166    /// Invalidates the current application layout.
167    ///
168    /// The shell will relayout the application widgets.
169    pub fn invalidate_layout(&mut self) {
170        self.invalidate_layout_with(Diff::Skip);
171    }
172
173    /// Invalidates the current application layout with the following [`Diff`] strategy.
174    pub fn invalidate_layout_with(&mut self, diff: Diff) {
175        self.is_layout_invalid = Some(diff);
176    }
177
178    /// Triggers the given function if the layout is invalid, cleaning it in the
179    /// process.
180    pub fn revalidate_layout(&mut self, f: impl FnOnce(Diff)) {
181        if let Some(diff) = self.is_layout_invalid.take() {
182            f(diff);
183        }
184    }
185
186    /// Returns whether the widgets of the current application have been
187    /// invalidated.
188    #[must_use]
189    pub fn are_widgets_invalid(&self) -> bool {
190        self.are_widgets_invalid
191    }
192
193    /// Invalidates the current application widgets.
194    ///
195    /// The shell will rebuild and relayout the widget tree.
196    pub fn invalidate_widgets(&mut self) {
197        self.are_widgets_invalid = true;
198    }
199
200    /// Merges the current [`Shell`] with another one by applying the given
201    /// function to the messages of the latter.
202    ///
203    /// This method is useful for composition.
204    pub fn merge<B>(&mut self, mut other: Shell<'_, B>, f: impl Fn(B) -> Message) {
205        self.messages.extend(other.messages.drain(..).map(f));
206
207        self.is_layout_invalid = match (self.is_layout_invalid, other.is_layout_invalid) {
208            (Some(a), Some(b)) => Some(a.max(b)),
209            _ => self.is_layout_invalid.or(other.is_layout_invalid),
210        };
211
212        self.are_widgets_invalid = self.are_widgets_invalid || other.are_widgets_invalid;
213        self.redraw_request = self.redraw_request.min(other.redraw_request);
214        self.event_status = self.event_status.merge(other.event_status);
215
216        self.input_method.merge(&other.input_method);
217        self.clipboard.merge(&mut other.clipboard);
218    }
219}
220
221/// A waker can be used to wake up the iced runtime and, consequently, trigger
222/// wake events concurrently from widget logic.
223#[derive(Clone)]
224pub struct Waker {
225    wake: Arc<dyn Fn() + Send + Sync + 'static>,
226}
227
228impl Waker {
229    /// Creates a new [`Waker`] with the given `wake` function.
230    pub fn new(wake: impl Fn() + Send + Sync + 'static) -> Self {
231        Self {
232            wake: Arc::new(wake),
233        }
234    }
235
236    /// Creates a new [`Waker`] that does nothing.
237    pub fn noop() -> Self {
238        Self::new(|| {})
239    }
240
241    /// Wakes up the iced runtime as soon as possible.
242    ///
243    /// You normally want to call this concurrently (e.g. from a different thread).
244    pub fn wake(&self) {
245        (self.wake)();
246    }
247}
248
249impl std::fmt::Debug for Waker {
250    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251        f.debug_struct("Waker").finish()
252    }
253}
254
255/// The diffing strategy to follow when invalidating some layout.
256#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
257pub enum Diff {
258    /// Skips the diffing step.
259    Skip,
260    /// Performs diffing again before layouting.
261    Perform,
262}