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