iced_core/
shell.rs

1use crate::InputMethod;
2use crate::event;
3use crate::window;
4
5/// A connection to the state of a shell.
6///
7/// A [`Widget`] can leverage a [`Shell`] to trigger changes in an application,
8/// like publishing messages or invalidating the current layout.
9///
10/// [`Widget`]: crate::Widget
11#[derive(Debug)]
12pub struct Shell<'a, Message> {
13    messages: &'a mut Vec<Message>,
14    event_status: event::Status,
15    redraw_request: window::RedrawRequest,
16    input_method: InputMethod,
17    is_layout_invalid: bool,
18    are_widgets_invalid: bool,
19}
20
21impl<'a, Message> Shell<'a, Message> {
22    /// Creates a new [`Shell`] with the provided buffer of messages.
23    pub fn new(messages: &'a mut Vec<Message>) -> Self {
24        Self {
25            messages,
26            event_status: event::Status::Ignored,
27            redraw_request: window::RedrawRequest::Wait,
28            is_layout_invalid: false,
29            are_widgets_invalid: false,
30            input_method: InputMethod::Disabled,
31        }
32    }
33
34    /// Returns true if the [`Shell`] contains no published messages
35    #[must_use]
36    pub fn is_empty(&self) -> bool {
37        self.messages.is_empty()
38    }
39
40    /// Publish the given `Message` for an application to process it.
41    pub fn publish(&mut self, message: Message) {
42        self.messages.push(message);
43    }
44
45    /// Marks the current event as captured. Prevents "event bubbling".
46    ///
47    /// A widget should capture an event when no ancestor should
48    /// handle it.
49    pub fn capture_event(&mut self) {
50        self.event_status = event::Status::Captured;
51    }
52
53    /// Returns the current [`event::Status`] of the [`Shell`].
54    #[must_use]
55    pub fn event_status(&self) -> event::Status {
56        self.event_status
57    }
58
59    /// Returns whether the current event has been captured.
60    #[must_use]
61    pub fn is_event_captured(&self) -> bool {
62        self.event_status == event::Status::Captured
63    }
64
65    /// Requests a new frame to be drawn as soon as possible.
66    pub fn request_redraw(&mut self) {
67        self.redraw_request = window::RedrawRequest::NextFrame;
68    }
69
70    /// Requests a new frame to be drawn at the given [`window::RedrawRequest`].
71    pub fn request_redraw_at(&mut self, redraw_request: impl Into<window::RedrawRequest>) {
72        self.redraw_request = self.redraw_request.min(redraw_request.into());
73    }
74
75    /// Returns the request a redraw should happen, if any.
76    #[must_use]
77    pub fn redraw_request(&self) -> window::RedrawRequest {
78        self.redraw_request
79    }
80
81    /// Replaces the redraw request of the [`Shell`]; without conflict resolution.
82    ///
83    /// This is useful if you want to overwrite the redraw request to a previous value.
84    /// Since it's a fairly advanced use case and should rarely be used, it is a static
85    /// method.
86    pub fn replace_redraw_request(shell: &mut Self, redraw_request: window::RedrawRequest) {
87        shell.redraw_request = redraw_request;
88    }
89
90    /// Requests the current [`InputMethod`] strategy.
91    ///
92    /// __Important__: This request will only be honored by the
93    /// [`Shell`] only during a [`window::Event::RedrawRequested`].
94    pub fn request_input_method<T: AsRef<str>>(&mut self, ime: &InputMethod<T>) {
95        self.input_method.merge(ime);
96    }
97
98    /// Returns the current [`InputMethod`] strategy.
99    #[must_use]
100    pub fn input_method(&self) -> &InputMethod {
101        &self.input_method
102    }
103
104    /// Returns the current [`InputMethod`] strategy.
105    #[must_use]
106    pub fn input_method_mut(&mut self) -> &mut InputMethod {
107        &mut self.input_method
108    }
109
110    /// Returns whether the current layout is invalid or not.
111    #[must_use]
112    pub fn is_layout_invalid(&self) -> bool {
113        self.is_layout_invalid
114    }
115
116    /// Invalidates the current application layout.
117    ///
118    /// The shell will relayout the application widgets.
119    pub fn invalidate_layout(&mut self) {
120        self.is_layout_invalid = true;
121    }
122
123    /// Triggers the given function if the layout is invalid, cleaning it in the
124    /// process.
125    pub fn revalidate_layout(&mut self, f: impl FnOnce()) {
126        if self.is_layout_invalid {
127            self.is_layout_invalid = false;
128
129            f();
130        }
131    }
132
133    /// Returns whether the widgets of the current application have been
134    /// invalidated.
135    #[must_use]
136    pub fn are_widgets_invalid(&self) -> bool {
137        self.are_widgets_invalid
138    }
139
140    /// Invalidates the current application widgets.
141    ///
142    /// The shell will rebuild and relayout the widget tree.
143    pub fn invalidate_widgets(&mut self) {
144        self.are_widgets_invalid = true;
145    }
146
147    /// Merges the current [`Shell`] with another one by applying the given
148    /// function to the messages of the latter.
149    ///
150    /// This method is useful for composition.
151    pub fn merge<B>(&mut self, other: Shell<'_, B>, f: impl Fn(B) -> Message) {
152        self.messages.extend(other.messages.drain(..).map(f));
153
154        self.is_layout_invalid = self.is_layout_invalid || other.is_layout_invalid;
155
156        self.are_widgets_invalid = self.are_widgets_invalid || other.are_widgets_invalid;
157
158        self.redraw_request = self.redraw_request.min(other.redraw_request);
159        self.event_status = self.event_status.merge(other.event_status);
160        self.input_method.merge(&other.input_method);
161    }
162}