cosmic/widget/rectangle_tracker/
mod.rs

1mod subscription;
2
3use iced::Vector;
4use iced::futures::channel::mpsc::UnboundedSender;
5use iced::widget::Container;
6pub use subscription::*;
7
8use iced_core::event::{self, Event};
9use iced_core::layout;
10use iced_core::mouse;
11use iced_core::overlay;
12use iced_core::renderer;
13use iced_core::widget::Tree;
14use iced_core::{Alignment, Clipboard, Element, Layout, Length, Padding, Rectangle, Shell, Widget};
15use std::{fmt::Debug, hash::Hash};
16
17pub use iced_widget::container::{Catalog, Style};
18
19pub fn rectangle_tracking_container<'a, Message, I, T>(
20    content: T,
21    id: I,
22    tx: UnboundedSender<(I, Rectangle)>,
23) -> RectangleTrackingContainer<'a, Message, crate::Renderer, I>
24where
25    I: Hash + Copy + Send + Sync + Debug + 'a,
26    T: Into<Element<'a, Message, crate::Theme, crate::Renderer>>,
27{
28    RectangleTrackingContainer::new(content, id, tx)
29}
30
31pub fn subscription<
32    I: 'static + Hash + Copy + Send + Sync + Debug,
33    R: 'static + Hash + Copy + Send + Sync + Debug + Eq,
34>(
35    id: I,
36) -> iced::Subscription<(I, RectangleUpdate<R>)> {
37    subscription::rectangle_tracker_subscription(id)
38}
39
40#[derive(Clone, Debug)]
41pub struct RectangleTracker<I> {
42    tx: UnboundedSender<(I, Rectangle)>,
43}
44
45impl<I> RectangleTracker<I>
46where
47    I: Hash + Copy + Send + Sync + Debug,
48{
49    pub fn container<'a, Message: 'static, T>(
50        &self,
51        id: I,
52        content: T,
53    ) -> RectangleTrackingContainer<'a, Message, crate::Renderer, I>
54    where
55        I: 'a,
56        T: Into<Element<'a, Message, crate::Theme, crate::Renderer>>,
57    {
58        RectangleTrackingContainer::new(content, id, self.tx.clone())
59    }
60}
61
62/// An element decorating some content.
63///
64/// It is normally used for alignment purposes.
65#[allow(missing_debug_implementations)]
66pub struct RectangleTrackingContainer<'a, Message, Renderer, I>
67where
68    Renderer: iced_core::Renderer,
69{
70    tx: UnboundedSender<(I, Rectangle)>,
71    id: I,
72    container: Container<'a, Message, crate::Theme, Renderer>,
73    ignore_bounds: bool,
74}
75
76impl<'a, Message, Renderer, I> RectangleTrackingContainer<'a, Message, Renderer, I>
77where
78    Renderer: iced_core::Renderer,
79    I: 'a + Hash + Copy + Send + Sync + Debug,
80{
81    /// Creates an empty [`Container`].
82    pub(crate) fn new<T>(content: T, id: I, tx: UnboundedSender<(I, Rectangle)>) -> Self
83    where
84        T: Into<Element<'a, Message, crate::Theme, Renderer>>,
85    {
86        RectangleTrackingContainer {
87            id,
88            tx,
89            container: Container::new(content),
90            ignore_bounds: false,
91        }
92    }
93
94    pub fn diff(&mut self, tree: &mut Tree) {
95        self.container.diff(tree);
96    }
97
98    /// Sets the [`Padding`] of the [`Container`].
99    #[must_use]
100    pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
101        self.container = self.container.padding(padding);
102        self
103    }
104
105    /// Sets the width of the [`self.`].
106    #[must_use]
107    pub fn width(mut self, width: Length) -> Self {
108        self.container = self.container.width(width);
109        self
110    }
111
112    /// Sets the height of the [`Container`].
113    #[must_use]
114    pub fn height(mut self, height: Length) -> Self {
115        self.container = self.container.height(height);
116        self
117    }
118
119    /// Sets the maximum width of the [`Container`].
120    #[must_use]
121    pub fn max_width(mut self, max_width: f32) -> Self {
122        self.container = self.container.max_width(max_width);
123        self
124    }
125
126    /// Sets the maximum height of the [`Container`] in pixels.
127    #[must_use]
128    pub fn max_height(mut self, max_height: f32) -> Self {
129        self.container = self.container.max_height(max_height);
130        self
131    }
132
133    /// Sets the content alignment for the horizontal axis of the [`Container`].
134    #[must_use]
135    pub fn align_x(mut self, alignment: Alignment) -> Self {
136        self.container = self.container.align_x(alignment);
137        self
138    }
139
140    /// Sets the content alignment for the vertical axis of the [`Container`].
141    #[must_use]
142    pub fn align_y(mut self, alignment: Alignment) -> Self {
143        self.container = self.container.align_y(alignment);
144        self
145    }
146
147    /// Centers the contents in the horizontal axis of the [`Container`].
148    #[must_use]
149    pub fn center_x(mut self, width: Length) -> Self {
150        self.container = self.container.center_x(width);
151        self
152    }
153
154    /// Centers the contents in the vertical axis of the [`Container`].
155    #[must_use]
156    pub fn center_y(mut self, height: Length) -> Self {
157        self.container = self.container.center_y(height);
158        self
159    }
160
161    /// Centers the contents in the horizontal and vertical axis of the [`Container`].
162    #[must_use]
163    pub fn center(mut self, length: Length) -> Self {
164        self.container = self.container.center(length);
165        self
166    }
167
168    /// Sets the style of the [`Container`].
169    #[must_use]
170    pub fn style(mut self, style: impl Into<<crate::Theme as Catalog>::Class<'a>>) -> Self {
171        self.container = self.container.class(style);
172        self
173    }
174
175    /// Set to true to ignore parent container bounds when performing layout.
176    /// This can be useful for widgets that are in auto-sized surfaces.
177    #[must_use]
178    pub fn ignore_bounds(mut self, ignore_bounds: bool) -> Self {
179        self.ignore_bounds = ignore_bounds;
180        self
181    }
182}
183
184impl<'a, Message, Renderer, I> Widget<Message, crate::Theme, Renderer>
185    for RectangleTrackingContainer<'a, Message, Renderer, I>
186where
187    Renderer: iced_core::Renderer,
188    I: 'a + Hash + Copy + Send + Sync + Debug,
189{
190    fn children(&self) -> Vec<Tree> {
191        self.container.children()
192    }
193
194    fn state(&self) -> iced_core::widget::tree::State {
195        self.container.state()
196    }
197
198    fn diff(&mut self, tree: &mut Tree) {
199        self.container.diff(tree);
200    }
201
202    fn size(&self) -> iced_core::Size<Length> {
203        self.container.size()
204    }
205
206    fn layout(
207        &self,
208        tree: &mut Tree,
209        renderer: &Renderer,
210        limits: &layout::Limits,
211    ) -> layout::Node {
212        self.container.layout(
213            tree,
214            renderer,
215            if self.ignore_bounds {
216                &layout::Limits::NONE
217            } else {
218                limits
219            },
220        )
221    }
222
223    fn operate(
224        &self,
225        tree: &mut Tree,
226        layout: Layout<'_>,
227        renderer: &Renderer,
228        operation: &mut dyn iced_core::widget::Operation<()>,
229    ) {
230        self.container.operate(tree, layout, renderer, operation);
231    }
232
233    fn on_event(
234        &mut self,
235        tree: &mut Tree,
236        event: Event,
237        layout: Layout<'_>,
238        cursor_position: mouse::Cursor,
239        renderer: &Renderer,
240        clipboard: &mut dyn Clipboard,
241        shell: &mut Shell<'_, Message>,
242        viewport: &iced_core::Rectangle,
243    ) -> event::Status {
244        self.container.on_event(
245            tree,
246            event,
247            layout,
248            cursor_position,
249            renderer,
250            clipboard,
251            shell,
252            viewport,
253        )
254    }
255
256    fn mouse_interaction(
257        &self,
258        tree: &Tree,
259        layout: Layout<'_>,
260        cursor_position: mouse::Cursor,
261        viewport: &Rectangle,
262        renderer: &Renderer,
263    ) -> mouse::Interaction {
264        self.container
265            .mouse_interaction(tree, layout, cursor_position, viewport, renderer)
266    }
267
268    fn draw(
269        &self,
270        tree: &Tree,
271        renderer: &mut Renderer,
272        theme: &crate::Theme,
273        renderer_style: &renderer::Style,
274        layout: Layout<'_>,
275        cursor_position: mouse::Cursor,
276        viewport: &Rectangle,
277    ) {
278        let _ = self.tx.unbounded_send((self.id, layout.bounds()));
279        self.container.draw(
280            tree,
281            renderer,
282            theme,
283            renderer_style,
284            layout,
285            cursor_position,
286            viewport,
287        );
288    }
289
290    fn overlay<'b>(
291        &'b mut self,
292        tree: &'b mut Tree,
293        layout: Layout<'_>,
294        renderer: &Renderer,
295        translation: Vector,
296    ) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
297        self.container.overlay(tree, layout, renderer, translation)
298    }
299
300    fn drag_destinations(
301        &self,
302        state: &Tree,
303        layout: Layout<'_>,
304        renderer: &Renderer,
305        dnd_rectangles: &mut iced_core::clipboard::DndDestinationRectangles,
306    ) {
307        self.container
308            .drag_destinations(state, layout, renderer, dnd_rectangles);
309    }
310
311    #[cfg(feature = "a11y")]
312    /// get the a11y nodes for the widget
313    fn a11y_nodes(
314        &self,
315        layout: Layout<'_>,
316        state: &Tree,
317        p: mouse::Cursor,
318    ) -> iced_accessibility::A11yTree {
319        self.container.a11y_nodes(layout, state, p)
320    }
321}
322
323impl<'a, Message, Renderer, I> From<RectangleTrackingContainer<'a, Message, Renderer, I>>
324    for Element<'a, Message, crate::Theme, Renderer>
325where
326    Message: 'a,
327    Renderer: 'a + iced_core::Renderer,
328    I: 'a + Hash + Copy + Send + Sync + Debug,
329{
330    fn from(
331        column: RectangleTrackingContainer<'a, Message, Renderer, I>,
332    ) -> Element<'a, Message, crate::Theme, Renderer> {
333        Element::new(column)
334    }
335}