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(
72        &mut self,
73        redraw_request: impl Into<window::RedrawRequest>,
74    ) {
75        self.redraw_request = self.redraw_request.min(redraw_request.into());
76    }
77
78    /// Returns the request a redraw should happen, if any.
79    #[must_use]
80    pub fn redraw_request(&self) -> window::RedrawRequest {
81        self.redraw_request
82    }
83
84    /// Replaces the redraw request of the [`Shell`]; without conflict resolution.
85    ///
86    /// This is useful if you want to overwrite the redraw request to a previous value.
87    /// Since it's a fairly advanced use case and should rarely be used, it is a static
88    /// method.
89    pub fn replace_redraw_request(
90        shell: &mut Self,
91        redraw_request: window::RedrawRequest,
92    ) {
93        shell.redraw_request = redraw_request;
94    }
95
96    /// Requests the current [`InputMethod`] strategy.
97    ///
98    /// __Important__: This request will only be honored by the
99    /// [`Shell`] only during a [`window::Event::RedrawRequested`].
100    pub fn request_input_method<T: AsRef<str>>(
101        &mut self,
102        ime: &InputMethod<T>,
103    ) {
104        self.input_method.merge(ime);
105    }
106
107    /// Returns the current [`InputMethod`] strategy.
108    #[must_use]
109    pub fn input_method(&self) -> &InputMethod {
110        &self.input_method
111    }
112
113    /// Returns the current [`InputMethod`] strategy.
114    #[must_use]
115    pub fn input_method_mut(&mut self) -> &mut InputMethod {
116        &mut self.input_method
117    }
118
119    /// Returns whether the current layout is invalid or not.
120    #[must_use]
121    pub fn is_layout_invalid(&self) -> bool {
122        self.is_layout_invalid
123    }
124
125    /// Invalidates the current application layout.
126    ///
127    /// The shell will relayout the application widgets.
128    pub fn invalidate_layout(&mut self) {
129        self.is_layout_invalid = true;
130    }
131
132    /// Triggers the given function if the layout is invalid, cleaning it in the
133    /// process.
134    pub fn revalidate_layout(&mut self, f: impl FnOnce()) {
135        if self.is_layout_invalid {
136            self.is_layout_invalid = false;
137
138            f();
139        }
140    }
141
142    /// Returns whether the widgets of the current application have been
143    /// invalidated.
144    #[must_use]
145    pub fn are_widgets_invalid(&self) -> bool {
146        self.are_widgets_invalid
147    }
148
149    /// Invalidates the current application widgets.
150    ///
151    /// The shell will rebuild and relayout the widget tree.
152    pub fn invalidate_widgets(&mut self) {
153        self.are_widgets_invalid = true;
154    }
155
156    /// Merges the current [`Shell`] with another one by applying the given
157    /// function to the messages of the latter.
158    ///
159    /// This method is useful for composition.
160    pub fn merge<B>(&mut self, other: Shell<'_, B>, f: impl Fn(B) -> Message) {
161        self.messages.extend(other.messages.drain(..).map(f));
162
163        self.is_layout_invalid =
164            self.is_layout_invalid || other.is_layout_invalid;
165
166        self.are_widgets_invalid =
167            self.are_widgets_invalid || other.are_widgets_invalid;
168
169        self.redraw_request = self.redraw_request.min(other.redraw_request);
170        self.event_status = self.event_status.merge(other.event_status);
171        self.input_method.merge(&other.input_method);
172    }
173}