diff options
Diffstat (limited to 'widget/src/pin.rs')
-rw-r--r-- | widget/src/pin.rs | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/widget/src/pin.rs b/widget/src/pin.rs new file mode 100644 index 00000000..1f167716 --- /dev/null +++ b/widget/src/pin.rs @@ -0,0 +1,270 @@ +//! A pin widget positions a widget at some fixed coordinates inside its boundaries. +//! +//! # Example +//! ```no_run +//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::core::Length::Fill; } +//! # pub type State = (); +//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +//! use iced::widget::pin; +//! use iced::Fill; +//! +//! enum Message { +//! // ... +//! } +//! +//! fn view(state: &State) -> Element<'_, Message> { +//! pin("This text is displayed at coordinates (50, 50)!") +//! .x(50) +//! .y(50) +//! .into() +//! } +//! ``` +use crate::core::layout; +use crate::core::mouse; +use crate::core::overlay; +use crate::core::renderer; +use crate::core::widget; +use crate::core::{ + self, Clipboard, Element, Event, Layout, Length, Pixels, Point, Rectangle, + Shell, Size, Vector, Widget, +}; + +/// A widget that positions its contents at some fixed coordinates inside of its boundaries. +/// +/// By default, a [`Pin`] widget will try to fill its parent. +/// +/// # Example +/// ```no_run +/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::core::Length::Fill; } +/// # pub type State = (); +/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>; +/// use iced::widget::pin; +/// use iced::Fill; +/// +/// enum Message { +/// // ... +/// } +/// +/// fn view(state: &State) -> Element<'_, Message> { +/// pin("This text is displayed at coordinates (50, 50)!") +/// .x(50) +/// .y(50) +/// .into() +/// } +/// ``` +#[allow(missing_debug_implementations)] +pub struct Pin<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> +where + Renderer: core::Renderer, +{ + content: Element<'a, Message, Theme, Renderer>, + width: Length, + height: Length, + position: Point, +} + +impl<'a, Message, Theme, Renderer> Pin<'a, Message, Theme, Renderer> +where + Renderer: core::Renderer, +{ + /// Creates a [`Pin`] widget with the given content. + pub fn new( + content: impl Into<Element<'a, Message, Theme, Renderer>>, + ) -> Self { + Self { + content: content.into(), + width: Length::Fill, + height: Length::Fill, + position: Point::ORIGIN, + } + } + + /// Sets the width of the [`Pin`]. + pub fn width(mut self, width: impl Into<Length>) -> Self { + self.width = width.into(); + self + } + + /// Sets the height of the [`Pin`]. + pub fn height(mut self, height: impl Into<Length>) -> Self { + self.height = height.into(); + self + } + + /// Sets the position of the [`Pin`]; where the pinned widget will be displayed. + pub fn position(mut self, position: impl Into<Point>) -> Self { + self.position = position.into(); + self + } + + /// Sets the X coordinate of the [`Pin`]. + pub fn x(mut self, x: impl Into<Pixels>) -> Self { + self.position.x = x.into().0; + self + } + + /// Sets the Y coordinate of the [`Pin`]. + pub fn y(mut self, y: impl Into<Pixels>) -> Self { + self.position.y = y.into().0; + self + } +} + +impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer> + for Pin<'a, Message, Theme, Renderer> +where + Renderer: core::Renderer, +{ + fn tag(&self) -> widget::tree::Tag { + self.content.as_widget().tag() + } + + fn state(&self) -> widget::tree::State { + self.content.as_widget().state() + } + + fn children(&self) -> Vec<widget::Tree> { + self.content.as_widget().children() + } + + fn diff(&self, tree: &mut widget::Tree) { + self.content.as_widget().diff(tree); + } + + fn size(&self) -> Size<Length> { + Size { + width: self.width, + height: self.height, + } + } + + fn layout( + &self, + tree: &mut widget::Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let limits = limits.width(self.width).height(self.height); + + let available = + limits.max() - Size::new(self.position.x, self.position.y); + + let node = self + .content + .as_widget() + .layout(tree, renderer, &layout::Limits::new(Size::ZERO, available)) + .move_to(self.position); + + let size = limits.resolve(self.width, self.height, node.size()); + layout::Node::with_children(size, vec![node]) + } + + fn operate( + &self, + tree: &mut widget::Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn widget::Operation, + ) { + self.content.as_widget().operate( + tree, + layout.children().next().unwrap(), + renderer, + operation, + ); + } + + fn update( + &mut self, + tree: &mut widget::Tree, + event: Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) { + self.content.as_widget_mut().update( + tree, + event, + layout.children().next().unwrap(), + cursor, + renderer, + clipboard, + shell, + viewport, + ); + } + + fn mouse_interaction( + &self, + tree: &widget::Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.content.as_widget().mouse_interaction( + tree, + layout.children().next().unwrap(), + cursor, + viewport, + renderer, + ) + } + + fn draw( + &self, + tree: &widget::Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + let bounds = layout.bounds(); + + if let Some(clipped_viewport) = bounds.intersection(viewport) { + self.content.as_widget().draw( + tree, + renderer, + theme, + style, + layout.children().next().unwrap(), + cursor, + &clipped_viewport, + ); + } + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut widget::Tree, + layout: Layout<'_>, + renderer: &Renderer, + translation: Vector, + ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> { + self.content.as_widget_mut().overlay( + tree, + layout.children().next().unwrap(), + renderer, + translation, + ) + } +} + +impl<'a, Message, Theme, Renderer> From<Pin<'a, Message, Theme, Renderer>> + for Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: 'a, + Renderer: core::Renderer + 'a, +{ + fn from( + pin: Pin<'a, Message, Theme, Renderer>, + ) -> Element<'a, Message, Theme, Renderer> { + Element::new(pin) + } +} |