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