diff options
Diffstat (limited to 'native/src/widget/pane_grid')
-rw-r--r-- | native/src/widget/pane_grid/axis.rs | 241 | ||||
-rw-r--r-- | native/src/widget/pane_grid/configuration.rs | 26 | ||||
-rw-r--r-- | native/src/widget/pane_grid/content.rs | 373 | ||||
-rw-r--r-- | native/src/widget/pane_grid/direction.rs | 12 | ||||
-rw-r--r-- | native/src/widget/pane_grid/draggable.rs | 12 | ||||
-rw-r--r-- | native/src/widget/pane_grid/node.rs | 250 | ||||
-rw-r--r-- | native/src/widget/pane_grid/pane.rs | 5 | ||||
-rw-r--r-- | native/src/widget/pane_grid/split.rs | 5 | ||||
-rw-r--r-- | native/src/widget/pane_grid/state.rs | 350 | ||||
-rw-r--r-- | native/src/widget/pane_grid/title_bar.rs | 432 |
10 files changed, 0 insertions, 1706 deletions
diff --git a/native/src/widget/pane_grid/axis.rs b/native/src/widget/pane_grid/axis.rs deleted file mode 100644 index 02bde064..00000000 --- a/native/src/widget/pane_grid/axis.rs +++ /dev/null @@ -1,241 +0,0 @@ -use crate::Rectangle; - -/// A fixed reference line for the measurement of coordinates. -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] -pub enum Axis { - /// The horizontal axis: — - Horizontal, - /// The vertical axis: | - Vertical, -} - -impl Axis { - /// Splits the provided [`Rectangle`] on the current [`Axis`] with the - /// given `ratio` and `spacing`. - pub fn split( - &self, - rectangle: &Rectangle, - ratio: f32, - spacing: f32, - ) -> (Rectangle, Rectangle) { - match self { - Axis::Horizontal => { - let height_top = - (rectangle.height * ratio - spacing / 2.0).round(); - let height_bottom = rectangle.height - height_top - spacing; - - ( - Rectangle { - height: height_top, - ..*rectangle - }, - Rectangle { - y: rectangle.y + height_top + spacing, - height: height_bottom, - ..*rectangle - }, - ) - } - Axis::Vertical => { - let width_left = - (rectangle.width * ratio - spacing / 2.0).round(); - let width_right = rectangle.width - width_left - spacing; - - ( - Rectangle { - width: width_left, - ..*rectangle - }, - Rectangle { - x: rectangle.x + width_left + spacing, - width: width_right, - ..*rectangle - }, - ) - } - } - } - - /// Calculates the bounds of the split line in a [`Rectangle`] region. - pub fn split_line_bounds( - &self, - rectangle: Rectangle, - ratio: f32, - spacing: f32, - ) -> Rectangle { - match self { - Axis::Horizontal => Rectangle { - x: rectangle.x, - y: (rectangle.y + rectangle.height * ratio - spacing / 2.0) - .round(), - width: rectangle.width, - height: spacing, - }, - Axis::Vertical => Rectangle { - x: (rectangle.x + rectangle.width * ratio - spacing / 2.0) - .round(), - y: rectangle.y, - width: spacing, - height: rectangle.height, - }, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - enum Case { - Horizontal { - overall_height: f32, - spacing: f32, - top_height: f32, - bottom_y: f32, - bottom_height: f32, - }, - Vertical { - overall_width: f32, - spacing: f32, - left_width: f32, - right_x: f32, - right_width: f32, - }, - } - - #[test] - fn split() { - let cases = vec![ - // Even height, even spacing - Case::Horizontal { - overall_height: 10.0, - spacing: 2.0, - top_height: 4.0, - bottom_y: 6.0, - bottom_height: 4.0, - }, - // Odd height, even spacing - Case::Horizontal { - overall_height: 9.0, - spacing: 2.0, - top_height: 4.0, - bottom_y: 6.0, - bottom_height: 3.0, - }, - // Even height, odd spacing - Case::Horizontal { - overall_height: 10.0, - spacing: 1.0, - top_height: 5.0, - bottom_y: 6.0, - bottom_height: 4.0, - }, - // Odd height, odd spacing - Case::Horizontal { - overall_height: 9.0, - spacing: 1.0, - top_height: 4.0, - bottom_y: 5.0, - bottom_height: 4.0, - }, - // Even width, even spacing - Case::Vertical { - overall_width: 10.0, - spacing: 2.0, - left_width: 4.0, - right_x: 6.0, - right_width: 4.0, - }, - // Odd width, even spacing - Case::Vertical { - overall_width: 9.0, - spacing: 2.0, - left_width: 4.0, - right_x: 6.0, - right_width: 3.0, - }, - // Even width, odd spacing - Case::Vertical { - overall_width: 10.0, - spacing: 1.0, - left_width: 5.0, - right_x: 6.0, - right_width: 4.0, - }, - // Odd width, odd spacing - Case::Vertical { - overall_width: 9.0, - spacing: 1.0, - left_width: 4.0, - right_x: 5.0, - right_width: 4.0, - }, - ]; - for case in cases { - match case { - Case::Horizontal { - overall_height, - spacing, - top_height, - bottom_y, - bottom_height, - } => { - let a = Axis::Horizontal; - let r = Rectangle { - x: 0.0, - y: 0.0, - width: 10.0, - height: overall_height, - }; - let (top, bottom) = a.split(&r, 0.5, spacing); - assert_eq!( - top, - Rectangle { - height: top_height, - ..r - } - ); - assert_eq!( - bottom, - Rectangle { - y: bottom_y, - height: bottom_height, - ..r - } - ); - } - Case::Vertical { - overall_width, - spacing, - left_width, - right_x, - right_width, - } => { - let a = Axis::Vertical; - let r = Rectangle { - x: 0.0, - y: 0.0, - width: overall_width, - height: 10.0, - }; - let (left, right) = a.split(&r, 0.5, spacing); - assert_eq!( - left, - Rectangle { - width: left_width, - ..r - } - ); - assert_eq!( - right, - Rectangle { - x: right_x, - width: right_width, - ..r - } - ); - } - } - } - } -} diff --git a/native/src/widget/pane_grid/configuration.rs b/native/src/widget/pane_grid/configuration.rs deleted file mode 100644 index 7d68fb46..00000000 --- a/native/src/widget/pane_grid/configuration.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::widget::pane_grid::Axis; - -/// The arrangement of a [`PaneGrid`]. -/// -/// [`PaneGrid`]: crate::widget::PaneGrid -#[derive(Debug, Clone)] -pub enum Configuration<T> { - /// A split of the available space. - Split { - /// The direction of the split. - axis: Axis, - - /// The ratio of the split in [0.0, 1.0]. - ratio: f32, - - /// The left/top [`Configuration`] of the split. - a: Box<Configuration<T>>, - - /// The right/bottom [`Configuration`] of the split. - b: Box<Configuration<T>>, - }, - /// A [`Pane`]. - /// - /// [`Pane`]: crate::widget::pane_grid::Pane - Pane(T), -} diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs deleted file mode 100644 index c9b0df07..00000000 --- a/native/src/widget/pane_grid/content.rs +++ /dev/null @@ -1,373 +0,0 @@ -use crate::event::{self, Event}; -use crate::layout; -use crate::mouse; -use crate::overlay; -use crate::renderer; -use crate::widget::container; -use crate::widget::pane_grid::{Draggable, TitleBar}; -use crate::widget::{self, Tree}; -use crate::{Clipboard, Element, 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: crate::Renderer, - Renderer::Theme: container::StyleSheet, -{ - title_bar: Option<TitleBar<'a, Message, Renderer>>, - body: Element<'a, Message, Renderer>, - style: <Renderer::Theme as container::StyleSheet>::Style, -} - -impl<'a, Message, Renderer> Content<'a, Message, Renderer> -where - Renderer: crate::Renderer, - Renderer::Theme: container::StyleSheet, -{ - /// Creates a new [`Content`] with the provided body. - pub fn new(body: impl Into<Element<'a, Message, Renderer>>) -> 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<<Renderer::Theme as container::StyleSheet>::Style>, - ) -> Self { - self.style = style.into(); - self - } -} - -impl<'a, Message, Renderer> Content<'a, Message, Renderer> -where - Renderer: crate::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`]: crate::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); - - self.body.as_widget().draw( - &tree.children[0], - renderer, - theme, - style, - body_layout, - cursor_position, - viewport, - ); - - title_bar.draw( - &tree.children[1], - renderer, - theme, - style, - title_bar_layout, - cursor_position, - viewport, - show_controls, - ); - } 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 operate( - &self, - tree: &mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - operation: &mut dyn widget::Operation<Message>, - ) { - let body_layout = if let Some(title_bar) = &self.title_bar { - let mut children = layout.children(); - - title_bar.operate( - &mut tree.children[1], - children.next().unwrap(), - renderer, - operation, - ); - - children.next().unwrap() - } else { - layout - }; - - self.body.as_widget().operate( - &mut tree.children[0], - body_layout, - renderer, - operation, - ); - } - - 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, - drag_enabled: bool, - ) -> 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 && drag_enabled { - 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 mut self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'b, Message, Renderer>> { - if let Some(title_bar) = self.title_bar.as_mut() { - 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_mut().overlay( - body_state, - children.next()?, - renderer, - ), - } - } else { - self.body.as_widget_mut().overlay( - &mut tree.children[0], - layout, - renderer, - ) - } - } -} - -impl<'a, Message, Renderer> Draggable for &Content<'a, Message, Renderer> -where - Renderer: crate::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<T> for Content<'a, Message, Renderer> -where - T: Into<Element<'a, Message, Renderer>>, - Renderer: crate::Renderer, - Renderer::Theme: container::StyleSheet, -{ - fn from(element: T) -> Self { - Self::new(element) - } -} diff --git a/native/src/widget/pane_grid/direction.rs b/native/src/widget/pane_grid/direction.rs deleted file mode 100644 index b31a8737..00000000 --- a/native/src/widget/pane_grid/direction.rs +++ /dev/null @@ -1,12 +0,0 @@ -/// A four cardinal direction. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Direction { - /// ↑ - Up, - /// ↓ - Down, - /// ← - Left, - /// → - Right, -} diff --git a/native/src/widget/pane_grid/draggable.rs b/native/src/widget/pane_grid/draggable.rs deleted file mode 100644 index 6044871d..00000000 --- a/native/src/widget/pane_grid/draggable.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::{Layout, Point}; - -/// A pane that can be dragged. -pub trait Draggable { - /// Returns whether the [`Draggable`] with the given [`Layout`] can be picked - /// at the provided cursor position. - fn can_be_dragged_at( - &self, - layout: Layout<'_>, - cursor_position: Point, - ) -> bool; -} diff --git a/native/src/widget/pane_grid/node.rs b/native/src/widget/pane_grid/node.rs deleted file mode 100644 index cc304b96..00000000 --- a/native/src/widget/pane_grid/node.rs +++ /dev/null @@ -1,250 +0,0 @@ -use crate::widget::pane_grid::{Axis, Pane, Split}; -use crate::{Rectangle, Size}; - -use std::collections::BTreeMap; - -/// A layout node of a [`PaneGrid`]. -/// -/// [`PaneGrid`]: crate::widget::PaneGrid -#[derive(Debug, Clone)] -pub enum Node { - /// The region of this [`Node`] is split into two. - Split { - /// The [`Split`] of this [`Node`]. - id: Split, - - /// The direction of the split. - axis: Axis, - - /// The ratio of the split in [0.0, 1.0]. - ratio: f32, - - /// The left/top [`Node`] of the split. - a: Box<Node>, - - /// The right/bottom [`Node`] of the split. - b: Box<Node>, - }, - /// The region of this [`Node`] is taken by a [`Pane`]. - Pane(Pane), -} - -impl Node { - /// Returns an iterator over each [`Split`] in this [`Node`]. - pub fn splits(&self) -> impl Iterator<Item = &Split> { - let mut unvisited_nodes = vec![self]; - - std::iter::from_fn(move || { - while let Some(node) = unvisited_nodes.pop() { - if let Node::Split { id, a, b, .. } = node { - unvisited_nodes.push(a); - unvisited_nodes.push(b); - - return Some(id); - } - } - - None - }) - } - - /// Returns the rectangular region for each [`Pane`] in the [`Node`] given - /// the spacing between panes and the total available space. - pub fn pane_regions( - &self, - spacing: f32, - size: Size, - ) -> BTreeMap<Pane, Rectangle> { - let mut regions = BTreeMap::new(); - - self.compute_regions( - spacing, - &Rectangle { - x: 0.0, - y: 0.0, - width: size.width, - height: size.height, - }, - &mut regions, - ); - - regions - } - - /// Returns the axis, rectangular region, and ratio for each [`Split`] in - /// the [`Node`] given the spacing between panes and the total available - /// space. - pub fn split_regions( - &self, - spacing: f32, - size: Size, - ) -> BTreeMap<Split, (Axis, Rectangle, f32)> { - let mut splits = BTreeMap::new(); - - self.compute_splits( - spacing, - &Rectangle { - x: 0.0, - y: 0.0, - width: size.width, - height: size.height, - }, - &mut splits, - ); - - splits - } - - pub(crate) fn find(&mut self, pane: &Pane) -> Option<&mut Node> { - match self { - Node::Split { a, b, .. } => { - a.find(pane).or_else(move || b.find(pane)) - } - Node::Pane(p) => { - if p == pane { - Some(self) - } else { - None - } - } - } - } - - pub(crate) fn split(&mut self, id: Split, axis: Axis, new_pane: Pane) { - *self = Node::Split { - id, - axis, - ratio: 0.5, - a: Box::new(self.clone()), - b: Box::new(Node::Pane(new_pane)), - }; - } - - pub(crate) fn update(&mut self, f: &impl Fn(&mut Node)) { - if let Node::Split { a, b, .. } = self { - a.update(f); - b.update(f); - } - - f(self); - } - - pub(crate) fn resize(&mut self, split: &Split, percentage: f32) -> bool { - match self { - Node::Split { - id, ratio, a, b, .. - } => { - if id == split { - *ratio = percentage; - - true - } else if a.resize(split, percentage) { - true - } else { - b.resize(split, percentage) - } - } - Node::Pane(_) => false, - } - } - - pub(crate) fn remove(&mut self, pane: &Pane) -> Option<Pane> { - match self { - Node::Split { a, b, .. } => { - if a.pane() == Some(*pane) { - *self = *b.clone(); - Some(self.first_pane()) - } else if b.pane() == Some(*pane) { - *self = *a.clone(); - Some(self.first_pane()) - } else { - a.remove(pane).or_else(|| b.remove(pane)) - } - } - Node::Pane(_) => None, - } - } - - fn pane(&self) -> Option<Pane> { - match self { - Node::Split { .. } => None, - Node::Pane(pane) => Some(*pane), - } - } - - fn first_pane(&self) -> Pane { - match self { - Node::Split { a, .. } => a.first_pane(), - Node::Pane(pane) => *pane, - } - } - - fn compute_regions( - &self, - spacing: f32, - current: &Rectangle, - regions: &mut BTreeMap<Pane, Rectangle>, - ) { - match self { - Node::Split { - axis, ratio, a, b, .. - } => { - let (region_a, region_b) = axis.split(current, *ratio, spacing); - - a.compute_regions(spacing, ®ion_a, regions); - b.compute_regions(spacing, ®ion_b, regions); - } - Node::Pane(pane) => { - let _ = regions.insert(*pane, *current); - } - } - } - - fn compute_splits( - &self, - spacing: f32, - current: &Rectangle, - splits: &mut BTreeMap<Split, (Axis, Rectangle, f32)>, - ) { - match self { - Node::Split { - axis, - ratio, - a, - b, - id, - } => { - let (region_a, region_b) = axis.split(current, *ratio, spacing); - - let _ = splits.insert(*id, (*axis, *current, *ratio)); - - a.compute_splits(spacing, ®ion_a, splits); - b.compute_splits(spacing, ®ion_b, splits); - } - Node::Pane(_) => {} - } - } -} - -impl std::hash::Hash for Node { - fn hash<H: std::hash::Hasher>(&self, state: &mut H) { - match self { - Node::Split { - id, - axis, - ratio, - a, - b, - } => { - id.hash(state); - axis.hash(state); - ((ratio * 100_000.0) as u32).hash(state); - a.hash(state); - b.hash(state); - } - Node::Pane(pane) => { - pane.hash(state); - } - } - } -} diff --git a/native/src/widget/pane_grid/pane.rs b/native/src/widget/pane_grid/pane.rs deleted file mode 100644 index d6fbab83..00000000 --- a/native/src/widget/pane_grid/pane.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// A rectangular region in a [`PaneGrid`] used to display widgets. -/// -/// [`PaneGrid`]: crate::widget::PaneGrid -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Pane(pub(super) usize); diff --git a/native/src/widget/pane_grid/split.rs b/native/src/widget/pane_grid/split.rs deleted file mode 100644 index 8132272a..00000000 --- a/native/src/widget/pane_grid/split.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// A divider that splits a region in a [`PaneGrid`] into two different panes. -/// -/// [`PaneGrid`]: crate::widget::PaneGrid -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Split(pub(super) usize); diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs deleted file mode 100644 index c4ae0a0e..00000000 --- a/native/src/widget/pane_grid/state.rs +++ /dev/null @@ -1,350 +0,0 @@ -//! The state of a [`PaneGrid`]. -//! -//! [`PaneGrid`]: crate::widget::PaneGrid -use crate::widget::pane_grid::{ - Axis, Configuration, Direction, Node, Pane, Split, -}; -use crate::{Point, Size}; - -use std::collections::HashMap; - -/// The state of a [`PaneGrid`]. -/// -/// It keeps track of the state of each [`Pane`] and the position of each -/// [`Split`]. -/// -/// The [`State`] needs to own any mutable contents a [`Pane`] may need. This is -/// why this struct is generic over the type `T`. Values of this type are -/// provided to the view function of [`PaneGrid::new`] for displaying each -/// [`Pane`]. -/// -/// [`PaneGrid`]: crate::widget::PaneGrid -/// [`PaneGrid::new`]: crate::widget::PaneGrid::new -#[derive(Debug, Clone)] -pub struct State<T> { - /// The panes of the [`PaneGrid`]. - /// - /// [`PaneGrid`]: crate::widget::PaneGrid - pub panes: HashMap<Pane, T>, - - /// The internal state of the [`PaneGrid`]. - /// - /// [`PaneGrid`]: crate::widget::PaneGrid - pub internal: Internal, - - /// The maximized [`Pane`] of the [`PaneGrid`]. - /// - /// [`PaneGrid`]: crate::widget::PaneGrid - pub(super) maximized: Option<Pane>, -} - -impl<T> State<T> { - /// Creates a new [`State`], initializing the first pane with the provided - /// state. - /// - /// Alongside the [`State`], it returns the first [`Pane`] identifier. - pub fn new(first_pane_state: T) -> (Self, Pane) { - ( - Self::with_configuration(Configuration::Pane(first_pane_state)), - Pane(0), - ) - } - - /// Creates a new [`State`] with the given [`Configuration`]. - pub fn with_configuration(config: impl Into<Configuration<T>>) -> Self { - let mut panes = HashMap::new(); - - let internal = - Internal::from_configuration(&mut panes, config.into(), 0); - - State { - panes, - internal, - maximized: None, - } - } - - /// Returns the total amount of panes in the [`State`]. - pub fn len(&self) -> usize { - self.panes.len() - } - - /// Returns `true` if the amount of panes in the [`State`] is 0. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns the internal state of the given [`Pane`], if it exists. - pub fn get(&self, pane: &Pane) -> Option<&T> { - self.panes.get(pane) - } - - /// Returns the internal state of the given [`Pane`] with mutability, if it - /// exists. - pub fn get_mut(&mut self, pane: &Pane) -> Option<&mut T> { - self.panes.get_mut(pane) - } - - /// Returns an iterator over all the panes of the [`State`], alongside its - /// internal state. - pub fn iter(&self) -> impl Iterator<Item = (&Pane, &T)> { - self.panes.iter() - } - - /// Returns a mutable iterator over all the panes of the [`State`], - /// alongside its internal state. - pub fn iter_mut(&mut self) -> impl Iterator<Item = (&Pane, &mut T)> { - self.panes.iter_mut() - } - - /// Returns the layout of the [`State`]. - pub fn layout(&self) -> &Node { - &self.internal.layout - } - - /// Returns the adjacent [`Pane`] of another [`Pane`] in the given - /// direction, if there is one. - pub fn adjacent(&self, pane: &Pane, direction: Direction) -> Option<Pane> { - let regions = self - .internal - .layout - .pane_regions(0.0, Size::new(4096.0, 4096.0)); - - let current_region = regions.get(pane)?; - - let target = match direction { - Direction::Left => { - Point::new(current_region.x - 1.0, current_region.y + 1.0) - } - Direction::Right => Point::new( - current_region.x + current_region.width + 1.0, - current_region.y + 1.0, - ), - Direction::Up => { - Point::new(current_region.x + 1.0, current_region.y - 1.0) - } - Direction::Down => Point::new( - current_region.x + 1.0, - current_region.y + current_region.height + 1.0, - ), - }; - - let mut colliding_regions = - regions.iter().filter(|(_, region)| region.contains(target)); - - let (pane, _) = colliding_regions.next()?; - - Some(*pane) - } - - /// Splits the given [`Pane`] into two in the given [`Axis`] and - /// initializing the new [`Pane`] with the provided internal state. - pub fn split( - &mut self, - axis: Axis, - pane: &Pane, - state: T, - ) -> Option<(Pane, Split)> { - let node = self.internal.layout.find(pane)?; - - let new_pane = { - self.internal.last_id = self.internal.last_id.checked_add(1)?; - - Pane(self.internal.last_id) - }; - - let new_split = { - self.internal.last_id = self.internal.last_id.checked_add(1)?; - - Split(self.internal.last_id) - }; - - node.split(new_split, axis, new_pane); - - let _ = self.panes.insert(new_pane, state); - let _ = self.maximized.take(); - - Some((new_pane, new_split)) - } - - /// Swaps the position of the provided panes in the [`State`]. - /// - /// If you want to swap panes on drag and drop in your [`PaneGrid`], you - /// will need to call this method when handling a [`DragEvent`]. - /// - /// [`PaneGrid`]: crate::widget::PaneGrid - /// [`DragEvent`]: crate::widget::pane_grid::DragEvent - pub fn swap(&mut self, a: &Pane, b: &Pane) { - self.internal.layout.update(&|node| match node { - Node::Split { .. } => {} - Node::Pane(pane) => { - if pane == a { - *node = Node::Pane(*b); - } else if pane == b { - *node = Node::Pane(*a); - } - } - }); - } - - /// Resizes two panes by setting the position of the provided [`Split`]. - /// - /// The ratio is a value in [0, 1], representing the exact position of a - /// [`Split`] between two panes. - /// - /// If you want to enable resize interactions in your [`PaneGrid`], you will - /// need to call this method when handling a [`ResizeEvent`]. - /// - /// [`PaneGrid`]: crate::widget::PaneGrid - /// [`ResizeEvent`]: crate::widget::pane_grid::ResizeEvent - pub fn resize(&mut self, split: &Split, ratio: f32) { - let _ = self.internal.layout.resize(split, ratio); - } - - /// Closes the given [`Pane`] and returns its internal state and its closest - /// sibling, if it exists. - pub fn close(&mut self, pane: &Pane) -> Option<(T, Pane)> { - if self.maximized == Some(*pane) { - let _ = self.maximized.take(); - } - - if let Some(sibling) = self.internal.layout.remove(pane) { - self.panes.remove(pane).map(|state| (state, sibling)) - } else { - None - } - } - - /// Maximize the given [`Pane`]. Only this pane will be rendered by the - /// [`PaneGrid`] until [`Self::restore()`] is called. - /// - /// [`PaneGrid`]: crate::widget::PaneGrid - pub fn maximize(&mut self, pane: &Pane) { - self.maximized = Some(*pane); - } - - /// Restore the currently maximized [`Pane`] to it's normal size. All panes - /// will be rendered by the [`PaneGrid`]. - /// - /// [`PaneGrid`]: crate::widget::PaneGrid - pub fn restore(&mut self) { - let _ = self.maximized.take(); - } - - /// Returns the maximized [`Pane`] of the [`PaneGrid`]. - /// - /// [`PaneGrid`]: crate::widget::PaneGrid - pub fn maximized(&self) -> Option<Pane> { - self.maximized - } -} - -/// The internal state of a [`PaneGrid`]. -/// -/// [`PaneGrid`]: crate::widget::PaneGrid -#[derive(Debug, Clone)] -pub struct Internal { - layout: Node, - last_id: usize, -} - -impl Internal { - /// Initializes the [`Internal`] state of a [`PaneGrid`] from a - /// [`Configuration`]. - /// - /// [`PaneGrid`]: crate::widget::PaneGrid - pub fn from_configuration<T>( - panes: &mut HashMap<Pane, T>, - content: Configuration<T>, - next_id: usize, - ) -> Self { - let (layout, last_id) = match content { - Configuration::Split { axis, ratio, a, b } => { - let Internal { - layout: a, - last_id: next_id, - .. - } = Self::from_configuration(panes, *a, next_id); - - let Internal { - layout: b, - last_id: next_id, - .. - } = Self::from_configuration(panes, *b, next_id); - - ( - Node::Split { - id: Split(next_id), - axis, - ratio, - a: Box::new(a), - b: Box::new(b), - }, - next_id + 1, - ) - } - Configuration::Pane(state) => { - let id = Pane(next_id); - let _ = panes.insert(id, state); - - (Node::Pane(id), next_id + 1) - } - }; - - Self { layout, last_id } - } -} - -/// The current action of a [`PaneGrid`]. -/// -/// [`PaneGrid`]: crate::widget::PaneGrid -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Action { - /// The [`PaneGrid`] is idle. - /// - /// [`PaneGrid`]: crate::widget::PaneGrid - Idle, - /// A [`Pane`] in the [`PaneGrid`] is being dragged. - /// - /// [`PaneGrid`]: crate::widget::PaneGrid - Dragging { - /// The [`Pane`] being dragged. - pane: Pane, - /// The starting [`Point`] of the drag interaction. - origin: Point, - }, - /// A [`Split`] in the [`PaneGrid`] is being dragged. - /// - /// [`PaneGrid`]: crate::widget::PaneGrid - Resizing { - /// The [`Split`] being dragged. - split: Split, - /// The [`Axis`] of the [`Split`]. - axis: Axis, - }, -} - -impl Action { - /// Returns the current [`Pane`] that is being dragged, if any. - pub fn picked_pane(&self) -> Option<(Pane, Point)> { - match *self { - Action::Dragging { pane, origin, .. } => Some((pane, origin)), - _ => None, - } - } - - /// Returns the current [`Split`] that is being dragged, if any. - pub fn picked_split(&self) -> Option<(Split, Axis)> { - match *self { - Action::Resizing { split, axis, .. } => Some((split, axis)), - _ => None, - } - } -} - -impl Internal { - /// The layout [`Node`] of the [`Internal`] state - pub fn layout(&self) -> &Node { - &self.layout - } -} diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs deleted file mode 100644 index 107078ef..00000000 --- a/native/src/widget/pane_grid/title_bar.rs +++ /dev/null @@ -1,432 +0,0 @@ -use crate::event::{self, Event}; -use crate::layout; -use crate::mouse; -use crate::overlay; -use crate::renderer; -use crate::widget::container; -use crate::widget::{self, Tree}; -use crate::{ - Clipboard, Element, 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: crate::Renderer, - Renderer::Theme: container::StyleSheet, -{ - content: Element<'a, Message, Renderer>, - controls: Option<Element<'a, Message, Renderer>>, - padding: Padding, - always_show_controls: bool, - style: <Renderer::Theme as container::StyleSheet>::Style, -} - -impl<'a, Message, Renderer> TitleBar<'a, Message, Renderer> -where - Renderer: crate::Renderer, - Renderer::Theme: container::StyleSheet, -{ - /// Creates a new [`TitleBar`] with the given content. - pub fn new<E>(content: E) -> Self - where - E: Into<Element<'a, Message, Renderer>>, - { - 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<Element<'a, Message, Renderer>>, - ) -> Self { - self.controls = Some(controls.into()); - self - } - - /// Sets the [`Padding`] of the [`TitleBar`]. - pub fn padding<P: Into<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<<Renderer::Theme as container::StyleSheet>::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: crate::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`]: crate::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 { - if show_controls || self.always_show_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().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(); - - if title_layout.bounds().width + controls_layout.bounds().width - > padded.bounds().width - { - !controls_layout.bounds().contains(cursor_position) - } else { - !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, self.padding.top)); - - layout::Node::with_children(node.size().pad(self.padding), vec![node]) - } - - pub(crate) fn operate( - &self, - tree: &mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - operation: &mut dyn widget::Operation<Message>, - ) { - 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; - } - - controls.as_widget().operate( - &mut tree.children[1], - controls_layout, - renderer, - operation, - ) - }; - - if show_title { - self.content.as_widget().operate( - &mut tree.children[0], - title_layout, - renderer, - operation, - ) - } - } - - 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 mut self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'b, Message, Renderer>> { - 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_mut() - .overlay(title_state, title_layout, renderer) - .or_else(move || { - controls.as_mut().and_then(|controls| { - let controls_layout = children.next()?; - - controls.as_widget_mut().overlay( - controls_state, - controls_layout, - renderer, - ) - }) - }) - } -} |