Skip to main content

iced_widget/
pin.rs

1//! A pin widget positions a widget at some fixed coordinates inside its boundaries.
2//!
3//! # Example
4//! ```no_run
5//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::core::Length::Fill; }
6//! # pub type State = ();
7//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
8//! use iced::widget::pin;
9//! use iced::Fill;
10//!
11//! enum Message {
12//!     // ...
13//! }
14//!
15//! fn view(state: &State) -> Element<'_, Message> {
16//!     pin("This text is displayed at coordinates (50, 50)!")
17//!         .x(50)
18//!         .y(50)
19//!         .into()
20//! }
21//! ```
22use crate::core::layout;
23use crate::core::mouse;
24use crate::core::overlay;
25use crate::core::renderer;
26use crate::core::widget;
27use crate::core::{
28    self, Element, Event, Layout, Length, Pixels, Point, Rectangle, Shell, Size, Vector, Widget,
29};
30
31/// A widget that positions its contents at some fixed coordinates inside of its boundaries.
32///
33/// By default, a [`Pin`] widget will try to fill its parent.
34///
35/// # Example
36/// ```no_run
37/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::core::Length::Fill; }
38/// # pub type State = ();
39/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
40/// use iced::widget::pin;
41/// use iced::Fill;
42///
43/// enum Message {
44///     // ...
45/// }
46///
47/// fn view(state: &State) -> Element<'_, Message> {
48///     pin("This text is displayed at coordinates (50, 50)!")
49///         .x(50)
50///         .y(50)
51///         .into()
52/// }
53/// ```
54pub struct Pin<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>
55where
56    Renderer: core::Renderer,
57{
58    content: Element<'a, Message, Theme, Renderer>,
59    width: Length,
60    height: Length,
61    position: Point,
62}
63
64impl<'a, Message, Theme, Renderer> Pin<'a, Message, Theme, Renderer>
65where
66    Renderer: core::Renderer,
67{
68    /// Creates a [`Pin`] widget with the given content.
69    pub fn new(content: impl Into<Element<'a, Message, Theme, Renderer>>) -> Self {
70        Self {
71            content: content.into(),
72            width: Length::Fill,
73            height: Length::Fill,
74            position: Point::ORIGIN,
75        }
76    }
77
78    /// Sets the width of the [`Pin`].
79    pub fn width(mut self, width: impl Into<Length>) -> Self {
80        self.width = width.into();
81        self
82    }
83
84    /// Sets the height of the [`Pin`].
85    pub fn height(mut self, height: impl Into<Length>) -> Self {
86        self.height = height.into();
87        self
88    }
89
90    /// Sets the position of the [`Pin`]; where the pinned widget will be displayed.
91    pub fn position(mut self, position: impl Into<Point>) -> Self {
92        self.position = position.into();
93        self
94    }
95
96    /// Sets the X coordinate of the [`Pin`].
97    pub fn x(mut self, x: impl Into<Pixels>) -> Self {
98        self.position.x = x.into().0;
99        self
100    }
101
102    /// Sets the Y coordinate of the [`Pin`].
103    pub fn y(mut self, y: impl Into<Pixels>) -> Self {
104        self.position.y = y.into().0;
105        self
106    }
107}
108
109impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
110    for Pin<'_, Message, Theme, Renderer>
111where
112    Renderer: core::Renderer,
113{
114    fn tag(&self) -> widget::tree::Tag {
115        self.content.as_widget().tag()
116    }
117
118    fn state(&self) -> widget::tree::State {
119        self.content.as_widget().state()
120    }
121
122    fn diff(&mut self, tree: &mut widget::Tree) {
123        self.content.as_widget_mut().diff(tree);
124    }
125
126    fn size(&self) -> Size<Length> {
127        Size {
128            width: self.width,
129            height: self.height,
130        }
131    }
132
133    fn layout(
134        &mut self,
135        tree: &mut widget::Tree,
136        renderer: &Renderer,
137        limits: &layout::Limits,
138    ) -> layout::Node {
139        let limits = limits.width(self.width).height(self.height);
140
141        let available = limits.max() - Size::new(self.position.x, self.position.y);
142
143        let node = self
144            .content
145            .as_widget_mut()
146            .layout(tree, renderer, &layout::Limits::new(Size::ZERO, available))
147            .move_to(self.position);
148
149        let size = limits.resolve(self.width, self.height, node.size());
150        layout::Node::with_children(size, vec![node])
151    }
152
153    fn operate(
154        &mut self,
155        tree: &mut widget::Tree,
156        layout: Layout<'_>,
157        renderer: &Renderer,
158        operation: &mut dyn widget::Operation,
159    ) {
160        self.content.as_widget_mut().operate(
161            tree,
162            layout.children().next().unwrap(),
163            renderer,
164            operation,
165        );
166    }
167
168    fn update(
169        &mut self,
170        tree: &mut widget::Tree,
171        event: &Event,
172        layout: Layout<'_>,
173        cursor: mouse::Cursor,
174        renderer: &Renderer,
175        shell: &mut Shell<'_, Message>,
176        viewport: &Rectangle,
177    ) {
178        self.content.as_widget_mut().update(
179            tree,
180            event,
181            layout.children().next().unwrap(),
182            cursor,
183            renderer,
184            shell,
185            viewport,
186        );
187    }
188
189    fn mouse_interaction(
190        &self,
191        tree: &widget::Tree,
192        layout: Layout<'_>,
193        cursor: mouse::Cursor,
194        viewport: &Rectangle,
195        renderer: &Renderer,
196    ) -> mouse::Interaction {
197        self.content.as_widget().mouse_interaction(
198            tree,
199            layout.children().next().unwrap(),
200            cursor,
201            viewport,
202            renderer,
203        )
204    }
205
206    fn draw(
207        &self,
208        tree: &widget::Tree,
209        renderer: &mut Renderer,
210        theme: &Theme,
211        style: &renderer::Style,
212        layout: Layout<'_>,
213        cursor: mouse::Cursor,
214        viewport: &Rectangle,
215    ) {
216        let bounds = layout.bounds();
217
218        if let Some(clipped_viewport) = bounds.intersection(viewport) {
219            self.content.as_widget().draw(
220                tree,
221                renderer,
222                theme,
223                style,
224                layout.children().next().unwrap(),
225                cursor,
226                &clipped_viewport,
227            );
228        }
229    }
230
231    fn overlay<'b>(
232        &'b mut self,
233        tree: &'b mut widget::Tree,
234        layout: Layout<'b>,
235        renderer: &Renderer,
236        viewport: &Rectangle,
237        translation: Vector,
238    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
239        self.content.as_widget_mut().overlay(
240            tree,
241            layout.children().next().unwrap(),
242            renderer,
243            viewport,
244            translation,
245        )
246    }
247}
248
249impl<'a, Message, Theme, Renderer> From<Pin<'a, Message, Theme, Renderer>>
250    for Element<'a, Message, Theme, Renderer>
251where
252    Message: 'a,
253    Theme: 'a,
254    Renderer: core::Renderer + 'a,
255{
256    fn from(pin: Pin<'a, Message, Theme, Renderer>) -> Element<'a, Message, Theme, Renderer> {
257        Element::new(pin)
258    }
259}