diff --git a/glow/src/widget/pane_grid.rs b/glow/src/widget/pane_grid.rs index c26dde48..44f9201c 100644 --- a/glow/src/widget/pane_grid.rs +++ b/glow/src/widget/pane_grid.rs @@ -9,9 +9,9 @@ //! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.2/examples/pane_grid use crate::Renderer; -pub use iced_native::pane_grid::{ - Axis, Configuration, Direction, DragEvent, Node, Pane, ResizeEvent, Split, - State, +pub use iced_graphics::pane_grid::{ + Axis, Configuration, Direction, DragEvent, Line, Node, Pane, ResizeEvent, + Split, State, StyleSheet, }; /// A collection of panes distributed using either vertical or horizontal splits diff --git a/graphics/src/widget/pane_grid.rs b/graphics/src/widget/pane_grid.rs index 29478447..3cd4fd34 100644 --- a/graphics/src/widget/pane_grid.rs +++ b/graphics/src/widget/pane_grid.rs @@ -8,16 +8,19 @@ //! //! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.2/examples/pane_grid use crate::defaults; -use crate::{Backend, Primitive, Renderer}; +use crate::{Backend, Color, Primitive, Renderer}; +use iced_native::container; use iced_native::mouse; use iced_native::pane_grid; use iced_native::{Element, Layout, Point, Rectangle, Vector}; pub use iced_native::pane_grid::{ - Axis, Configuration, Content, Direction, DragEvent, Pane, ResizeEvent, - Split, State, TitleBar, + Axis, Configuration, Content, Direction, DragEvent, Node, Pane, + ResizeEvent, Split, State, TitleBar, }; +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. /// @@ -31,13 +34,16 @@ impl pane_grid::Renderer for Renderer where B: Backend, { + type Style = Box; + fn draw( &mut self, defaults: &Self::Defaults, content: &[(Pane, Content<'_, Message, Self>)], dragging: Option<(Pane, Point)>, - resizing: Option, + resizing: Option<(Axis, Rectangle, bool)>, layout: Layout<'_>, + style_sheet: &::Style, cursor_position: Point, ) -> Self::Output { let pane_cursor_position = if dragging.is_some() { @@ -73,7 +79,8 @@ where }) .collect(); - let primitives = if let Some((index, layout, origin)) = dragged_pane { + let mut primitives = if let Some((index, layout, origin)) = dragged_pane + { let pane = panes.remove(index); let bounds = layout.bounds(); @@ -103,15 +110,62 @@ where panes }; + let (primitives, mouse_interaction) = + if let Some((axis, split_region, is_picked)) = resizing { + let highlight = if is_picked { + style_sheet.picked_split() + } else { + style_sheet.hovered_split() + }; + + if let Some(highlight) = highlight { + primitives.push(Primitive::Quad { + bounds: match axis { + Axis::Horizontal => Rectangle { + x: split_region.x, + y: (split_region.y + + (split_region.height - highlight.width) + / 2.0) + .round(), + width: split_region.width, + height: highlight.width, + }, + Axis::Vertical => Rectangle { + x: (split_region.x + + (split_region.width - highlight.width) + / 2.0) + .round(), + y: split_region.y, + width: highlight.width, + height: split_region.height, + }, + }, + background: highlight.color.into(), + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }); + } + + ( + primitives, + match axis { + Axis::Horizontal => { + mouse::Interaction::ResizingVertically + } + Axis::Vertical => { + mouse::Interaction::ResizingHorizontally + } + }, + ) + } else { + (primitives, mouse_interaction) + }; + ( Primitive::Group { primitives }, if dragging.is_some() { mouse::Interaction::Grabbing - } else if let Some(axis) = resizing { - match axis { - Axis::Horizontal => mouse::Interaction::ResizingVertically, - Axis::Vertical => mouse::Interaction::ResizingHorizontally, - } } else { mouse_interaction }, @@ -122,7 +176,7 @@ where &mut self, defaults: &Self::Defaults, bounds: Rectangle, - style_sheet: &Self::Style, + style_sheet: &::Style, title_bar: Option<(&TitleBar<'_, Message, Self>, Layout<'_>)>, body: (&Element<'_, Message, Self>, Layout<'_>), cursor_position: Point, @@ -182,7 +236,7 @@ where &mut self, defaults: &Self::Defaults, bounds: Rectangle, - style_sheet: &Self::Style, + style_sheet: &::Style, content: (&Element<'_, Message, Self>, Layout<'_>), controls: Option<(&Element<'_, Message, Self>, Layout<'_>)>, cursor_position: Point, diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index bea8041c..f505b012 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -246,13 +246,16 @@ impl container::Renderer for Null { } impl pane_grid::Renderer for Null { + type Style = (); + fn draw( &mut self, _defaults: &Self::Defaults, _content: &[(pane_grid::Pane, pane_grid::Content<'_, Message, Self>)], _dragging: Option<(pane_grid::Pane, Point)>, - _resizing: Option, + _resizing: Option<(pane_grid::Axis, Rectangle, bool)>, _layout: Layout<'_>, + _style: &::Style, _cursor_position: Point, ) { } @@ -261,7 +264,7 @@ impl pane_grid::Renderer for Null { &mut self, _defaults: &Self::Defaults, _bounds: Rectangle, - _style: &Self::Style, + _style: &::Style, _title_bar: Option<( &pane_grid::TitleBar<'_, Message, Self>, Layout<'_>, @@ -275,7 +278,7 @@ impl pane_grid::Renderer for Null { &mut self, _defaults: &Self::Defaults, _bounds: Rectangle, - _style: &Self::Style, + _style: &::Style, _content: (&Element<'_, Message, Self>, Layout<'_>), _controls: Option<(&Element<'_, Message, Self>, Layout<'_>)>, _cursor_position: Point, diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 9cf8bc34..da3e25fd 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -97,6 +97,7 @@ pub struct PaneGrid<'a, Message, Renderer: self::Renderer> { 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> @@ -128,6 +129,7 @@ where on_click: None, on_drag: None, on_resize: None, + style: Default::default(), } } @@ -185,6 +187,15 @@ where 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> PaneGrid<'a, Message, Renderer> @@ -382,7 +393,7 @@ where relative_cursor, ); - if let Some((split, axis)) = clicked_split { + if let Some((split, axis, _)) = clicked_split { self.state.pick_split(&split, axis); } else { self.click_pane( @@ -475,6 +486,23 @@ where let picked_split = self .state .picked_split() + .and_then(|(split, axis)| { + let bounds = layout.bounds(); + + let splits = self + .state + .split_regions(f32::from(self.spacing), bounds.size()); + + let (_axis, region, ratio) = splits.get(&split)?; + + let region = axis.split_line_bounds( + *region, + *ratio, + f32::from(self.spacing), + ); + + Some((axis, region + Vector::new(bounds.x, bounds.y), true)) + }) .or_else(|| match self.on_resize { Some((leeway, _)) => { let bounds = layout.bounds(); @@ -488,15 +516,20 @@ where .state .split_regions(f32::from(self.spacing), bounds.size()); - hovered_split( + let (_split, axis, region) = hovered_split( splits.iter(), f32::from(self.spacing + leeway), relative_cursor, - ) + )?; + + Some(( + axis, + region + Vector::new(bounds.x, bounds.y), + false, + )) } None => None, - }) - .map(|(_, axis)| axis); + }); self::Renderer::draw( renderer, @@ -505,6 +538,7 @@ where self.state.picked_pane(), picked_split, layout, + &self.style, cursor_position, ) } @@ -543,6 +577,9 @@ where /// /// [renderer]: crate::renderer pub trait Renderer: crate::Renderer + container::Renderer + Sized { + /// The style supported by this renderer. + type Style: Default; + /// Draws a [`PaneGrid`]. /// /// It receives: @@ -556,8 +593,9 @@ pub trait Renderer: crate::Renderer + container::Renderer + Sized { defaults: &Self::Defaults, content: &[(Pane, Content<'_, Message, Self>)], dragging: Option<(Pane, Point)>, - resizing: Option, + resizing: Option<(Axis, Rectangle, bool)>, layout: Layout<'_>, + style: &::Style, cursor_position: Point, ) -> Self::Output; @@ -572,7 +610,7 @@ pub trait Renderer: crate::Renderer + container::Renderer + Sized { &mut self, defaults: &Self::Defaults, bounds: Rectangle, - style: &Self::Style, + style: &::Style, title_bar: Option<(&TitleBar<'_, Message, Self>, Layout<'_>)>, body: (&Element<'_, Message, Self>, Layout<'_>), cursor_position: Point, @@ -590,7 +628,7 @@ pub trait Renderer: crate::Renderer + container::Renderer + Sized { &mut self, defaults: &Self::Defaults, bounds: Rectangle, - style: &Self::Style, + style: &::Style, content: (&Element<'_, Message, Self>, Layout<'_>), controls: Option<(&Element<'_, Message, Self>, Layout<'_>)>, cursor_position: Point, @@ -617,14 +655,14 @@ fn hovered_split<'a>( splits: impl Iterator, spacing: f32, cursor_position: Point, -) -> Option<(Split, Axis)> { +) -> Option<(Split, Axis, Rectangle)> { splits .filter_map(|(split, (axis, region, ratio))| { let bounds = axis.split_line_bounds(*region, *ratio, f32::from(spacing)); if bounds.contains(cursor_position) { - Some((*split, *axis)) + Some((*split, *axis, bounds)) } else { None } diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs index c9981903..913cfe96 100644 --- a/native/src/widget/pane_grid/content.rs +++ b/native/src/widget/pane_grid/content.rs @@ -12,7 +12,7 @@ use crate::{Clipboard, Element, Hasher, Layout, Point, Size}; pub struct Content<'a, Message, Renderer: pane_grid::Renderer> { title_bar: Option>, body: Element<'a, Message, Renderer>, - style: Renderer::Style, + style: ::Style, } impl<'a, Message, Renderer> Content<'a, Message, Renderer> @@ -24,7 +24,7 @@ where Self { title_bar: None, body: body.into(), - style: Renderer::Style::default(), + style: Default::default(), } } @@ -38,7 +38,10 @@ where } /// Sets the style of the [`Content`]. - pub fn style(mut self, style: impl Into) -> Self { + pub fn style( + mut self, + style: impl Into<::Style>, + ) -> Self { self.style = style.into(); self } diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs index 30e88e6f..efaecf9e 100644 --- a/native/src/widget/pane_grid/title_bar.rs +++ b/native/src/widget/pane_grid/title_bar.rs @@ -1,3 +1,4 @@ +use crate::container; use crate::event::{self, Event}; use crate::layout; use crate::pane_grid; @@ -12,7 +13,7 @@ pub struct TitleBar<'a, Message, Renderer: pane_grid::Renderer> { controls: Option>, padding: u16, always_show_controls: bool, - style: Renderer::Style, + style: ::Style, } impl<'a, Message, Renderer> TitleBar<'a, Message, Renderer> @@ -29,7 +30,7 @@ where controls: None, padding: 0, always_show_controls: false, - style: Renderer::Style::default(), + style: Default::default(), } } @@ -49,7 +50,10 @@ where } /// Sets the style of the [`TitleBar`]. - pub fn style(mut self, style: impl Into) -> Self { + pub fn style( + mut self, + style: impl Into<::Style>, + ) -> Self { self.style = style.into(); self } diff --git a/style/src/lib.rs b/style/src/lib.rs index 7e0a9f49..f09b5f9d 100644 --- a/style/src/lib.rs +++ b/style/src/lib.rs @@ -10,6 +10,7 @@ pub mod button; pub mod checkbox; pub mod container; pub mod menu; +pub mod pane_grid; pub mod pick_list; pub mod progress_bar; pub mod radio; diff --git a/style/src/pane_grid.rs b/style/src/pane_grid.rs new file mode 100644 index 00000000..e39ee797 --- /dev/null +++ b/style/src/pane_grid.rs @@ -0,0 +1,51 @@ +//! Let your users split regions of your application and organize layout +//! dynamically. +use iced_core::Color; + +/// A set of rules that dictate the style of a container. +pub trait StyleSheet { + /// The [`Line`] to draw when a split is picked. + fn picked_split(&self) -> Option; + + /// The [`Line`] to draw when a split is hovered. + fn hovered_split(&self) -> Option; +} + +/// A line. +/// +/// It is normally used to define the highlight of something, like a split. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Line { + /// The [`Color`] of the [`Line`]. + pub color: Color, + + /// The width of the [`Line`]. + pub width: f32, +} + +struct Default; + +impl StyleSheet for Default { + fn picked_split(&self) -> Option { + None + } + + fn hovered_split(&self) -> Option { + None + } +} + +impl std::default::Default for Box { + fn default() -> Self { + Box::new(Default) + } +} + +impl From for Box +where + T: 'static + StyleSheet, +{ + fn from(style: T) -> Self { + Box::new(style) + } +} diff --git a/wgpu/src/widget/pane_grid.rs b/wgpu/src/widget/pane_grid.rs index c26dde48..44f9201c 100644 --- a/wgpu/src/widget/pane_grid.rs +++ b/wgpu/src/widget/pane_grid.rs @@ -9,9 +9,9 @@ //! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.2/examples/pane_grid use crate::Renderer; -pub use iced_native::pane_grid::{ - Axis, Configuration, Direction, DragEvent, Node, Pane, ResizeEvent, Split, - State, +pub use iced_graphics::pane_grid::{ + Axis, Configuration, Direction, DragEvent, Line, Node, Pane, ResizeEvent, + Split, State, StyleSheet, }; /// A collection of panes distributed using either vertical or horizontal splits