1use iced::{Limits, Size};
4use iced_core::event::{self, Event};
5use iced_core::layout;
6use iced_core::mouse;
7use iced_core::overlay;
8use iced_core::renderer;
9use iced_core::widget::{Id, Operation, Tree, tree};
10use iced_core::{Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget};
11
12pub(crate) fn responsive_container<'a, Message: 'static, Theme, E>(
13 content: E,
14 id: Id,
15 on_action: impl Fn(crate::surface::Action) -> Message + 'static,
16) -> ResponsiveContainer<'a, Message, Theme, crate::Renderer>
17where
18 E: Into<Element<'a, Message, Theme, crate::Renderer>>,
19 Theme: iced_widget::container::Catalog,
20 <Theme as iced_widget::container::Catalog>::Class<'a>: From<crate::theme::Container<'a>>,
21{
22 ResponsiveContainer::new(content, id, on_action)
23}
24
25#[allow(missing_debug_implementations)]
29pub struct ResponsiveContainer<'a, Message, Theme, Renderer>
30where
31 Renderer: iced_core::Renderer,
32{
33 content: Element<'a, Message, Theme, Renderer>,
34 id: Id,
35 size: Option<Size>,
36 on_action: Box<dyn Fn(crate::surface::Action) -> Message>,
37}
38
39impl<'a, Message, Theme, Renderer> ResponsiveContainer<'a, Message, Theme, Renderer>
40where
41 Renderer: iced_core::Renderer,
42{
43 pub(crate) fn new<T>(
45 content: T,
46 id: Id,
47 on_action: impl Fn(crate::surface::Action) -> Message + 'static,
48 ) -> Self
49 where
50 T: Into<Element<'a, Message, Theme, Renderer>>,
51 {
52 ResponsiveContainer {
53 content: content.into(),
54 id,
55 size: None,
56 on_action: Box::new(on_action),
57 }
58 }
59
60 pub(crate) fn size(mut self, size: Size) -> Self {
61 self.size = Some(size);
62 self
63 }
64}
65
66impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
67 for ResponsiveContainer<'_, Message, Theme, Renderer>
68where
69 Renderer: iced_core::Renderer,
70{
71 fn tag(&self) -> tree::Tag {
72 tree::Tag::of::<State>()
73 }
74
75 fn state(&self) -> tree::State {
76 tree::State::new(State::new())
77 }
78
79 fn children(&self) -> Vec<Tree> {
80 vec![Tree::new(&self.content)]
81 }
82
83 fn diff(&mut self, tree: &mut Tree) {
84 tree.diff_children(std::slice::from_mut(&mut self.content));
85 }
86
87 fn size(&self) -> iced_core::Size<Length> {
88 self.content.as_widget().size()
89 }
90
91 fn layout(
92 &mut self,
93 tree: &mut Tree,
94 renderer: &Renderer,
95 limits: &layout::Limits,
96 ) -> layout::Node {
97 let state = tree.state.downcast_mut::<State>();
98 let mut unrestricted_size = self.size.unwrap_or_else(|| {
99 let node =
100 self.content
101 .as_widget_mut()
102 .layout(&mut tree.children[0], renderer, &Limits::NONE);
103 node.size()
104 });
105
106 let cur_unrestricted_size = {
107 let node =
108 self.content
109 .as_widget_mut()
110 .layout(&mut tree.children[0], renderer, &Limits::NONE);
111 node.size()
112 };
113
114 let max_size = limits.max();
115
116 let old_max = state.limits.max();
117
118 state.needs_update = (cur_unrestricted_size.width > max_size.width)
119 || (cur_unrestricted_size.width > old_max.width)
120 || (cur_unrestricted_size.height > max_size.height)
121 || (cur_unrestricted_size.height > old_max.height)
122 || ((unrestricted_size.width <= max_size.width)
123 && (unrestricted_size.height <= max_size.height)
124 && (unrestricted_size.width - cur_unrestricted_size.width > 1.
125 || unrestricted_size.height - cur_unrestricted_size.height > 1.));
126
127 if unrestricted_size.width < cur_unrestricted_size.width {
128 state.needs_update = true;
129 unrestricted_size.width = cur_unrestricted_size.width;
130 } else if unrestricted_size.height < cur_unrestricted_size.height {
131 state.needs_update = true;
132 unrestricted_size.height = cur_unrestricted_size.height;
133 }
134 let node = self
135 .content
136 .as_widget_mut()
137 .layout(&mut tree.children[0], renderer, limits);
138 let size = node.size();
139
140 if state.needs_update {
141 state.limits = *limits;
142 state.size = unrestricted_size;
143 }
144
145 layout::Node::with_children(size, vec![node])
146 }
147
148 fn operate(
149 &mut self,
150 tree: &mut Tree,
151 layout: Layout<'_>,
152 renderer: &Renderer,
153 operation: &mut dyn Operation,
154 ) {
155 operation.container(Some(&self.id), layout.bounds());
156 operation.traverse(&mut |operation| {
157 self.content.as_widget_mut().operate(
158 &mut tree.children[0],
159 layout
160 .children()
161 .next()
162 .unwrap()
163 .with_virtual_offset(layout.virtual_offset()),
164 renderer,
165 operation,
166 );
167 });
168 }
169
170 fn update(
171 &mut self,
172 tree: &mut Tree,
173 event: &Event,
174 layout: Layout<'_>,
175 cursor_position: mouse::Cursor,
176 renderer: &Renderer,
177 clipboard: &mut dyn Clipboard,
178 shell: &mut Shell<'_, Message>,
179 viewport: &Rectangle,
180 ) {
181 let state = tree.state.downcast_mut::<State>();
182
183 if state.needs_update {
184 shell.publish((self.on_action)(
185 crate::surface::Action::ResponsiveMenuBar {
186 menu_bar: self.id.clone(),
187 limits: state.limits,
188 size: state.size,
189 },
190 ));
191 state.needs_update = false;
192 }
193
194 self.content.as_widget_mut().update(
195 &mut tree.children[0],
196 event,
197 layout
198 .children()
199 .next()
200 .unwrap()
201 .with_virtual_offset(layout.virtual_offset()),
202 cursor_position,
203 renderer,
204 clipboard,
205 shell,
206 viewport,
207 )
208 }
209
210 fn mouse_interaction(
211 &self,
212 tree: &Tree,
213 layout: Layout<'_>,
214 cursor_position: mouse::Cursor,
215 viewport: &Rectangle,
216 renderer: &Renderer,
217 ) -> mouse::Interaction {
218 let content_layout = layout.children().next().unwrap();
219 self.content.as_widget().mouse_interaction(
220 &tree.children[0],
221 content_layout.with_virtual_offset(layout.virtual_offset()),
222 cursor_position,
223 viewport,
224 renderer,
225 )
226 }
227
228 fn draw(
229 &self,
230 tree: &Tree,
231 renderer: &mut Renderer,
232 theme: &Theme,
233 renderer_style: &renderer::Style,
234 layout: Layout<'_>,
235 cursor_position: mouse::Cursor,
236 viewport: &Rectangle,
237 ) {
238 let content_layout = layout.children().next().unwrap();
239 self.content.as_widget().draw(
240 &tree.children[0],
241 renderer,
242 theme,
243 renderer_style,
244 content_layout.with_virtual_offset(layout.virtual_offset()),
245 cursor_position,
246 viewport,
247 );
248 }
249
250 fn overlay<'b>(
251 &'b mut self,
252 tree: &'b mut Tree,
253 layout: Layout<'b>,
254 renderer: &Renderer,
255 viewport: &Rectangle,
256 translation: Vector,
257 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
258 self.content.as_widget_mut().overlay(
259 &mut tree.children[0],
260 layout
261 .children()
262 .next()
263 .unwrap()
264 .with_virtual_offset(layout.virtual_offset()),
265 renderer,
266 viewport,
267 translation,
268 )
269 }
270
271 fn drag_destinations(
272 &self,
273 state: &Tree,
274 layout: Layout<'_>,
275 renderer: &Renderer,
276 dnd_rectangles: &mut iced_core::clipboard::DndDestinationRectangles,
277 ) {
278 let content_layout = layout.children().next().unwrap();
279 self.content.as_widget().drag_destinations(
280 &state.children[0],
281 content_layout.with_virtual_offset(layout.virtual_offset()),
282 renderer,
283 dnd_rectangles,
284 );
285 }
286
287 fn id(&self) -> Option<crate::widget::Id> {
288 Some(self.id.clone())
289 }
290
291 fn set_id(&mut self, id: crate::widget::Id) {
292 self.id = id;
293 }
294
295 #[cfg(feature = "a11y")]
296 fn a11y_nodes(
298 &self,
299 layout: Layout<'_>,
300 state: &Tree,
301 p: mouse::Cursor,
302 ) -> iced_accessibility::A11yTree {
303 let c_layout = layout.children().next().unwrap();
304 let c_state = &state.children[0];
305 self.content.as_widget().a11y_nodes(
306 c_layout.with_virtual_offset(layout.virtual_offset()),
307 c_state,
308 p,
309 )
310 }
311}
312
313impl<'a, Message, Theme, Renderer> From<ResponsiveContainer<'a, Message, Theme, Renderer>>
314 for Element<'a, Message, Theme, Renderer>
315where
316 Message: 'a,
317 Renderer: 'a + iced_core::Renderer,
318 Theme: 'a,
319{
320 fn from(
321 c: ResponsiveContainer<'a, Message, Theme, Renderer>,
322 ) -> Element<'a, Message, Theme, Renderer> {
323 Element::new(c)
324 }
325}
326
327#[derive(Debug, Clone, Copy)]
328struct State {
329 limits: Limits,
330 size: Size,
331 needs_update: bool,
332}
333
334impl State {
335 fn new() -> Self {
336 Self {
337 limits: Limits::NONE,
338 size: Size::new(0., 0.),
339 needs_update: false,
340 }
341 }
342}