From ff2519b1d43d481987351a83b6dd7237524c21f0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 27 Jul 2022 06:49:20 +0200 Subject: Replace stateful widgets with new `iced_pure` API --- pure/src/widget/button.rs | 274 ---------------------- pure/src/widget/checkbox.rs | 109 --------- pure/src/widget/column.rs | 246 -------------------- pure/src/widget/container.rs | 263 --------------------- pure/src/widget/image.rs | 69 ------ pure/src/widget/pane_grid.rs | 414 --------------------------------- pure/src/widget/pane_grid/content.rs | 345 --------------------------- pure/src/widget/pane_grid/title_bar.rs | 388 ------------------------------ pure/src/widget/pick_list.rs | 240 ------------------- pure/src/widget/progress_bar.rs | 105 --------- pure/src/widget/radio.rs | 109 --------- pure/src/widget/row.rs | 233 ------------------- pure/src/widget/rule.rs | 105 --------- pure/src/widget/scrollable.rs | 278 ---------------------- pure/src/widget/slider.rs | 255 -------------------- pure/src/widget/space.rs | 101 -------- pure/src/widget/svg.rs | 65 ------ pure/src/widget/text.rs | 77 ------ pure/src/widget/text_input.rs | 285 ----------------------- pure/src/widget/toggler.rs | 109 --------- pure/src/widget/tooltip.rs | 240 ------------------- pure/src/widget/tree.rs | 176 -------------- 22 files changed, 4486 deletions(-) delete mode 100644 pure/src/widget/button.rs delete mode 100644 pure/src/widget/checkbox.rs delete mode 100644 pure/src/widget/column.rs delete mode 100644 pure/src/widget/container.rs delete mode 100644 pure/src/widget/image.rs delete mode 100644 pure/src/widget/pane_grid.rs delete mode 100644 pure/src/widget/pane_grid/content.rs delete mode 100644 pure/src/widget/pane_grid/title_bar.rs delete mode 100644 pure/src/widget/pick_list.rs delete mode 100644 pure/src/widget/progress_bar.rs delete mode 100644 pure/src/widget/radio.rs delete mode 100644 pure/src/widget/row.rs delete mode 100644 pure/src/widget/rule.rs delete mode 100644 pure/src/widget/scrollable.rs delete mode 100644 pure/src/widget/slider.rs delete mode 100644 pure/src/widget/space.rs delete mode 100644 pure/src/widget/svg.rs delete mode 100644 pure/src/widget/text.rs delete mode 100644 pure/src/widget/text_input.rs delete mode 100644 pure/src/widget/toggler.rs delete mode 100644 pure/src/widget/tooltip.rs delete mode 100644 pure/src/widget/tree.rs (limited to 'pure/src/widget') diff --git a/pure/src/widget/button.rs b/pure/src/widget/button.rs deleted file mode 100644 index eb174e57..00000000 --- a/pure/src/widget/button.rs +++ /dev/null @@ -1,274 +0,0 @@ -//! Allow your users to perform actions by pressing a button. -use crate::overlay; -use crate::widget::tree::{self, Tree}; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::widget::button; -use iced_native::{ - Clipboard, Layout, Length, Padding, Point, Rectangle, Shell, -}; - -pub use iced_style::button::{Appearance, StyleSheet}; - -use button::State; - -/// A generic widget that produces a message when pressed. -/// -/// ``` -/// # type Button<'a, Message> = -/// # iced_pure::widget::Button<'a, Message, iced_native::renderer::Null>; -/// # -/// #[derive(Clone)] -/// enum Message { -/// ButtonPressed, -/// } -/// -/// let button = Button::new("Press me!").on_press(Message::ButtonPressed); -/// ``` -/// -/// If a [`Button::on_press`] handler is not set, the resulting [`Button`] will -/// be disabled: -/// -/// ``` -/// # type Button<'a, Message> = -/// # iced_pure::widget::Button<'a, Message, iced_native::renderer::Null>; -/// # -/// #[derive(Clone)] -/// enum Message { -/// ButtonPressed, -/// } -/// -/// fn disabled_button<'a>() -> Button<'a, Message> { -/// Button::new("I'm disabled!") -/// } -/// -/// fn enabled_button<'a>() -> Button<'a, Message> { -/// disabled_button().on_press(Message::ButtonPressed) -/// } -/// ``` -pub struct Button<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - content: Element<'a, Message, Renderer>, - on_press: Option, - width: Length, - height: Length, - padding: Padding, - style: ::Style, -} - -impl<'a, Message, Renderer> Button<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - /// Creates a new [`Button`] with the given content. - pub fn new(content: impl Into>) -> Self { - Button { - content: content.into(), - on_press: None, - width: Length::Shrink, - height: Length::Shrink, - padding: Padding::new(5), - style: ::Style::default(), - } - } - - /// Sets the width of the [`Button`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the height of the [`Button`]. - pub fn height(mut self, height: Length) -> Self { - self.height = height; - self - } - - /// Sets the [`Padding`] of the [`Button`]. - pub fn padding>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the message that will be produced when the [`Button`] is pressed. - /// - /// Unless `on_press` is called, the [`Button`] will be disabled. - pub fn on_press(mut self, msg: Message) -> Self { - self.on_press = Some(msg); - self - } - - /// Sets the style variant of this [`Button`]. - pub fn style( - mut self, - style: ::Style, - ) -> Self { - self.style = style; - self - } -} - -impl<'a, Message, Renderer> Widget - for Button<'a, Message, Renderer> -where - Message: 'a + Clone, - Renderer: 'a + iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::new(State::new()) - } - - fn children(&self) -> Vec { - vec![Tree::new(&self.content)] - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children(std::slice::from_ref(&self.content)) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - button::layout( - renderer, - limits, - self.width, - self.height, - self.padding, - |renderer, limits| { - self.content.as_widget().layout(renderer, limits) - }, - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - if let event::Status::Captured = self.content.as_widget_mut().on_event( - &mut tree.children[0], - event.clone(), - layout.children().next().unwrap(), - cursor_position, - renderer, - clipboard, - shell, - ) { - return event::Status::Captured; - } - - button::update( - event, - layout, - cursor_position, - shell, - &self.on_press, - || tree.state.downcast_mut::(), - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - _style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - ) { - let bounds = layout.bounds(); - let content_layout = layout.children().next().unwrap(); - - let styling = button::draw( - renderer, - bounds, - cursor_position, - self.on_press.is_some(), - theme, - self.style, - || tree.state.downcast_ref::(), - ); - - self.content.as_widget().draw( - &tree.children[0], - renderer, - theme, - &renderer::Style { - text_color: styling.text_color, - }, - content_layout, - cursor_position, - &bounds, - ); - } - - fn mouse_interaction( - &self, - _tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - _renderer: &Renderer, - ) -> mouse::Interaction { - button::mouse_interaction( - layout, - cursor_position, - self.on_press.is_some(), - ) - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option> { - self.content.as_widget().overlay( - &mut tree.children[0], - layout.children().next().unwrap(), - renderer, - ) - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Message: Clone + 'a, - Renderer: iced_native::Renderer + 'a, - Renderer::Theme: StyleSheet, -{ - fn from(button: Button<'a, Message, Renderer>) -> Self { - Self::new(button) - } -} diff --git a/pure/src/widget/checkbox.rs b/pure/src/widget/checkbox.rs deleted file mode 100644 index e0f9b764..00000000 --- a/pure/src/widget/checkbox.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! Show toggle controls using checkboxes. -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::text; -use iced_native::widget; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -pub use iced_native::widget::checkbox::{Appearance, Checkbox, StyleSheet}; - -impl<'a, Message, Renderer> Widget - for Checkbox<'a, Message, Renderer> -where - Renderer: text::Renderer, - Renderer::Theme: StyleSheet + widget::text::StyleSheet, -{ - fn width(&self) -> Length { - >::width(self) - } - - fn height(&self) -> Length { - >::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - >::layout( - self, renderer, limits, - ) - } - - fn on_event( - &mut self, - _state: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - >::on_event( - self, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - >::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } - - fn mouse_interaction( - &self, - _state: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - >::mouse_interaction( - self, - layout, - cursor_position, - viewport, - renderer, - ) - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: text::Renderer + 'a, - Renderer::Theme: StyleSheet + widget::text::StyleSheet, -{ - fn from(checkbox: Checkbox<'a, Message, Renderer>) -> Self { - Self::new(checkbox) - } -} diff --git a/pure/src/widget/column.rs b/pure/src/widget/column.rs deleted file mode 100644 index 027eff0a..00000000 --- a/pure/src/widget/column.rs +++ /dev/null @@ -1,246 +0,0 @@ -use crate::flex; -use crate::overlay; -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::{ - Alignment, Clipboard, Length, Padding, Point, Rectangle, Shell, -}; - -use std::u32; - -/// A container that distributes its contents vertically. -pub struct Column<'a, Message, Renderer> { - spacing: u16, - padding: Padding, - width: Length, - height: Length, - max_width: u32, - align_items: Alignment, - children: Vec>, -} - -impl<'a, Message, Renderer> Column<'a, Message, Renderer> { - /// Creates an empty [`Column`]. - pub fn new() -> Self { - Self::with_children(Vec::new()) - } - - /// Creates a [`Column`] with the given elements. - pub fn with_children( - children: Vec>, - ) -> Self { - Column { - spacing: 0, - padding: Padding::ZERO, - width: Length::Shrink, - height: Length::Shrink, - max_width: u32::MAX, - align_items: Alignment::Start, - children, - } - } - - /// Sets the vertical spacing _between_ elements. - /// - /// Custom margins per element do not exist in iced. You should use this - /// method instead! While less flexible, it helps you keep spacing between - /// elements consistent. - pub fn spacing(mut self, units: u16) -> Self { - self.spacing = units; - self - } - - /// Sets the [`Padding`] of the [`Column`]. - pub fn padding>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the width of the [`Column`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the height of the [`Column`]. - pub fn height(mut self, height: Length) -> Self { - self.height = height; - self - } - - /// Sets the maximum width of the [`Column`]. - pub fn max_width(mut self, max_width: u32) -> Self { - self.max_width = max_width; - self - } - - /// Sets the horizontal alignment of the contents of the [`Column`] . - pub fn align_items(mut self, align: Alignment) -> Self { - self.align_items = align; - self - } - - /// Adds an element to the [`Column`]. - pub fn push( - mut self, - child: impl Into>, - ) -> Self { - self.children.push(child.into()); - self - } -} - -impl<'a, Message, Renderer> Default for Column<'a, Message, Renderer> { - fn default() -> Self { - Self::new() - } -} - -impl<'a, Message, Renderer> Widget - for Column<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, -{ - fn children(&self) -> Vec { - self.children.iter().map(Tree::new).collect() - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children(&self.children); - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - let limits = limits - .max_width(self.max_width) - .width(self.width) - .height(self.height); - - flex::resolve( - flex::Axis::Vertical, - renderer, - &limits, - self.padding, - self.spacing as f32, - self.align_items, - &self.children, - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - self.children - .iter_mut() - .zip(&mut tree.children) - .zip(layout.children()) - .map(|((child, state), layout)| { - child.as_widget_mut().on_event( - state, - event.clone(), - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - }) - .fold(event::Status::Ignored, event::Status::merge) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - self.children - .iter() - .zip(&tree.children) - .zip(layout.children()) - .map(|((child, state), layout)| { - child.as_widget().mouse_interaction( - state, - layout, - cursor_position, - viewport, - renderer, - ) - }) - .max() - .unwrap_or_default() - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - for ((child, state), layout) in self - .children - .iter() - .zip(&tree.children) - .zip(layout.children()) - { - child.as_widget().draw( - state, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ); - } - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option> { - overlay::from_children(&self.children, tree, layout, renderer) - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: iced_native::Renderer + 'a, -{ - fn from(column: Column<'a, Message, Renderer>) -> Self { - Self::new(column) - } -} diff --git a/pure/src/widget/container.rs b/pure/src/widget/container.rs deleted file mode 100644 index 44ffff8c..00000000 --- a/pure/src/widget/container.rs +++ /dev/null @@ -1,263 +0,0 @@ -//! Decorate content and apply alignment. -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::alignment; -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::overlay; -use iced_native::renderer; -use iced_native::widget::container; -use iced_native::{ - Clipboard, Layout, Length, Padding, Point, Rectangle, Shell, -}; - -use std::u32; - -pub use iced_style::container::{Appearance, StyleSheet}; - -/// An element decorating some content. -/// -/// It is normally used for alignment purposes. -#[allow(missing_debug_implementations)] -pub struct Container<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - padding: Padding, - width: Length, - height: Length, - max_width: u32, - max_height: u32, - horizontal_alignment: alignment::Horizontal, - vertical_alignment: alignment::Vertical, - style: ::Style, - content: Element<'a, Message, Renderer>, -} - -impl<'a, Message, Renderer> Container<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - /// Creates an empty [`Container`]. - pub fn new(content: T) -> Self - where - T: Into>, - { - Container { - padding: Padding::ZERO, - width: Length::Shrink, - height: Length::Shrink, - max_width: u32::MAX, - max_height: u32::MAX, - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Top, - style: Default::default(), - content: content.into(), - } - } - - /// Sets the [`Padding`] of the [`Container`]. - pub fn padding>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the width of the [`Container`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the height of the [`Container`]. - pub fn height(mut self, height: Length) -> Self { - self.height = height; - self - } - - /// Sets the maximum width of the [`Container`]. - pub fn max_width(mut self, max_width: u32) -> Self { - self.max_width = max_width; - self - } - - /// Sets the maximum height of the [`Container`] in pixels. - pub fn max_height(mut self, max_height: u32) -> Self { - self.max_height = max_height; - self - } - - /// Sets the content alignment for the horizontal axis of the [`Container`]. - pub fn align_x(mut self, alignment: alignment::Horizontal) -> Self { - self.horizontal_alignment = alignment; - self - } - - /// Sets the content alignment for the vertical axis of the [`Container`]. - pub fn align_y(mut self, alignment: alignment::Vertical) -> Self { - self.vertical_alignment = alignment; - self - } - - /// Centers the contents in the horizontal axis of the [`Container`]. - pub fn center_x(mut self) -> Self { - self.horizontal_alignment = alignment::Horizontal::Center; - self - } - - /// Centers the contents in the vertical axis of the [`Container`]. - pub fn center_y(mut self) -> Self { - self.vertical_alignment = alignment::Vertical::Center; - self - } - - /// Sets the style of the [`Container`]. - pub fn style( - mut self, - style: impl Into<::Style>, - ) -> Self { - self.style = style.into(); - self - } -} - -impl<'a, Message, Renderer> Widget - for Container<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn children(&self) -> Vec { - vec![Tree::new(&self.content)] - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children(std::slice::from_ref(&self.content)) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - container::layout( - renderer, - limits, - self.width, - self.height, - self.max_width, - self.max_height, - self.padding, - self.horizontal_alignment, - self.vertical_alignment, - |renderer, limits| { - self.content.as_widget().layout(renderer, limits) - }, - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - self.content.as_widget_mut().on_event( - &mut tree.children[0], - event, - layout.children().next().unwrap(), - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - self.content.as_widget().mouse_interaction( - &tree.children[0], - layout.children().next().unwrap(), - cursor_position, - viewport, - renderer, - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - renderer_style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - let style = theme.appearance(self.style); - - container::draw_background(renderer, &style, layout.bounds()); - - self.content.as_widget().draw( - &tree.children[0], - renderer, - theme, - &renderer::Style { - text_color: style - .text_color - .unwrap_or(renderer_style.text_color), - }, - layout.children().next().unwrap(), - cursor_position, - viewport, - ); - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option> { - self.content.as_widget().overlay( - &mut tree.children[0], - layout.children().next().unwrap(), - renderer, - ) - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: 'a + iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn from( - column: Container<'a, Message, Renderer>, - ) -> Element<'a, Message, Renderer> { - Element::new(column) - } -} diff --git a/pure/src/widget/image.rs b/pure/src/widget/image.rs deleted file mode 100644 index 58f81a6f..00000000 --- a/pure/src/widget/image.rs +++ /dev/null @@ -1,69 +0,0 @@ -//! Display images in your user interface. -use crate::widget::{Tree, Widget}; -use crate::Element; - -use iced_native::layout::{self, Layout}; -use iced_native::renderer; -use iced_native::widget::image; -use iced_native::{Length, Point, Rectangle}; - -use std::hash::Hash; - -pub use image::Image; - -impl Widget for Image -where - Handle: Clone + Hash, - Renderer: iced_native::image::Renderer, -{ - fn width(&self) -> Length { - >::width(self) - } - - fn height(&self) -> Length { - >::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - >::layout( - self, renderer, limits, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - >::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } -} - -impl<'a, Message, Renderer, Handle> From> - for Element<'a, Message, Renderer> -where - Message: Clone + 'a, - Renderer: iced_native::image::Renderer + 'a, - Handle: Clone + Hash + 'a, -{ - fn from(image: Image) -> Self { - Self::new(image) - } -} diff --git a/pure/src/widget/pane_grid.rs b/pure/src/widget/pane_grid.rs deleted file mode 100644 index 69150aa8..00000000 --- a/pure/src/widget/pane_grid.rs +++ /dev/null @@ -1,414 +0,0 @@ -//! Let your users split regions of your application and organize layout dynamically. -//! -//! [![Pane grid - Iced](https://thumbs.gfycat.com/MixedFlatJellyfish-small.gif)](https://gfycat.com/mixedflatjellyfish) -//! -//! # Example -//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing, -//! drag and drop, and hotkey support. -//! -//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.4/examples/pane_grid -mod content; -mod title_bar; - -pub use content::Content; -pub use title_bar::TitleBar; - -pub use iced_native::widget::pane_grid::{ - Axis, Configuration, Direction, DragEvent, Node, Pane, ResizeEvent, Split, - State, -}; - -use crate::overlay; -use crate::widget::container; -use crate::widget::tree::{self, Tree}; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::widget::pane_grid; -use iced_native::widget::pane_grid::state; -use iced_native::{Clipboard, Layout, Length, Point, Rectangle, Shell}; - -pub use iced_style::pane_grid::{Line, StyleSheet}; - -/// A collection of panes distributed using either vertical or horizontal splits -/// to completely fill the space available. -/// -/// [![Pane grid - Iced](https://thumbs.gfycat.com/FrailFreshAiredaleterrier-small.gif)](https://gfycat.com/frailfreshairedaleterrier) -/// -/// This distribution of space is common in tiling window managers (like -/// [`awesome`](https://awesomewm.org/), [`i3`](https://i3wm.org/), or even -/// [`tmux`](https://github.com/tmux/tmux)). -/// -/// A [`PaneGrid`] supports: -/// -/// * Vertical and horizontal splits -/// * Tracking of the last active pane -/// * Mouse-based resizing -/// * Drag and drop to reorganize panes -/// * Hotkey support -/// * Configurable modifier keys -/// * [`State`] API to perform actions programmatically (`split`, `swap`, `resize`, etc.) -/// -/// ## Example -/// -/// ``` -/// # use iced_pure::widget::pane_grid; -/// # use iced_pure::text; -/// # -/// # type PaneGrid<'a, Message> = -/// # iced_pure::widget::PaneGrid<'a, Message, iced_native::renderer::Null>; -/// # -/// enum PaneState { -/// SomePane, -/// AnotherKindOfPane, -/// } -/// -/// enum Message { -/// PaneDragged(pane_grid::DragEvent), -/// PaneResized(pane_grid::ResizeEvent), -/// } -/// -/// let (mut state, _) = pane_grid::State::new(PaneState::SomePane); -/// -/// let pane_grid = -/// PaneGrid::new(&state, |pane, state| { -/// pane_grid::Content::new(match state { -/// PaneState::SomePane => text("This is some pane"), -/// PaneState::AnotherKindOfPane => text("This is another kind of pane"), -/// }) -/// }) -/// .on_drag(Message::PaneDragged) -/// .on_resize(10, Message::PaneResized); -/// ``` -#[allow(missing_debug_implementations)] -pub struct PaneGrid<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet + container::StyleSheet, -{ - state: &'a state::Internal, - elements: Vec<(Pane, Content<'a, Message, Renderer>)>, - width: Length, - height: Length, - spacing: u16, - on_click: Option Message + 'a>>, - on_drag: Option Message + 'a>>, - on_resize: Option<(u16, Box Message + 'a>)>, - style: ::Style, -} - -impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet + container::StyleSheet, -{ - /// Creates a [`PaneGrid`] with the given [`State`] and view function. - /// - /// The view function will be called to display each [`Pane`] present in the - /// [`State`]. - pub fn new( - state: &'a State, - view: impl Fn(Pane, &'a T) -> Content<'a, Message, Renderer>, - ) -> Self { - let elements = { - state - .panes - .iter() - .map(|(pane, pane_state)| (*pane, view(*pane, pane_state))) - .collect() - }; - - Self { - elements, - state: &state.internal, - width: Length::Fill, - height: Length::Fill, - spacing: 0, - on_click: None, - on_drag: None, - on_resize: None, - style: Default::default(), - } - } - - /// Sets the width of the [`PaneGrid`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the height of the [`PaneGrid`]. - pub fn height(mut self, height: Length) -> Self { - self.height = height; - self - } - - /// Sets the spacing _between_ the panes of the [`PaneGrid`]. - pub fn spacing(mut self, units: u16) -> Self { - self.spacing = units; - self - } - - /// Sets the message that will be produced when a [`Pane`] of the - /// [`PaneGrid`] is clicked. - pub fn on_click(mut self, f: F) -> Self - where - F: 'a + Fn(Pane) -> Message, - { - self.on_click = Some(Box::new(f)); - self - } - - /// Enables the drag and drop interactions of the [`PaneGrid`], which will - /// use the provided function to produce messages. - pub fn on_drag(mut self, f: F) -> Self - where - F: 'a + Fn(DragEvent) -> Message, - { - self.on_drag = Some(Box::new(f)); - self - } - - /// Enables the resize interactions of the [`PaneGrid`], which will - /// use the provided function to produce messages. - /// - /// The `leeway` describes the amount of space around a split that can be - /// used to grab it. - /// - /// The grabbable area of a split will have a length of `spacing + leeway`, - /// properly centered. In other words, a length of - /// `(spacing + leeway) / 2.0` on either side of the split line. - pub fn on_resize(mut self, leeway: u16, f: F) -> Self - where - F: 'a + Fn(ResizeEvent) -> Message, - { - self.on_resize = Some((leeway, Box::new(f))); - self - } - - /// Sets the style of the [`PaneGrid`]. - pub fn style( - mut self, - style: impl Into<::Style>, - ) -> Self { - self.style = style.into(); - self - } -} - -impl<'a, Message, Renderer> Widget - for PaneGrid<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet + container::StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::new(state::Action::Idle) - } - - fn children(&self) -> Vec { - self.elements - .iter() - .map(|(_, content)| content.state()) - .collect() - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children_custom( - &self.elements, - |state, (_, content)| content.diff(state), - |(_, content)| content.state(), - ) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - pane_grid::layout( - renderer, - limits, - self.state, - self.width, - self.height, - self.spacing, - self.elements.iter().map(|(pane, content)| (*pane, content)), - |element, renderer, limits| element.layout(renderer, limits), - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - let action = tree.state.downcast_mut::(); - - let event_status = pane_grid::update( - action, - self.state, - &event, - layout, - cursor_position, - shell, - self.spacing, - self.elements.iter().map(|(pane, content)| (*pane, content)), - &self.on_click, - &self.on_drag, - &self.on_resize, - ); - - let picked_pane = action.picked_pane().map(|(pane, _)| pane); - - self.elements - .iter_mut() - .zip(&mut tree.children) - .zip(layout.children()) - .map(|(((pane, content), tree), layout)| { - let is_picked = picked_pane == Some(*pane); - - content.on_event( - tree, - event.clone(), - layout, - cursor_position, - renderer, - clipboard, - shell, - is_picked, - ) - }) - .fold(event_status, event::Status::merge) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - pane_grid::mouse_interaction( - tree.state.downcast_ref(), - self.state, - layout, - cursor_position, - self.spacing, - self.on_resize.as_ref().map(|(leeway, _)| *leeway), - ) - .unwrap_or_else(|| { - self.elements - .iter() - .zip(&tree.children) - .zip(layout.children()) - .map(|(((_pane, content), tree), layout)| { - content.mouse_interaction( - tree, - layout, - cursor_position, - viewport, - renderer, - ) - }) - .max() - .unwrap_or_default() - }) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - pane_grid::draw( - tree.state.downcast_ref(), - self.state, - layout, - cursor_position, - renderer, - theme, - style, - viewport, - self.spacing, - self.on_resize.as_ref().map(|(leeway, _)| *leeway), - self.style, - self.elements - .iter() - .zip(&tree.children) - .map(|((pane, content), tree)| (*pane, (content, tree))), - |(content, tree), - renderer, - style, - layout, - cursor_position, - rectangle| { - content.draw( - tree, - renderer, - theme, - style, - layout, - cursor_position, - rectangle, - ); - }, - ) - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option> { - self.elements - .iter() - .zip(&mut tree.children) - .zip(layout.children()) - .filter_map(|(((_, pane), tree), layout)| { - pane.overlay(tree, layout, renderer) - }) - .next() - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: 'a + iced_native::Renderer, - Renderer::Theme: StyleSheet + container::StyleSheet, -{ - fn from( - pane_grid: PaneGrid<'a, Message, Renderer>, - ) -> Element<'a, Message, Renderer> { - Element::new(pane_grid) - } -} diff --git a/pure/src/widget/pane_grid/content.rs b/pure/src/widget/pane_grid/content.rs deleted file mode 100644 index 9c2a0f4a..00000000 --- a/pure/src/widget/pane_grid/content.rs +++ /dev/null @@ -1,345 +0,0 @@ -use crate::widget::pane_grid::TitleBar; -use crate::widget::tree::Tree; -use crate::Element; - -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::overlay; -use iced_native::renderer; -use iced_native::widget::container; -use iced_native::widget::pane_grid::Draggable; -use iced_native::{Clipboard, Layout, Point, Rectangle, Shell, Size}; - -/// The content of a [`Pane`]. -/// -/// [`Pane`]: crate::widget::pane_grid::Pane -#[allow(missing_debug_implementations)] -pub struct Content<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - title_bar: Option>, - body: Element<'a, Message, Renderer>, - style: ::Style, -} - -impl<'a, Message, Renderer> Content<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - /// Creates a new [`Content`] with the provided body. - pub fn new(body: impl Into>) -> Self { - Self { - title_bar: None, - body: body.into(), - style: Default::default(), - } - } - - /// Sets the [`TitleBar`] of this [`Content`]. - pub fn title_bar( - mut self, - title_bar: TitleBar<'a, Message, Renderer>, - ) -> Self { - self.title_bar = Some(title_bar); - self - } - - /// Sets the style of the [`Content`]. - pub fn style( - mut self, - style: impl Into<::Style>, - ) -> Self { - self.style = style.into(); - self - } -} - -impl<'a, Message, Renderer> Content<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - pub(super) fn state(&self) -> Tree { - let children = if let Some(title_bar) = self.title_bar.as_ref() { - vec![Tree::new(&self.body), title_bar.state()] - } else { - vec![Tree::new(&self.body), Tree::empty()] - }; - - Tree { - children, - ..Tree::empty() - } - } - - pub(super) fn diff(&self, tree: &mut Tree) { - if tree.children.len() == 2 { - if let Some(title_bar) = self.title_bar.as_ref() { - title_bar.diff(&mut tree.children[1]); - } - - tree.children[0].diff(&self.body); - } else { - *tree = self.state(); - } - } - - /// Draws the [`Content`] with the provided [`Renderer`] and [`Layout`]. - /// - /// [`Renderer`]: iced_native::Renderer - pub fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - use container::StyleSheet; - - let bounds = layout.bounds(); - - { - let style = theme.appearance(self.style); - - container::draw_background(renderer, &style, bounds); - } - - if let Some(title_bar) = &self.title_bar { - let mut children = layout.children(); - let title_bar_layout = children.next().unwrap(); - let body_layout = children.next().unwrap(); - - let show_controls = bounds.contains(cursor_position); - - title_bar.draw( - &tree.children[1], - renderer, - theme, - style, - title_bar_layout, - cursor_position, - viewport, - show_controls, - ); - - self.body.as_widget().draw( - &tree.children[0], - renderer, - theme, - style, - body_layout, - cursor_position, - viewport, - ); - } else { - self.body.as_widget().draw( - &tree.children[0], - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ); - } - } - - pub(crate) fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - if let Some(title_bar) = &self.title_bar { - let max_size = limits.max(); - - let title_bar_layout = title_bar - .layout(renderer, &layout::Limits::new(Size::ZERO, max_size)); - - let title_bar_size = title_bar_layout.size(); - - let mut body_layout = self.body.as_widget().layout( - renderer, - &layout::Limits::new( - Size::ZERO, - Size::new( - max_size.width, - max_size.height - title_bar_size.height, - ), - ), - ); - - body_layout.move_to(Point::new(0.0, title_bar_size.height)); - - layout::Node::with_children( - max_size, - vec![title_bar_layout, body_layout], - ) - } else { - self.body.as_widget().layout(renderer, limits) - } - } - - pub(crate) fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - is_picked: bool, - ) -> event::Status { - let mut event_status = event::Status::Ignored; - - let body_layout = if let Some(title_bar) = &mut self.title_bar { - let mut children = layout.children(); - - event_status = title_bar.on_event( - &mut tree.children[1], - event.clone(), - children.next().unwrap(), - cursor_position, - renderer, - clipboard, - shell, - ); - - children.next().unwrap() - } else { - layout - }; - - let body_status = if is_picked { - event::Status::Ignored - } else { - self.body.as_widget_mut().on_event( - &mut tree.children[0], - event, - body_layout, - cursor_position, - renderer, - clipboard, - shell, - ) - }; - - event_status.merge(body_status) - } - - pub(crate) fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - let (body_layout, title_bar_interaction) = - if let Some(title_bar) = &self.title_bar { - let mut children = layout.children(); - let title_bar_layout = children.next().unwrap(); - - let is_over_pick_area = title_bar - .is_over_pick_area(title_bar_layout, cursor_position); - - if is_over_pick_area { - return mouse::Interaction::Grab; - } - - let mouse_interaction = title_bar.mouse_interaction( - &tree.children[1], - title_bar_layout, - cursor_position, - viewport, - renderer, - ); - - (children.next().unwrap(), mouse_interaction) - } else { - (layout, mouse::Interaction::default()) - }; - - self.body - .as_widget() - .mouse_interaction( - &tree.children[0], - body_layout, - cursor_position, - viewport, - renderer, - ) - .max(title_bar_interaction) - } - - pub(crate) fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option> { - if let Some(title_bar) = self.title_bar.as_ref() { - let mut children = layout.children(); - let title_bar_layout = children.next()?; - - let mut states = tree.children.iter_mut(); - let body_state = states.next().unwrap(); - let title_bar_state = states.next().unwrap(); - - match title_bar.overlay(title_bar_state, title_bar_layout, renderer) - { - Some(overlay) => Some(overlay), - None => self.body.as_widget().overlay( - body_state, - children.next()?, - renderer, - ), - } - } else { - self.body.as_widget().overlay( - &mut tree.children[0], - layout, - renderer, - ) - } - } -} - -impl<'a, Message, Renderer> Draggable for &Content<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - fn can_be_dragged_at( - &self, - layout: Layout<'_>, - cursor_position: Point, - ) -> bool { - if let Some(title_bar) = &self.title_bar { - let mut children = layout.children(); - let title_bar_layout = children.next().unwrap(); - - title_bar.is_over_pick_area(title_bar_layout, cursor_position) - } else { - false - } - } -} - -impl<'a, T, Message, Renderer> From for Content<'a, Message, Renderer> -where - T: Into>, - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - fn from(element: T) -> Self { - Self::new(element) - } -} diff --git a/pure/src/widget/pane_grid/title_bar.rs b/pure/src/widget/pane_grid/title_bar.rs deleted file mode 100644 index de9591a2..00000000 --- a/pure/src/widget/pane_grid/title_bar.rs +++ /dev/null @@ -1,388 +0,0 @@ -use crate::widget::Tree; -use crate::Element; - -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::overlay; -use iced_native::renderer; -use iced_native::widget::container; -use iced_native::{Clipboard, Layout, Padding, Point, Rectangle, Shell, Size}; - -/// The title bar of a [`Pane`]. -/// -/// [`Pane`]: crate::widget::pane_grid::Pane -#[allow(missing_debug_implementations)] -pub struct TitleBar<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - content: Element<'a, Message, Renderer>, - controls: Option>, - padding: Padding, - always_show_controls: bool, - style: ::Style, -} - -impl<'a, Message, Renderer> TitleBar<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - /// Creates a new [`TitleBar`] with the given content. - pub fn new(content: E) -> Self - where - E: Into>, - { - Self { - content: content.into(), - controls: None, - padding: Padding::ZERO, - always_show_controls: false, - style: Default::default(), - } - } - - /// Sets the controls of the [`TitleBar`]. - pub fn controls( - mut self, - controls: impl Into>, - ) -> Self { - self.controls = Some(controls.into()); - self - } - - /// Sets the [`Padding`] of the [`TitleBar`]. - pub fn padding>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the style of the [`TitleBar`]. - pub fn style( - mut self, - style: impl Into<::Style>, - ) -> Self { - self.style = style.into(); - self - } - - /// Sets whether or not the [`controls`] attached to this [`TitleBar`] are - /// always visible. - /// - /// By default, the controls are only visible when the [`Pane`] of this - /// [`TitleBar`] is hovered. - /// - /// [`controls`]: Self::controls - /// [`Pane`]: crate::widget::pane_grid::Pane - pub fn always_show_controls(mut self) -> Self { - self.always_show_controls = true; - self - } -} - -impl<'a, Message, Renderer> TitleBar<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - pub(super) fn state(&self) -> Tree { - let children = if let Some(controls) = self.controls.as_ref() { - vec![Tree::new(&self.content), Tree::new(controls)] - } else { - vec![Tree::new(&self.content), Tree::empty()] - }; - - Tree { - children, - ..Tree::empty() - } - } - - pub(super) fn diff(&self, tree: &mut Tree) { - if tree.children.len() == 2 { - if let Some(controls) = self.controls.as_ref() { - tree.children[1].diff(controls); - } - - tree.children[0].diff(&self.content); - } else { - *tree = self.state(); - } - } - - /// Draws the [`TitleBar`] with the provided [`Renderer`] and [`Layout`]. - /// - /// [`Renderer`]: iced_native::Renderer - pub fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - inherited_style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - show_controls: bool, - ) { - use container::StyleSheet; - - let bounds = layout.bounds(); - let style = theme.appearance(self.style); - let inherited_style = renderer::Style { - text_color: style.text_color.unwrap_or(inherited_style.text_color), - }; - - container::draw_background(renderer, &style, bounds); - - let mut children = layout.children(); - let padded = children.next().unwrap(); - - let mut children = padded.children(); - let title_layout = children.next().unwrap(); - let mut show_title = true; - - if let Some(controls) = &self.controls { - let controls_layout = children.next().unwrap(); - if title_layout.bounds().width + controls_layout.bounds().width - > padded.bounds().width - { - show_title = false; - } - - if show_controls || self.always_show_controls { - controls.as_widget().draw( - &tree.children[1], - renderer, - theme, - &inherited_style, - controls_layout, - cursor_position, - viewport, - ); - } - } - - if show_title { - self.content.as_widget().draw( - &tree.children[0], - renderer, - theme, - &inherited_style, - title_layout, - cursor_position, - viewport, - ); - } - } - - /// Returns whether the mouse cursor is over the pick area of the - /// [`TitleBar`] or not. - /// - /// The whole [`TitleBar`] is a pick area, except its controls. - pub fn is_over_pick_area( - &self, - layout: Layout<'_>, - cursor_position: Point, - ) -> bool { - if layout.bounds().contains(cursor_position) { - let mut children = layout.children(); - let padded = children.next().unwrap(); - let mut children = padded.children(); - let title_layout = children.next().unwrap(); - - if self.controls.is_some() { - let controls_layout = children.next().unwrap(); - - !controls_layout.bounds().contains(cursor_position) - && !title_layout.bounds().contains(cursor_position) - } else { - !title_layout.bounds().contains(cursor_position) - } - } else { - false - } - } - - pub(crate) fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - let limits = limits.pad(self.padding); - let max_size = limits.max(); - - let title_layout = self - .content - .as_widget() - .layout(renderer, &layout::Limits::new(Size::ZERO, max_size)); - - let title_size = title_layout.size(); - - let mut node = if let Some(controls) = &self.controls { - let mut controls_layout = controls - .as_widget() - .layout(renderer, &layout::Limits::new(Size::ZERO, max_size)); - - let controls_size = controls_layout.size(); - let space_before_controls = max_size.width - controls_size.width; - - let height = title_size.height.max(controls_size.height); - - controls_layout.move_to(Point::new(space_before_controls, 0.0)); - - layout::Node::with_children( - Size::new(max_size.width, height), - vec![title_layout, controls_layout], - ) - } else { - layout::Node::with_children( - Size::new(max_size.width, title_size.height), - vec![title_layout], - ) - }; - - node.move_to(Point::new( - self.padding.left.into(), - self.padding.top.into(), - )); - - layout::Node::with_children(node.size().pad(self.padding), vec![node]) - } - - pub(crate) fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - let mut children = layout.children(); - let padded = children.next().unwrap(); - - let mut children = padded.children(); - let title_layout = children.next().unwrap(); - let mut show_title = true; - - let control_status = if let Some(controls) = &mut self.controls { - let controls_layout = children.next().unwrap(); - if title_layout.bounds().width + controls_layout.bounds().width - > padded.bounds().width - { - show_title = false; - } - - controls.as_widget_mut().on_event( - &mut tree.children[1], - event.clone(), - controls_layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } else { - event::Status::Ignored - }; - - let title_status = if show_title { - self.content.as_widget_mut().on_event( - &mut tree.children[0], - event, - title_layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } else { - event::Status::Ignored - }; - - control_status.merge(title_status) - } - - pub(crate) fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - let mut children = layout.children(); - let padded = children.next().unwrap(); - - let mut children = padded.children(); - let title_layout = children.next().unwrap(); - - let title_interaction = self.content.as_widget().mouse_interaction( - &tree.children[0], - title_layout, - cursor_position, - viewport, - renderer, - ); - - if let Some(controls) = &self.controls { - let controls_layout = children.next().unwrap(); - let controls_interaction = controls.as_widget().mouse_interaction( - &tree.children[1], - controls_layout, - cursor_position, - viewport, - renderer, - ); - - if title_layout.bounds().width + controls_layout.bounds().width - > padded.bounds().width - { - controls_interaction - } else { - controls_interaction.max(title_interaction) - } - } else { - title_interaction - } - } - - pub(crate) fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option> { - let mut children = layout.children(); - let padded = children.next()?; - - let mut children = padded.children(); - let title_layout = children.next()?; - - let Self { - content, controls, .. - } = self; - - let mut states = tree.children.iter_mut(); - let title_state = states.next().unwrap(); - let controls_state = states.next().unwrap(); - - content - .as_widget() - .overlay(title_state, title_layout, renderer) - .or_else(move || { - controls.as_ref().and_then(|controls| { - let controls_layout = children.next()?; - - controls.as_widget().overlay( - controls_state, - controls_layout, - renderer, - ) - }) - }) - } -} diff --git a/pure/src/widget/pick_list.rs b/pure/src/widget/pick_list.rs deleted file mode 100644 index 9264544a..00000000 --- a/pure/src/widget/pick_list.rs +++ /dev/null @@ -1,240 +0,0 @@ -//! Display a dropdown list of selectable values. -use crate::widget::tree::{self, Tree}; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::overlay; -use iced_native::renderer; -use iced_native::text; -use iced_native::widget::pick_list; -use iced_native::{ - Clipboard, Layout, Length, Padding, Point, Rectangle, Shell, -}; - -use std::borrow::Cow; - -pub use iced_style::pick_list::{Appearance, StyleSheet}; - -/// A widget for selecting a single value from a list of options. -#[allow(missing_debug_implementations)] -pub struct PickList<'a, T, Message, Renderer> -where - [T]: ToOwned>, - Renderer: text::Renderer, - Renderer::Theme: StyleSheet, -{ - on_selected: Box Message + 'a>, - options: Cow<'a, [T]>, - placeholder: Option, - selected: Option, - width: Length, - padding: Padding, - text_size: Option, - font: Renderer::Font, - style: ::Style, -} - -impl<'a, T: 'a, Message, Renderer> PickList<'a, T, Message, Renderer> -where - T: ToString + Eq, - [T]: ToOwned>, - Renderer: text::Renderer, - Renderer::Theme: StyleSheet, -{ - /// The default padding of a [`PickList`]. - pub const DEFAULT_PADDING: Padding = Padding::new(5); - - /// Creates a new [`PickList`] with the given list of options, the current - /// selected value, and the message to produce when an option is selected. - pub fn new( - options: impl Into>, - selected: Option, - on_selected: impl Fn(T) -> Message + 'a, - ) -> Self { - Self { - on_selected: Box::new(on_selected), - options: options.into(), - placeholder: None, - selected, - width: Length::Shrink, - text_size: None, - padding: Self::DEFAULT_PADDING, - font: Default::default(), - style: Default::default(), - } - } - - /// Sets the placeholder of the [`PickList`]. - pub fn placeholder(mut self, placeholder: impl Into) -> Self { - self.placeholder = Some(placeholder.into()); - self - } - - /// Sets the width of the [`PickList`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the [`Padding`] of the [`PickList`]. - pub fn padding>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the text size of the [`PickList`]. - pub fn text_size(mut self, size: u16) -> Self { - self.text_size = Some(size); - self - } - - /// Sets the font of the [`PickList`]. - pub fn font(mut self, font: Renderer::Font) -> Self { - self.font = font; - self - } - - /// Sets the style of the [`PickList`]. - pub fn style( - mut self, - style: impl Into<::Style>, - ) -> Self { - self.style = style.into(); - self - } -} - -impl<'a, T: 'a, Message, Renderer> Widget - for PickList<'a, T, Message, Renderer> -where - T: Clone + ToString + Eq + 'static, - [T]: ToOwned>, - Message: 'a, - Renderer: text::Renderer + 'a, - Renderer::Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::>() - } - - fn state(&self) -> tree::State { - tree::State::new(pick_list::State::::new()) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - Length::Shrink - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - pick_list::layout( - renderer, - limits, - self.width, - self.padding, - self.text_size, - &self.font, - self.placeholder.as_deref(), - &self.options, - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - _renderer: &Renderer, - _clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - pick_list::update( - event, - layout, - cursor_position, - shell, - self.on_selected.as_ref(), - self.selected.as_ref(), - &self.options, - || tree.state.downcast_mut::>(), - ) - } - - fn mouse_interaction( - &self, - _tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - _renderer: &Renderer, - ) -> mouse::Interaction { - pick_list::mouse_interaction(layout, cursor_position) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - _style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - ) { - pick_list::draw( - renderer, - theme, - layout, - cursor_position, - self.padding, - self.text_size, - &self.font, - self.placeholder.as_deref(), - self.selected.as_ref(), - self.style, - ) - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - _renderer: &Renderer, - ) -> Option> { - let state = tree.state.downcast_mut::>(); - - pick_list::overlay( - layout, - state, - self.padding, - self.text_size, - self.font.clone(), - &self.options, - self.style, - ) - } -} - -impl<'a, T: 'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - T: Clone + ToString + Eq + 'static, - [T]: ToOwned>, - Message: 'a, - Renderer: text::Renderer + 'a, - Renderer::Theme: StyleSheet, -{ - fn from(pick_list: PickList<'a, T, Message, Renderer>) -> Self { - Self::new(pick_list) - } -} diff --git a/pure/src/widget/progress_bar.rs b/pure/src/widget/progress_bar.rs deleted file mode 100644 index c9644853..00000000 --- a/pure/src/widget/progress_bar.rs +++ /dev/null @@ -1,105 +0,0 @@ -//! Provide progress feedback to your users. -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -pub use iced_native::widget::progress_bar::*; - -impl Widget for ProgressBar -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn width(&self) -> Length { - >::width(self) - } - - fn height(&self) -> Length { - >::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - >::layout( - self, renderer, limits, - ) - } - - fn on_event( - &mut self, - _state: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - >::on_event( - self, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - >::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } - - fn mouse_interaction( - &self, - _state: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - >::mouse_interaction( - self, - layout, - cursor_position, - viewport, - renderer, - ) - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Renderer: iced_native::Renderer + 'a, - Renderer::Theme: StyleSheet, -{ - fn from(progress_bar: ProgressBar) -> Self { - Self::new(progress_bar) - } -} diff --git a/pure/src/widget/radio.rs b/pure/src/widget/radio.rs deleted file mode 100644 index 604c2785..00000000 --- a/pure/src/widget/radio.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! Create choices using radio buttons. -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::text; -use iced_native::widget; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -pub use iced_native::widget::radio::{Appearance, Radio, StyleSheet}; - -impl Widget for Radio -where - Message: Clone, - Renderer: text::Renderer, - Renderer::Theme: StyleSheet + widget::text::StyleSheet, -{ - fn width(&self) -> Length { - >::width(self) - } - - fn height(&self) -> Length { - >::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - >::layout( - self, renderer, limits, - ) - } - - fn on_event( - &mut self, - _state: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - >::on_event( - self, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - >::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } - - fn mouse_interaction( - &self, - _state: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - >::mouse_interaction( - self, - layout, - cursor_position, - viewport, - renderer, - ) - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Message: 'a + Clone, - Renderer: text::Renderer + 'a, - Renderer::Theme: StyleSheet + widget::text::StyleSheet, -{ - fn from(radio: Radio) -> Self { - Self::new(radio) - } -} diff --git a/pure/src/widget/row.rs b/pure/src/widget/row.rs deleted file mode 100644 index a288a68d..00000000 --- a/pure/src/widget/row.rs +++ /dev/null @@ -1,233 +0,0 @@ -use crate::flex; -use crate::overlay; -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::{ - Alignment, Clipboard, Length, Padding, Point, Rectangle, Shell, -}; - -/// A container that distributes its contents horizontally. -pub struct Row<'a, Message, Renderer> { - spacing: u16, - padding: Padding, - width: Length, - height: Length, - align_items: Alignment, - children: Vec>, -} - -impl<'a, Message, Renderer> Row<'a, Message, Renderer> { - /// Creates an empty [`Row`]. - pub fn new() -> Self { - Self::with_children(Vec::new()) - } - - /// Creates a [`Row`] with the given elements. - pub fn with_children( - children: Vec>, - ) -> Self { - Row { - spacing: 0, - padding: Padding::ZERO, - width: Length::Shrink, - height: Length::Shrink, - align_items: Alignment::Start, - children, - } - } - - /// Sets the horizontal spacing _between_ elements. - /// - /// Custom margins per element do not exist in iced. You should use this - /// method instead! While less flexible, it helps you keep spacing between - /// elements consistent. - pub fn spacing(mut self, units: u16) -> Self { - self.spacing = units; - self - } - - /// Sets the [`Padding`] of the [`Row`]. - pub fn padding>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the width of the [`Row`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the height of the [`Row`]. - pub fn height(mut self, height: Length) -> Self { - self.height = height; - self - } - - /// Sets the vertical alignment of the contents of the [`Row`] . - pub fn align_items(mut self, align: Alignment) -> Self { - self.align_items = align; - self - } - - /// Adds an [`Element`] to the [`Row`]. - pub fn push( - mut self, - child: impl Into>, - ) -> Self { - self.children.push(child.into()); - self - } -} - -impl<'a, Message, Renderer> Default for Row<'a, Message, Renderer> { - fn default() -> Self { - Self::new() - } -} - -impl<'a, Message, Renderer> Widget - for Row<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, -{ - fn children(&self) -> Vec { - self.children.iter().map(Tree::new).collect() - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children(&self.children) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - let limits = limits.width(self.width).height(self.height); - - flex::resolve( - flex::Axis::Horizontal, - renderer, - &limits, - self.padding, - self.spacing as f32, - self.align_items, - &self.children, - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - self.children - .iter_mut() - .zip(&mut tree.children) - .zip(layout.children()) - .map(|((child, state), layout)| { - child.as_widget_mut().on_event( - state, - event.clone(), - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - }) - .fold(event::Status::Ignored, event::Status::merge) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - self.children - .iter() - .zip(&tree.children) - .zip(layout.children()) - .map(|((child, state), layout)| { - child.as_widget().mouse_interaction( - state, - layout, - cursor_position, - viewport, - renderer, - ) - }) - .max() - .unwrap_or_default() - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - for ((child, state), layout) in self - .children - .iter() - .zip(&tree.children) - .zip(layout.children()) - { - child.as_widget().draw( - state, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ); - } - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option> { - overlay::from_children(&self.children, tree, layout, renderer) - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: iced_native::Renderer + 'a, -{ - fn from(row: Row<'a, Message, Renderer>) -> Self { - Self::new(row) - } -} diff --git a/pure/src/widget/rule.rs b/pure/src/widget/rule.rs deleted file mode 100644 index 0fb4ebab..00000000 --- a/pure/src/widget/rule.rs +++ /dev/null @@ -1,105 +0,0 @@ -//! Display a horizontal or vertical rule for dividing content. -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -pub use iced_native::widget::rule::*; - -impl Widget for Rule -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn width(&self) -> Length { - >::width(self) - } - - fn height(&self) -> Length { - >::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - >::layout( - self, renderer, limits, - ) - } - - fn on_event( - &mut self, - _state: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - >::on_event( - self, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - >::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } - - fn mouse_interaction( - &self, - _state: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - >::mouse_interaction( - self, - layout, - cursor_position, - viewport, - renderer, - ) - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Renderer: iced_native::Renderer + 'a, - Renderer::Theme: StyleSheet, -{ - fn from(rule: Rule) -> Self { - Self::new(rule) - } -} diff --git a/pure/src/widget/scrollable.rs b/pure/src/widget/scrollable.rs deleted file mode 100644 index 4118b67e..00000000 --- a/pure/src/widget/scrollable.rs +++ /dev/null @@ -1,278 +0,0 @@ -//! Navigate an endless amount of content with a scrollbar. -use crate::overlay; -use crate::widget::tree::{self, Tree}; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::widget::scrollable; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell, Vector}; - -pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; - -/// A widget that can vertically display an infinite amount of content with a -/// scrollbar. -#[allow(missing_debug_implementations)] -pub struct Scrollable<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - height: Length, - scrollbar_width: u16, - scrollbar_margin: u16, - scroller_width: u16, - content: Element<'a, Message, Renderer>, - on_scroll: Option Message + 'a>>, - style: ::Style, -} - -impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - /// Creates a new [`Scrollable`]. - pub fn new(content: impl Into>) -> Self { - Scrollable { - height: Length::Shrink, - scrollbar_width: 10, - scrollbar_margin: 0, - scroller_width: 10, - content: content.into(), - on_scroll: None, - style: Default::default(), - } - } - - /// Sets the height of the [`Scrollable`]. - pub fn height(mut self, height: Length) -> Self { - self.height = height; - self - } - - /// Sets the scrollbar width of the [`Scrollable`] . - /// Silently enforces a minimum value of 1. - pub fn scrollbar_width(mut self, scrollbar_width: u16) -> Self { - self.scrollbar_width = scrollbar_width.max(1); - self - } - - /// Sets the scrollbar margin of the [`Scrollable`] . - pub fn scrollbar_margin(mut self, scrollbar_margin: u16) -> Self { - self.scrollbar_margin = scrollbar_margin; - self - } - - /// Sets the scroller width of the [`Scrollable`] . - /// - /// It silently enforces a minimum value of 1. - pub fn scroller_width(mut self, scroller_width: u16) -> Self { - self.scroller_width = scroller_width.max(1); - self - } - - /// Sets a function to call when the [`Scrollable`] is scrolled. - /// - /// The function takes the new relative offset of the [`Scrollable`] - /// (e.g. `0` means top, while `1` means bottom). - pub fn on_scroll(mut self, f: impl Fn(f32) -> Message + 'a) -> Self { - self.on_scroll = Some(Box::new(f)); - self - } - - /// Sets the style of the [`Scrollable`] . - pub fn style( - mut self, - style: impl Into<::Style>, - ) -> Self { - self.style = style.into(); - self - } -} - -impl<'a, Message, Renderer> Widget - for Scrollable<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::new(scrollable::State::new()) - } - - fn children(&self) -> Vec { - vec![Tree::new(&self.content)] - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children(std::slice::from_ref(&self.content)) - } - - fn width(&self) -> Length { - self.content.as_widget().width() - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - scrollable::layout( - renderer, - limits, - Widget::::width(self), - self.height, - u32::MAX, - |renderer, limits| { - self.content.as_widget().layout(renderer, limits) - }, - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - scrollable::update( - tree.state.downcast_mut::(), - event, - layout, - cursor_position, - clipboard, - shell, - self.scrollbar_width, - self.scrollbar_margin, - self.scroller_width, - &self.on_scroll, - |event, layout, cursor_position, clipboard, shell| { - self.content.as_widget_mut().on_event( - &mut tree.children[0], - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - }, - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - ) { - scrollable::draw( - tree.state.downcast_ref::(), - renderer, - theme, - layout, - cursor_position, - self.scrollbar_width, - self.scrollbar_margin, - self.scroller_width, - self.style, - |renderer, layout, cursor_position, viewport| { - self.content.as_widget().draw( - &tree.children[0], - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - }, - ) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - scrollable::mouse_interaction( - tree.state.downcast_ref::(), - layout, - cursor_position, - self.scrollbar_width, - self.scrollbar_margin, - self.scroller_width, - |layout, cursor_position, viewport| { - self.content.as_widget().mouse_interaction( - &tree.children[0], - layout, - cursor_position, - viewport, - renderer, - ) - }, - ) - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option> { - self.content - .as_widget() - .overlay( - &mut tree.children[0], - layout.children().next().unwrap(), - renderer, - ) - .map(|overlay| { - let bounds = layout.bounds(); - let content_layout = layout.children().next().unwrap(); - let content_bounds = content_layout.bounds(); - let offset = tree - .state - .downcast_ref::() - .offset(bounds, content_bounds); - - overlay.translate(Vector::new(0.0, -(offset as f32))) - }) - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Message: 'a + Clone, - Renderer: 'a + iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn from( - text_input: Scrollable<'a, Message, Renderer>, - ) -> Element<'a, Message, Renderer> { - Element::new(text_input) - } -} diff --git a/pure/src/widget/slider.rs b/pure/src/widget/slider.rs deleted file mode 100644 index fed979e5..00000000 --- a/pure/src/widget/slider.rs +++ /dev/null @@ -1,255 +0,0 @@ -//! Display an interactive selector of a single value from a range of values. -use crate::widget::tree::{self, Tree}; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::widget::slider; -use iced_native::{Clipboard, Layout, Length, Point, Rectangle, Shell, Size}; - -use std::ops::RangeInclusive; - -pub use iced_style::slider::{Appearance, Handle, HandleShape, StyleSheet}; - -/// An horizontal bar and a handle that selects a single value from a range of -/// values. -/// -/// A [`Slider`] will try to fill the horizontal space of its container. -/// -/// The [`Slider`] range of numeric values is generic and its step size defaults -/// to 1 unit. -/// -/// # Example -/// ``` -/// # use iced_pure::widget::slider; -/// # use iced_native::renderer::Null; -/// # -/// # type Slider<'a, T, Message> = slider::Slider<'a, T, Message, Null>; -/// # -/// #[derive(Clone)] -/// pub enum Message { -/// SliderChanged(f32), -/// } -/// -/// let value = 50.0; -/// -/// Slider::new(0.0..=100.0, value, Message::SliderChanged); -/// ``` -/// -/// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true) -#[allow(missing_debug_implementations)] -pub struct Slider<'a, T, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - range: RangeInclusive, - step: T, - value: T, - on_change: Box Message + 'a>, - on_release: Option, - width: Length, - height: u16, - style: ::Style, -} - -impl<'a, T, Message, Renderer> Slider<'a, T, Message, Renderer> -where - T: Copy + From + std::cmp::PartialOrd, - Message: Clone, - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - /// The default height of a [`Slider`]. - pub const DEFAULT_HEIGHT: u16 = 22; - - /// Creates a new [`Slider`]. - /// - /// It expects: - /// * an inclusive range of possible values - /// * the current value of the [`Slider`] - /// * a function that will be called when the [`Slider`] is dragged. - /// It receives the new value of the [`Slider`] and must produce a - /// `Message`. - pub fn new(range: RangeInclusive, value: T, on_change: F) -> Self - where - F: 'a + Fn(T) -> Message, - { - let value = if value >= *range.start() { - value - } else { - *range.start() - }; - - let value = if value <= *range.end() { - value - } else { - *range.end() - }; - - Slider { - value, - range, - step: T::from(1), - on_change: Box::new(on_change), - on_release: None, - width: Length::Fill, - height: Self::DEFAULT_HEIGHT, - style: Default::default(), - } - } - - /// Sets the release message of the [`Slider`]. - /// This is called when the mouse is released from the slider. - /// - /// Typically, the user's interaction with the slider is finished when this message is produced. - /// This is useful if you need to spawn a long-running task from the slider's result, where - /// the default on_change message could create too many events. - pub fn on_release(mut self, on_release: Message) -> Self { - self.on_release = Some(on_release); - self - } - - /// Sets the width of the [`Slider`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the height of the [`Slider`]. - pub fn height(mut self, height: u16) -> Self { - self.height = height; - self - } - - /// Sets the style of the [`Slider`]. - pub fn style( - mut self, - style: impl Into<::Style>, - ) -> Self { - self.style = style.into(); - self - } - - /// Sets the step size of the [`Slider`]. - pub fn step(mut self, step: T) -> Self { - self.step = step; - self - } -} - -impl<'a, T, Message, Renderer> Widget - for Slider<'a, T, Message, Renderer> -where - T: Copy + Into + num_traits::FromPrimitive, - Message: Clone, - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::new(slider::State::new()) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - Length::Shrink - } - - fn layout( - &self, - _renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - let limits = - limits.width(self.width).height(Length::Units(self.height)); - - let size = limits.resolve(Size::ZERO); - - layout::Node::new(size) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - _renderer: &Renderer, - _clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - slider::update( - event, - layout, - cursor_position, - shell, - tree.state.downcast_mut::(), - &mut self.value, - &self.range, - self.step, - self.on_change.as_ref(), - &self.on_release, - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - _style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - ) { - slider::draw( - renderer, - layout, - cursor_position, - tree.state.downcast_ref::(), - self.value, - &self.range, - theme, - self.style, - ) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - _renderer: &Renderer, - ) -> mouse::Interaction { - slider::mouse_interaction( - layout, - cursor_position, - tree.state.downcast_ref::(), - ) - } -} - -impl<'a, T, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - T: 'a + Copy + Into + num_traits::FromPrimitive, - Message: 'a + Clone, - Renderer: 'a + iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn from( - slider: Slider<'a, T, Message, Renderer>, - ) -> Element<'a, Message, Renderer> { - Element::new(slider) - } -} diff --git a/pure/src/widget/space.rs b/pure/src/widget/space.rs deleted file mode 100644 index 408cb647..00000000 --- a/pure/src/widget/space.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -pub use iced_native::widget::Space; - -impl Widget for Space -where - Renderer: iced_native::Renderer, -{ - fn width(&self) -> Length { - >::width(self) - } - - fn height(&self) -> Length { - >::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - >::layout( - self, renderer, limits, - ) - } - - fn on_event( - &mut self, - _state: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - >::on_event( - self, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - >::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } - - fn mouse_interaction( - &self, - _state: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - >::mouse_interaction( - self, - layout, - cursor_position, - viewport, - renderer, - ) - } -} - -impl<'a, Message, Renderer> From for Element<'a, Message, Renderer> -where - Renderer: iced_native::Renderer + 'a, -{ - fn from(space: Space) -> Self { - Self::new(space) - } -} diff --git a/pure/src/widget/svg.rs b/pure/src/widget/svg.rs deleted file mode 100644 index ae4e8cff..00000000 --- a/pure/src/widget/svg.rs +++ /dev/null @@ -1,65 +0,0 @@ -//! Display vector graphics in your application. -use crate::widget::{Tree, Widget}; -use crate::Element; - -use iced_native::layout::{self, Layout}; -use iced_native::renderer; -use iced_native::widget::svg; -use iced_native::{Length, Point, Rectangle}; - -pub use iced_native::svg::Handle; -pub use svg::Svg; - -impl Widget for Svg -where - Renderer: iced_native::svg::Renderer, -{ - fn width(&self) -> Length { - >::width(self) - } - - fn height(&self) -> Length { - >::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - >::layout( - self, renderer, limits, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - >::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } -} - -impl<'a, Message, Renderer> From for Element<'a, Message, Renderer> -where - Message: Clone + 'a, - Renderer: iced_native::svg::Renderer + 'a, -{ - fn from(svg: Svg) -> Self { - Self::new(svg) - } -} diff --git a/pure/src/widget/text.rs b/pure/src/widget/text.rs deleted file mode 100644 index 7c6f6ce9..00000000 --- a/pure/src/widget/text.rs +++ /dev/null @@ -1,77 +0,0 @@ -//! Write some text for your users to read. -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::layout::{self, Layout}; -use iced_native::renderer; -use iced_native::text; -use iced_native::widget; -use iced_native::{Length, Point, Rectangle}; - -pub use iced_native::widget::text::{Appearance, StyleSheet, Text}; - -impl Widget for Text -where - Renderer: text::Renderer, - Renderer::Theme: widget::text::StyleSheet, -{ - fn width(&self) -> Length { - >::width(self) - } - - fn height(&self) -> Length { - >::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - >::layout( - self, renderer, limits, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - >::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Renderer: text::Renderer + 'a, - Renderer::Theme: widget::text::StyleSheet, -{ - fn from(text: Text) -> Self { - Self::new(text) - } -} - -impl<'a, Message, Renderer> From<&'a str> for Element<'a, Message, Renderer> -where - Renderer: text::Renderer + 'a, - Renderer::Theme: widget::text::StyleSheet, -{ - fn from(contents: &'a str) -> Self { - Text::new(contents).into() - } -} diff --git a/pure/src/widget/text_input.rs b/pure/src/widget/text_input.rs deleted file mode 100644 index 514a6795..00000000 --- a/pure/src/widget/text_input.rs +++ /dev/null @@ -1,285 +0,0 @@ -//! Display fields that can be filled with text. -use crate::widget::tree::{self, Tree}; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::text; -use iced_native::widget::text_input; -use iced_native::{Clipboard, Length, Padding, Point, Rectangle, Shell}; - -pub use iced_style::text_input::{Appearance, StyleSheet}; - -/// A field that can be filled with text. -/// -/// # Example -/// ``` -/// # pub type TextInput<'a, Message> = iced_pure::widget::TextInput<'a, Message, iced_native::renderer::Null>; -/// #[derive(Debug, Clone)] -/// enum Message { -/// TextInputChanged(String), -/// } -/// -/// let value = "Some text"; -/// -/// let input = TextInput::new( -/// "This is the placeholder...", -/// value, -/// Message::TextInputChanged, -/// ) -/// .padding(10); -/// ``` -/// ![Text input drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true) -#[allow(missing_debug_implementations)] -pub struct TextInput<'a, Message, Renderer> -where - Renderer: text::Renderer, - Renderer::Theme: StyleSheet, -{ - placeholder: String, - value: text_input::Value, - is_secure: bool, - font: Renderer::Font, - width: Length, - padding: Padding, - size: Option, - on_change: Box Message + 'a>, - on_paste: Option Message + 'a>>, - on_submit: Option, - style: ::Style, -} - -impl<'a, Message, Renderer> TextInput<'a, Message, Renderer> -where - Message: Clone, - Renderer: text::Renderer, - Renderer::Theme: StyleSheet, -{ - /// Creates a new [`TextInput`]. - /// - /// It expects: - /// - a placeholder, - /// - the current value, and - /// - a function that produces a message when the [`TextInput`] changes. - pub fn new(placeholder: &str, value: &str, on_change: F) -> Self - where - F: 'a + Fn(String) -> Message, - { - TextInput { - placeholder: String::from(placeholder), - value: text_input::Value::new(value), - is_secure: false, - font: Default::default(), - width: Length::Fill, - padding: Padding::ZERO, - size: None, - on_change: Box::new(on_change), - on_paste: None, - on_submit: None, - style: Default::default(), - } - } - - /// Converts the [`TextInput`] into a secure password input. - pub fn password(mut self) -> Self { - self.is_secure = true; - self - } - - /// Sets the message that should be produced when some text is pasted into - /// the [`TextInput`]. - pub fn on_paste( - mut self, - on_paste: impl Fn(String) -> Message + 'a, - ) -> Self { - self.on_paste = Some(Box::new(on_paste)); - self - } - - /// Sets the [`Font`] of the [`TextInput`]. - /// - /// [`Font`]: text::Renderer::Font - pub fn font(mut self, font: Renderer::Font) -> Self { - self.font = font; - self - } - /// Sets the width of the [`TextInput`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the [`Padding`] of the [`TextInput`]. - pub fn padding>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the text size of the [`TextInput`]. - pub fn size(mut self, size: u16) -> Self { - self.size = Some(size); - self - } - - /// Sets the message that should be produced when the [`TextInput`] is - /// focused and the enter key is pressed. - pub fn on_submit(mut self, message: Message) -> Self { - self.on_submit = Some(message); - self - } - - /// Sets the style of the [`TextInput`]. - pub fn style( - mut self, - style: impl Into<::Style>, - ) -> Self { - self.style = style.into(); - self - } - - /// Draws the [`TextInput`] with the given [`Renderer`], overriding its - /// [`text_input::Value`] if provided. - /// - /// [`Renderer`]: text::Renderer - pub fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - layout: Layout<'_>, - cursor_position: Point, - value: Option<&text_input::Value>, - ) { - text_input::draw( - renderer, - theme, - layout, - cursor_position, - tree.state.downcast_ref::(), - value.unwrap_or(&self.value), - &self.placeholder, - self.size, - &self.font, - self.is_secure, - self.style, - ) - } -} - -impl<'a, Message, Renderer> Widget - for TextInput<'a, Message, Renderer> -where - Message: Clone, - Renderer: text::Renderer, - Renderer::Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::new(text_input::State::new()) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - Length::Shrink - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - text_input::layout( - renderer, - limits, - self.width, - self.padding, - self.size, - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - text_input::update( - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - &mut self.value, - self.size, - &self.font, - self.is_secure, - self.on_change.as_ref(), - self.on_paste.as_deref(), - &self.on_submit, - || tree.state.downcast_mut::(), - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - _style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - ) { - text_input::draw( - renderer, - theme, - layout, - cursor_position, - tree.state.downcast_ref::(), - &self.value, - &self.placeholder, - self.size, - &self.font, - self.is_secure, - self.style, - ) - } - - fn mouse_interaction( - &self, - _state: &Tree, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - _renderer: &Renderer, - ) -> mouse::Interaction { - text_input::mouse_interaction(layout, cursor_position) - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Message: 'a + Clone, - Renderer: 'a + text::Renderer, - Renderer::Theme: StyleSheet, -{ - fn from( - text_input: TextInput<'a, Message, Renderer>, - ) -> Element<'a, Message, Renderer> { - Element::new(text_input) - } -} diff --git a/pure/src/widget/toggler.rs b/pure/src/widget/toggler.rs deleted file mode 100644 index 8d0044d2..00000000 --- a/pure/src/widget/toggler.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! Show toggle controls using togglers. -use crate::widget::{Tree, Widget}; -use crate::Element; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::text; -use iced_native::widget; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -pub use iced_native::widget::toggler::{Appearance, StyleSheet, Toggler}; - -impl<'a, Message, Renderer> Widget - for Toggler<'a, Message, Renderer> -where - Renderer: text::Renderer, - Renderer::Theme: StyleSheet + widget::text::StyleSheet, -{ - fn width(&self) -> Length { - >::width(self) - } - - fn height(&self) -> Length { - >::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - >::layout( - self, renderer, limits, - ) - } - - fn draw( - &self, - _state: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - >::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } - - fn mouse_interaction( - &self, - _state: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - >::mouse_interaction( - self, - layout, - cursor_position, - viewport, - renderer, - ) - } - - fn on_event( - &mut self, - _state: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - >::on_event( - self, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: text::Renderer + 'a, - Renderer::Theme: StyleSheet + widget::text::StyleSheet, -{ - fn from(toggler: Toggler<'a, Message, Renderer>) -> Self { - Self::new(toggler) - } -} diff --git a/pure/src/widget/tooltip.rs b/pure/src/widget/tooltip.rs deleted file mode 100644 index cbc34722..00000000 --- a/pure/src/widget/tooltip.rs +++ /dev/null @@ -1,240 +0,0 @@ -//! Display a widget over another. -use crate::widget::Tree; -use crate::{Element, Widget}; -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::overlay; -use iced_native::renderer; -use iced_native::text; -use iced_native::widget::container; -use iced_native::widget::tooltip; -use iced_native::widget::{self, Text}; -use iced_native::{Clipboard, Layout, Length, Point, Rectangle, Shell}; - -pub use iced_style::container::{Appearance, StyleSheet}; -pub use tooltip::Position; - -/// An element to display a widget over another. -#[allow(missing_debug_implementations)] -pub struct Tooltip<'a, Message, Renderer: text::Renderer> -where - Renderer: text::Renderer, - Renderer::Theme: container::StyleSheet + widget::text::StyleSheet, -{ - content: Element<'a, Message, Renderer>, - tooltip: Text, - position: Position, - gap: u16, - padding: u16, - style: ::Style, -} - -impl<'a, Message, Renderer> Tooltip<'a, Message, Renderer> -where - Renderer: text::Renderer, - Renderer::Theme: container::StyleSheet + widget::text::StyleSheet, -{ - /// The default padding of a [`Tooltip`] drawn by this renderer. - const DEFAULT_PADDING: u16 = 5; - - /// Creates a new [`Tooltip`]. - /// - /// [`Tooltip`]: struct.Tooltip.html - pub fn new( - content: impl Into>, - tooltip: impl ToString, - position: Position, - ) -> Self { - Tooltip { - content: content.into(), - tooltip: Text::new(tooltip.to_string()), - position, - gap: 0, - padding: Self::DEFAULT_PADDING, - style: Default::default(), - } - } - - /// Sets the size of the text of the [`Tooltip`]. - pub fn size(mut self, size: u16) -> Self { - self.tooltip = self.tooltip.size(size); - self - } - - /// Sets the font of the [`Tooltip`]. - /// - /// [`Font`]: Renderer::Font - pub fn font(mut self, font: impl Into) -> Self { - self.tooltip = self.tooltip.font(font); - self - } - - /// Sets the gap between the content and its [`Tooltip`]. - pub fn gap(mut self, gap: u16) -> Self { - self.gap = gap; - self - } - - /// Sets the padding of the [`Tooltip`]. - pub fn padding(mut self, padding: u16) -> Self { - self.padding = padding; - self - } - - /// Sets the style of the [`Tooltip`]. - pub fn style( - mut self, - style: impl Into<::Style>, - ) -> Self { - self.style = style.into(); - self - } -} - -impl<'a, Message, Renderer> Widget - for Tooltip<'a, Message, Renderer> -where - Renderer: text::Renderer, - Renderer::Theme: container::StyleSheet + widget::text::StyleSheet, -{ - fn children(&self) -> Vec { - vec![Tree::new(&self.content)] - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children(std::slice::from_ref(&self.content)) - } - - fn width(&self) -> Length { - self.content.as_widget().width() - } - - fn height(&self) -> Length { - self.content.as_widget().height() - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - self.content.as_widget().layout(renderer, limits) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - self.content.as_widget_mut().on_event( - &mut tree.children[0], - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - self.content.as_widget().mouse_interaction( - &tree.children[0], - layout.children().next().unwrap(), - cursor_position, - viewport, - renderer, - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - inherited_style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - self.content.as_widget().draw( - &tree.children[0], - renderer, - theme, - inherited_style, - layout, - cursor_position, - viewport, - ); - - let tooltip = &self.tooltip; - - tooltip::draw( - renderer, - theme, - inherited_style, - layout, - cursor_position, - viewport, - self.position, - self.gap, - self.padding, - self.style, - |renderer, limits| { - Widget::<(), Renderer>::layout(tooltip, renderer, limits) - }, - |renderer, defaults, layout, cursor_position, viewport| { - Widget::<(), Renderer>::draw( - tooltip, - &Tree::empty(), - renderer, - theme, - defaults, - layout, - cursor_position, - viewport, - ); - }, - ); - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option> { - self.content.as_widget().overlay( - &mut tree.children[0], - layout, - renderer, - ) - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: 'a + text::Renderer, - Renderer::Theme: container::StyleSheet + widget::text::StyleSheet, -{ - fn from( - tooltip: Tooltip<'a, Message, Renderer>, - ) -> Element<'a, Message, Renderer> { - Element::new(tooltip) - } -} diff --git a/pure/src/widget/tree.rs b/pure/src/widget/tree.rs deleted file mode 100644 index 2f876523..00000000 --- a/pure/src/widget/tree.rs +++ /dev/null @@ -1,176 +0,0 @@ -//! Store internal widget state in a state tree to ensure continuity. -use crate::Widget; - -use std::any::{self, Any}; -use std::borrow::Borrow; - -/// A persistent state widget tree. -/// -/// A [`Tree`] is normally associated with a specific widget in the widget tree. -pub struct Tree { - /// The tag of the [`Tree`]. - pub tag: Tag, - - /// The [`State`] of the [`Tree`]. - pub state: State, - - /// The children of the root widget of the [`Tree`]. - pub children: Vec, -} - -impl Tree { - /// Creates an empty, stateless [`Tree`] with no children. - pub fn empty() -> Self { - Self { - tag: Tag::stateless(), - state: State::None, - children: Vec::new(), - } - } - - /// Creates a new [`Tree`] for the provided [`Element`]. - pub fn new<'a, Message, Renderer>( - widget: impl Borrow + 'a>, - ) -> Self - where - Renderer: iced_native::Renderer, - { - let widget = widget.borrow(); - - Self { - tag: widget.tag(), - state: widget.state(), - children: widget.children(), - } - } - - /// Reconciliates the current tree with the provided [`Element`]. - /// - /// If the tag of the [`Element`] matches the tag of the [`Tree`], then the - /// [`Element`] proceeds with the reconciliation (i.e. [`Widget::diff`] is called). - /// - /// Otherwise, the whole [`Tree`] is recreated. - /// - /// [`Widget::diff`]: crate::Widget::diff - pub fn diff<'a, Message, Renderer>( - &mut self, - new: impl Borrow + 'a>, - ) where - Renderer: iced_native::Renderer, - { - if self.tag == new.borrow().tag() { - new.borrow().diff(self) - } else { - *self = Self::new(new); - } - } - - /// Reconciliates the children of the tree with the provided list of [`Element`]. - pub fn diff_children<'a, Message, Renderer>( - &mut self, - new_children: &[impl Borrow + 'a>], - ) where - Renderer: iced_native::Renderer, - { - self.diff_children_custom( - new_children, - |tree, widget| tree.diff(widget.borrow()), - |widget| Self::new(widget.borrow()), - ) - } - - /// Reconciliates the children of the tree with the provided list of [`Element`] using custom - /// logic both for diffing and creating new widget state. - pub fn diff_children_custom( - &mut self, - new_children: &[T], - diff: impl Fn(&mut Tree, &T), - new_state: impl Fn(&T) -> Self, - ) { - if self.children.len() > new_children.len() { - self.children.truncate(new_children.len()); - } - - for (child_state, new) in - self.children.iter_mut().zip(new_children.iter()) - { - diff(child_state, new); - } - - if self.children.len() < new_children.len() { - self.children.extend( - new_children[self.children.len()..].iter().map(new_state), - ); - } - } -} - -/// The identifier of some widget state. -#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] -pub struct Tag(any::TypeId); - -impl Tag { - /// Creates a [`Tag`] for a state of type `T`. - pub fn of() -> Self - where - T: 'static, - { - Self(any::TypeId::of::()) - } - - /// Creates a [`Tag`] for a stateless widget. - pub fn stateless() -> Self { - Self::of::<()>() - } -} - -/// The internal [`State`] of a widget. -pub enum State { - /// No meaningful internal state. - None, - - /// Some meaningful internal state. - Some(Box), -} - -impl State { - /// Creates a new [`State`]. - pub fn new(state: T) -> Self - where - T: 'static, - { - State::Some(Box::new(state)) - } - - /// Downcasts the [`State`] to `T` and returns a reference to it. - /// - /// # Panics - /// This method will panic if the downcast fails or the [`State`] is [`State::None`]. - pub fn downcast_ref(&self) -> &T - where - T: 'static, - { - match self { - State::None => panic!("Downcast on stateless state"), - State::Some(state) => { - state.downcast_ref().expect("Downcast widget state") - } - } - } - - /// Downcasts the [`State`] to `T` and returns a mutable reference to it. - /// - /// # Panics - /// This method will panic if the downcast fails or the [`State`] is [`State::None`]. - pub fn downcast_mut(&mut self) -> &mut T - where - T: 'static, - { - match self { - State::None => panic!("Downcast on stateless state"), - State::Some(state) => { - state.downcast_mut().expect("Downcast widget state") - } - } - } -} -- cgit