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