diff options
| author | 2024-03-06 15:41:57 +0100 | |
|---|---|---|
| committer | 2024-03-06 15:49:06 +0100 | |
| commit | 9b2fd6416775cb27af69e34fb20063d28b4314eb (patch) | |
| tree | d6c55df1df5466429e0afe410acfa488600718c5 /widget | |
| parent | 68c8f23f02a55373db728b115c3a4360669e2b80 (diff) | |
| download | iced-9b2fd6416775cb27af69e34fb20063d28b4314eb.tar.gz iced-9b2fd6416775cb27af69e34fb20063d28b4314eb.tar.bz2 iced-9b2fd6416775cb27af69e34fb20063d28b4314eb.zip | |
Simplify theming for `PaneGrid` widget
Diffstat (limited to '')
| -rw-r--r-- | widget/src/pane_grid.rs | 1073 | 
1 files changed, 522 insertions, 551 deletions
| diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index a18d0fbf..62067e66 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -30,8 +30,6 @@ pub use split::Split;  pub use state::State;  pub use title_bar::TitleBar; -pub use crate::style::pane_grid::{Appearance, Line, StyleSheet}; -  use crate::core::event::{self, Event};  use crate::core::layout;  use crate::core::mouse; @@ -41,9 +39,10 @@ use crate::core::touch;  use crate::core::widget;  use crate::core::widget::tree::{self, Tree};  use crate::core::{ -    Clipboard, Element, Layout, Length, Pixels, Point, Rectangle, Shell, Size, -    Vector, Widget, +    Background, Border, Clipboard, Color, Element, Layout, Length, Pixels, +    Point, Rectangle, Shell, Size, Vector, Widget,  }; +use crate::style::Theme;  const DRAG_DEADBAND_DISTANCE: f32 = 10.0;  const THICKNESS_RATIO: f32 = 25.0; @@ -104,7 +103,6 @@ pub struct PaneGrid<      Theme = crate::Theme,      Renderer = crate::Renderer,  > where -    Theme: StyleSheet,      Renderer: crate::core::Renderer,  {      contents: Contents<'a, Content<'a, Message, Theme, Renderer>>, @@ -114,12 +112,11 @@ pub struct PaneGrid<      on_click: Option<Box<dyn Fn(Pane) -> Message + 'a>>,      on_drag: Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,      on_resize: Option<(f32, Box<dyn Fn(ResizeEvent) -> Message + 'a>)>, -    style: <Theme as StyleSheet>::Style, +    style: fn(&Theme) -> Appearance,  }  impl<'a, Message, Theme, Renderer> PaneGrid<'a, Message, Theme, Renderer>  where -    Theme: StyleSheet,      Renderer: crate::core::Renderer,  {      /// Creates a [`PaneGrid`] with the given [`State`] and view function. @@ -129,7 +126,10 @@ where      pub fn new<T>(          state: &'a State<T>,          view: impl Fn(Pane, &'a T, bool) -> Content<'a, Message, Theme, Renderer>, -    ) -> Self { +    ) -> Self +    where +        Theme: Style, +    {          let contents = if let Some((pane, pane_state)) =              state.maximized.and_then(|pane| {                  state.panes.get(&pane).map(|pane_state| (pane, pane_state)) @@ -160,7 +160,7 @@ where              on_click: None,              on_drag: None,              on_resize: None, -            style: Default::default(), +            style: Theme::style(),          }      } @@ -220,11 +220,8 @@ where      }      /// Sets the style of the [`PaneGrid`]. -    pub fn style( -        mut self, -        style: impl Into<<Theme as StyleSheet>::Style>, -    ) -> Self { -        self.style = style.into(); +    pub fn style(mut self, style: fn(&Theme) -> Appearance) -> Self { +        self.style = style;          self      } @@ -239,7 +236,6 @@ impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>      for PaneGrid<'a, Message, Theme, Renderer>  where      Renderer: crate::core::Renderer, -    Theme: StyleSheet,  {      fn tag(&self) -> tree::Tag {          tree::Tag::of::<state::Action>() @@ -284,19 +280,29 @@ where          renderer: &Renderer,          limits: &layout::Limits,      ) -> layout::Node { -        layout( -            tree, -            renderer, -            limits, -            self.contents.layout(), -            self.width, -            self.height, -            self.spacing, -            self.contents.iter(), -            |content, tree, renderer, limits| { -                content.layout(tree, renderer, limits) -            }, -        ) +        let size = limits.resolve(self.width, self.height, Size::ZERO); +        let node = self.contents.layout(); +        let regions = node.pane_regions(self.spacing, size); + +        let children = self +            .contents +            .iter() +            .zip(tree.children.iter_mut()) +            .filter_map(|((pane, content), tree)| { +                let region = regions.get(&pane)?; +                let size = Size::new(region.width, region.height); + +                let node = content.layout( +                    tree, +                    renderer, +                    &layout::Limits::new(size, size), +                ); + +                Some(node.move_to(Point::new(region.x, region.y))) +            }) +            .collect(); + +        layout::Node::with_children(size, children)      }      fn operate( @@ -328,7 +334,10 @@ where          shell: &mut Shell<'_, Message>,          viewport: &Rectangle,      ) -> event::Status { +        let mut event_status = event::Status::Ignored; +          let action = tree.state.downcast_mut::<state::Action>(); +        let node = self.contents.layout();          let on_drag = if self.drag_enabled() {              &self.on_drag @@ -336,19 +345,164 @@ where              &None          }; -        let event_status = update( -            action, -            self.contents.layout(), -            &event, -            layout, -            cursor, -            shell, -            self.spacing, -            self.contents.iter(), -            &self.on_click, -            on_drag, -            &self.on_resize, -        ); +        match event { +            Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) +            | Event::Touch(touch::Event::FingerPressed { .. }) => { +                let bounds = layout.bounds(); + +                if let Some(cursor_position) = cursor.position_over(bounds) { +                    event_status = event::Status::Captured; + +                    match &self.on_resize { +                        Some((leeway, _)) => { +                            let relative_cursor = Point::new( +                                cursor_position.x - bounds.x, +                                cursor_position.y - bounds.y, +                            ); + +                            let splits = node.split_regions( +                                self.spacing, +                                Size::new(bounds.width, bounds.height), +                            ); + +                            let clicked_split = hovered_split( +                                splits.iter(), +                                self.spacing + leeway, +                                relative_cursor, +                            ); + +                            if let Some((split, axis, _)) = clicked_split { +                                if action.picked_pane().is_none() { +                                    *action = +                                        state::Action::Resizing { split, axis }; +                                } +                            } else { +                                click_pane( +                                    action, +                                    layout, +                                    cursor_position, +                                    shell, +                                    self.contents.iter(), +                                    &self.on_click, +                                    on_drag, +                                ); +                            } +                        } +                        None => { +                            click_pane( +                                action, +                                layout, +                                cursor_position, +                                shell, +                                self.contents.iter(), +                                &self.on_click, +                                on_drag, +                            ); +                        } +                    } +                } +            } +            Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) +            | Event::Touch(touch::Event::FingerLifted { .. }) +            | Event::Touch(touch::Event::FingerLost { .. }) => { +                if let Some((pane, origin)) = action.picked_pane() { +                    if let Some(on_drag) = on_drag { +                        if let Some(cursor_position) = cursor.position() { +                            if cursor_position.distance(origin) +                                > DRAG_DEADBAND_DISTANCE +                            { +                                let event = if let Some(edge) = +                                    in_edge(layout, cursor_position) +                                { +                                    DragEvent::Dropped { +                                        pane, +                                        target: Target::Edge(edge), +                                    } +                                } else { +                                    let dropped_region = self +                                        .contents +                                        .iter() +                                        .zip(layout.children()) +                                        .find_map(|(target, layout)| { +                                            layout_region( +                                                layout, +                                                cursor_position, +                                            ) +                                            .map(|region| (target, region)) +                                        }); + +                                    match dropped_region { +                                        Some(((target, _), region)) +                                            if pane != target => +                                        { +                                            DragEvent::Dropped { +                                                pane, +                                                target: Target::Pane( +                                                    target, region, +                                                ), +                                            } +                                        } +                                        _ => DragEvent::Canceled { pane }, +                                    } +                                }; + +                                shell.publish(on_drag(event)); +                            } +                        } +                    } + +                    event_status = event::Status::Captured; +                } else if action.picked_split().is_some() { +                    event_status = event::Status::Captured; +                } + +                *action = state::Action::Idle; +            } +            Event::Mouse(mouse::Event::CursorMoved { .. }) +            | Event::Touch(touch::Event::FingerMoved { .. }) => { +                if let Some((_, on_resize)) = &self.on_resize { +                    if let Some((split, _)) = action.picked_split() { +                        let bounds = layout.bounds(); + +                        let splits = node.split_regions( +                            self.spacing, +                            Size::new(bounds.width, bounds.height), +                        ); + +                        if let Some((axis, rectangle, _)) = splits.get(&split) { +                            if let Some(cursor_position) = cursor.position() { +                                let ratio = match axis { +                                    Axis::Horizontal => { +                                        let position = cursor_position.y +                                            - bounds.y +                                            - rectangle.y; + +                                        (position / rectangle.height) +                                            .clamp(0.1, 0.9) +                                    } +                                    Axis::Vertical => { +                                        let position = cursor_position.x +                                            - bounds.x +                                            - rectangle.x; + +                                        (position / rectangle.width) +                                            .clamp(0.1, 0.9) +                                    } +                                }; + +                                shell.publish(on_resize(ResizeEvent { +                                    split, +                                    ratio, +                                })); + +                                event_status = event::Status::Captured; +                            } +                        } +                    } +                } +            } +            _ => {} +        }          let picked_pane = action.picked_pane().map(|(pane, _)| pane); @@ -382,32 +536,61 @@ where          viewport: &Rectangle,          renderer: &Renderer,      ) -> mouse::Interaction { -        mouse_interaction( -            tree.state.downcast_ref(), -            self.contents.layout(), -            layout, -            cursor, -            self.spacing, -            self.on_resize.as_ref().map(|(leeway, _)| *leeway), -        ) -        .unwrap_or_else(|| { -            self.contents -                .iter() -                .zip(&tree.children) -                .zip(layout.children()) -                .map(|(((_pane, content), tree), layout)| { -                    content.mouse_interaction( -                        tree, -                        layout, -                        cursor, -                        viewport, -                        renderer, -                        self.drag_enabled(), +        let action = tree.state.downcast_ref::<state::Action>(); + +        if action.picked_pane().is_some() { +            return mouse::Interaction::Grabbing; +        } + +        let resize_leeway = self.on_resize.as_ref().map(|(leeway, _)| *leeway); +        let node = self.contents.layout(); + +        let resize_axis = +            action.picked_split().map(|(_, axis)| axis).or_else(|| { +                resize_leeway.and_then(|leeway| { +                    let cursor_position = cursor.position()?; +                    let bounds = layout.bounds(); + +                    let splits = +                        node.split_regions(self.spacing, bounds.size()); + +                    let relative_cursor = Point::new( +                        cursor_position.x - bounds.x, +                        cursor_position.y - bounds.y, +                    ); + +                    hovered_split( +                        splits.iter(), +                        self.spacing + leeway, +                        relative_cursor,                      ) +                    .map(|(_, axis, _)| axis)                  }) -                .max() -                .unwrap_or_default() -        }) +            }); + +        if let Some(resize_axis) = resize_axis { +            return match resize_axis { +                Axis::Horizontal => mouse::Interaction::ResizingVertically, +                Axis::Vertical => mouse::Interaction::ResizingHorizontally, +            }; +        } + +        self.contents +            .iter() +            .zip(&tree.children) +            .zip(layout.children()) +            .map(|(((_pane, content), tree), layout)| { +                content.mouse_interaction( +                    tree, +                    layout, +                    cursor, +                    viewport, +                    renderer, +                    self.drag_enabled(), +                ) +            }) +            .max() +            .unwrap_or_default()      }      fn draw( @@ -420,28 +603,210 @@ where          cursor: mouse::Cursor,          viewport: &Rectangle,      ) { -        draw( -            tree.state.downcast_ref(), -            self.contents.layout(), -            layout, -            cursor, -            renderer, -            theme, -            style, -            viewport, -            self.spacing, -            self.on_resize.as_ref().map(|(leeway, _)| *leeway), -            &self.style, -            self.contents -                .iter() -                .zip(&tree.children) -                .map(|((pane, content), tree)| (pane, (content, tree))), -            |(content, tree), renderer, style, layout, cursor, rectangle| { -                content.draw( -                    tree, renderer, theme, style, layout, cursor, rectangle, +        let action = tree.state.downcast_ref::<state::Action>(); +        let node = self.contents.layout(); +        let resize_leeway = self.on_resize.as_ref().map(|(leeway, _)| *leeway); + +        let contents = self +            .contents +            .iter() +            .zip(&tree.children) +            .map(|((pane, content), tree)| (pane, (content, tree))); + +        let picked_pane = action.picked_pane().filter(|(_, origin)| { +            cursor +                .position() +                .map(|position| position.distance(*origin)) +                .unwrap_or_default() +                > DRAG_DEADBAND_DISTANCE +        }); + +        let picked_split = action +            .picked_split() +            .and_then(|(split, axis)| { +                let bounds = layout.bounds(); + +                let splits = node.split_regions(self.spacing, bounds.size()); + +                let (_axis, region, ratio) = splits.get(&split)?; + +                let region = +                    axis.split_line_bounds(*region, *ratio, self.spacing); + +                Some((axis, region + Vector::new(bounds.x, bounds.y), true)) +            }) +            .or_else(|| match resize_leeway { +                Some(leeway) => { +                    let cursor_position = cursor.position()?; +                    let bounds = layout.bounds(); + +                    let relative_cursor = Point::new( +                        cursor_position.x - bounds.x, +                        cursor_position.y - bounds.y, +                    ); + +                    let splits = +                        node.split_regions(self.spacing, bounds.size()); + +                    let (_split, axis, region) = hovered_split( +                        splits.iter(), +                        self.spacing + leeway, +                        relative_cursor, +                    )?; + +                    Some(( +                        axis, +                        region + Vector::new(bounds.x, bounds.y), +                        false, +                    )) +                } +                None => None, +            }); + +        let pane_cursor = if picked_pane.is_some() { +            mouse::Cursor::Unavailable +        } else { +            cursor +        }; + +        let mut render_picked_pane = None; + +        let pane_in_edge = if picked_pane.is_some() { +            cursor +                .position() +                .and_then(|cursor_position| in_edge(layout, cursor_position)) +        } else { +            None +        }; + +        let appearance = (self.style)(theme); + +        for ((id, (content, tree)), pane_layout) in +            contents.zip(layout.children()) +        { +            match picked_pane { +                Some((dragging, origin)) if id == dragging => { +                    render_picked_pane = +                        Some(((content, tree), origin, pane_layout)); +                } +                Some((dragging, _)) if id != dragging => { +                    content.draw( +                        tree, +                        renderer, +                        theme, +                        style, +                        pane_layout, +                        pane_cursor, +                        viewport, +                    ); + +                    if picked_pane.is_some() && pane_in_edge.is_none() { +                        if let Some(region) = +                            cursor.position().and_then(|cursor_position| { +                                layout_region(pane_layout, cursor_position) +                            }) +                        { +                            let bounds = +                                layout_region_bounds(pane_layout, region); + +                            renderer.fill_quad( +                                renderer::Quad { +                                    bounds, +                                    border: appearance.hovered_region.border, +                                    ..renderer::Quad::default() +                                }, +                                appearance.hovered_region.background, +                            ); +                        } +                    } +                } +                _ => { +                    content.draw( +                        tree, +                        renderer, +                        theme, +                        style, +                        pane_layout, +                        pane_cursor, +                        viewport, +                    ); +                } +            } +        } + +        if let Some(edge) = pane_in_edge { +            let bounds = edge_bounds(layout, edge); + +            renderer.fill_quad( +                renderer::Quad { +                    bounds, +                    border: appearance.hovered_region.border, +                    ..renderer::Quad::default() +                }, +                appearance.hovered_region.background, +            ); +        } + +        // Render picked pane last +        if let Some(((content, tree), origin, layout)) = render_picked_pane { +            if let Some(cursor_position) = cursor.position() { +                let bounds = layout.bounds(); + +                let translation = +                    cursor_position - Point::new(origin.x, origin.y); + +                renderer.with_translation(translation, |renderer| { +                    renderer.with_layer(bounds, |renderer| { +                        content.draw( +                            tree, +                            renderer, +                            theme, +                            style, +                            layout, +                            pane_cursor, +                            viewport, +                        ); +                    }); +                }); +            } +        } + +        if picked_pane.is_none() { +            if let Some((axis, split_region, is_picked)) = picked_split { +                let highlight = if is_picked { +                    appearance.picked_split +                } else { +                    appearance.hovered_split +                }; + +                renderer.fill_quad( +                    renderer::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, +                            }, +                        }, +                        ..renderer::Quad::default() +                    }, +                    highlight.color,                  ); -            }, -        ); +            } +        }      }      fn overlay<'b>( @@ -469,7 +834,7 @@ impl<'a, Message, Theme, Renderer> From<PaneGrid<'a, Message, Theme, Renderer>>      for Element<'a, Message, Theme, Renderer>  where      Message: 'a, -    Theme: StyleSheet + 'a, +    Theme: 'a,      Renderer: crate::core::Renderer + 'a,  {      fn from( @@ -479,219 +844,6 @@ where      }  } -/// Calculates the [`Layout`] of a [`PaneGrid`]. -pub fn layout<Renderer, T>( -    tree: &mut Tree, -    renderer: &Renderer, -    limits: &layout::Limits, -    node: &Node, -    width: Length, -    height: Length, -    spacing: f32, -    contents: impl Iterator<Item = (Pane, T)>, -    layout_content: impl Fn( -        T, -        &mut Tree, -        &Renderer, -        &layout::Limits, -    ) -> layout::Node, -) -> layout::Node { -    let size = limits.resolve(width, height, Size::ZERO); - -    let regions = node.pane_regions(spacing, size); -    let children = contents -        .zip(tree.children.iter_mut()) -        .filter_map(|((pane, content), tree)| { -            let region = regions.get(&pane)?; -            let size = Size::new(region.width, region.height); - -            let node = layout_content( -                content, -                tree, -                renderer, -                &layout::Limits::new(size, size), -            ); - -            Some(node.move_to(Point::new(region.x, region.y))) -        }) -        .collect(); - -    layout::Node::with_children(size, children) -} - -/// Processes an [`Event`] and updates the [`state`] of a [`PaneGrid`] -/// accordingly. -pub fn update<'a, Message, T: Draggable>( -    action: &mut state::Action, -    node: &Node, -    event: &Event, -    layout: Layout<'_>, -    cursor: mouse::Cursor, -    shell: &mut Shell<'_, Message>, -    spacing: f32, -    contents: impl Iterator<Item = (Pane, T)>, -    on_click: &Option<Box<dyn Fn(Pane) -> Message + 'a>>, -    on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>, -    on_resize: &Option<(f32, Box<dyn Fn(ResizeEvent) -> Message + 'a>)>, -) -> event::Status { -    let mut event_status = event::Status::Ignored; - -    match event { -        Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) -        | Event::Touch(touch::Event::FingerPressed { .. }) => { -            let bounds = layout.bounds(); - -            if let Some(cursor_position) = cursor.position_over(bounds) { -                event_status = event::Status::Captured; - -                match on_resize { -                    Some((leeway, _)) => { -                        let relative_cursor = Point::new( -                            cursor_position.x - bounds.x, -                            cursor_position.y - bounds.y, -                        ); - -                        let splits = node.split_regions( -                            spacing, -                            Size::new(bounds.width, bounds.height), -                        ); - -                        let clicked_split = hovered_split( -                            splits.iter(), -                            spacing + leeway, -                            relative_cursor, -                        ); - -                        if let Some((split, axis, _)) = clicked_split { -                            if action.picked_pane().is_none() { -                                *action = -                                    state::Action::Resizing { split, axis }; -                            } -                        } else { -                            click_pane( -                                action, -                                layout, -                                cursor_position, -                                shell, -                                contents, -                                on_click, -                                on_drag, -                            ); -                        } -                    } -                    None => { -                        click_pane( -                            action, -                            layout, -                            cursor_position, -                            shell, -                            contents, -                            on_click, -                            on_drag, -                        ); -                    } -                } -            } -        } -        Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) -        | Event::Touch(touch::Event::FingerLifted { .. }) -        | Event::Touch(touch::Event::FingerLost { .. }) => { -            if let Some((pane, origin)) = action.picked_pane() { -                if let Some(on_drag) = on_drag { -                    if let Some(cursor_position) = cursor.position() { -                        if cursor_position.distance(origin) -                            > DRAG_DEADBAND_DISTANCE -                        { -                            let event = if let Some(edge) = -                                in_edge(layout, cursor_position) -                            { -                                DragEvent::Dropped { -                                    pane, -                                    target: Target::Edge(edge), -                                } -                            } else { -                                let dropped_region = contents -                                    .zip(layout.children()) -                                    .find_map(|(target, layout)| { -                                        layout_region(layout, cursor_position) -                                            .map(|region| (target, region)) -                                    }); - -                                match dropped_region { -                                    Some(((target, _), region)) -                                        if pane != target => -                                    { -                                        DragEvent::Dropped { -                                            pane, -                                            target: Target::Pane( -                                                target, region, -                                            ), -                                        } -                                    } -                                    _ => DragEvent::Canceled { pane }, -                                } -                            }; - -                            shell.publish(on_drag(event)); -                        } -                    } -                } - -                event_status = event::Status::Captured; -            } else if action.picked_split().is_some() { -                event_status = event::Status::Captured; -            } - -            *action = state::Action::Idle; -        } -        Event::Mouse(mouse::Event::CursorMoved { .. }) -        | Event::Touch(touch::Event::FingerMoved { .. }) => { -            if let Some((_, on_resize)) = on_resize { -                if let Some((split, _)) = action.picked_split() { -                    let bounds = layout.bounds(); - -                    let splits = node.split_regions( -                        spacing, -                        Size::new(bounds.width, bounds.height), -                    ); - -                    if let Some((axis, rectangle, _)) = splits.get(&split) { -                        if let Some(cursor_position) = cursor.position() { -                            let ratio = match axis { -                                Axis::Horizontal => { -                                    let position = cursor_position.y -                                        - bounds.y -                                        - rectangle.y; - -                                    (position / rectangle.height) -                                        .clamp(0.1, 0.9) -                                } -                                Axis::Vertical => { -                                    let position = cursor_position.x -                                        - bounds.x -                                        - rectangle.x; - -                                    (position / rectangle.width).clamp(0.1, 0.9) -                                } -                            }; - -                            shell.publish(on_resize(ResizeEvent { -                                split, -                                ratio, -                            })); - -                            event_status = event::Status::Captured; -                        } -                    } -                } -            } -        } -        _ => {} -    } - -    event_status -} -  fn layout_region(layout: Layout<'_>, cursor_position: Point) -> Option<Region> {      let bounds = layout.bounds(); @@ -747,257 +899,6 @@ fn click_pane<'a, Message, T>(      }  } -/// Returns the current [`mouse::Interaction`] of a [`PaneGrid`]. -pub fn mouse_interaction( -    action: &state::Action, -    node: &Node, -    layout: Layout<'_>, -    cursor: mouse::Cursor, -    spacing: f32, -    resize_leeway: Option<f32>, -) -> Option<mouse::Interaction> { -    if action.picked_pane().is_some() { -        return Some(mouse::Interaction::Grabbing); -    } - -    let resize_axis = -        action.picked_split().map(|(_, axis)| axis).or_else(|| { -            resize_leeway.and_then(|leeway| { -                let cursor_position = cursor.position()?; -                let bounds = layout.bounds(); - -                let splits = node.split_regions(spacing, bounds.size()); - -                let relative_cursor = Point::new( -                    cursor_position.x - bounds.x, -                    cursor_position.y - bounds.y, -                ); - -                hovered_split(splits.iter(), spacing + leeway, relative_cursor) -                    .map(|(_, axis, _)| axis) -            }) -        }); - -    if let Some(resize_axis) = resize_axis { -        return Some(match resize_axis { -            Axis::Horizontal => mouse::Interaction::ResizingVertically, -            Axis::Vertical => mouse::Interaction::ResizingHorizontally, -        }); -    } - -    None -} - -/// Draws a [`PaneGrid`]. -pub fn draw<Theme, Renderer, T>( -    action: &state::Action, -    node: &Node, -    layout: Layout<'_>, -    cursor: mouse::Cursor, -    renderer: &mut Renderer, -    theme: &Theme, -    default_style: &renderer::Style, -    viewport: &Rectangle, -    spacing: f32, -    resize_leeway: Option<f32>, -    style: &Theme::Style, -    contents: impl Iterator<Item = (Pane, T)>, -    draw_pane: impl Fn( -        T, -        &mut Renderer, -        &renderer::Style, -        Layout<'_>, -        mouse::Cursor, -        &Rectangle, -    ), -) where -    Theme: StyleSheet, -    Renderer: crate::core::Renderer, -{ -    let picked_pane = action.picked_pane().filter(|(_, origin)| { -        cursor -            .position() -            .map(|position| position.distance(*origin)) -            .unwrap_or_default() -            > DRAG_DEADBAND_DISTANCE -    }); - -    let picked_split = action -        .picked_split() -        .and_then(|(split, axis)| { -            let bounds = layout.bounds(); - -            let splits = node.split_regions(spacing, bounds.size()); - -            let (_axis, region, ratio) = splits.get(&split)?; - -            let region = axis.split_line_bounds(*region, *ratio, spacing); - -            Some((axis, region + Vector::new(bounds.x, bounds.y), true)) -        }) -        .or_else(|| match resize_leeway { -            Some(leeway) => { -                let cursor_position = cursor.position()?; -                let bounds = layout.bounds(); - -                let relative_cursor = Point::new( -                    cursor_position.x - bounds.x, -                    cursor_position.y - bounds.y, -                ); - -                let splits = node.split_regions(spacing, bounds.size()); - -                let (_split, axis, region) = hovered_split( -                    splits.iter(), -                    spacing + leeway, -                    relative_cursor, -                )?; - -                Some((axis, region + Vector::new(bounds.x, bounds.y), false)) -            } -            None => None, -        }); - -    let pane_cursor = if picked_pane.is_some() { -        mouse::Cursor::Unavailable -    } else { -        cursor -    }; - -    let mut render_picked_pane = None; - -    let pane_in_edge = if picked_pane.is_some() { -        cursor -            .position() -            .and_then(|cursor_position| in_edge(layout, cursor_position)) -    } else { -        None -    }; - -    for ((id, pane), pane_layout) in contents.zip(layout.children()) { -        match picked_pane { -            Some((dragging, origin)) if id == dragging => { -                render_picked_pane = Some((pane, origin, pane_layout)); -            } -            Some((dragging, _)) if id != dragging => { -                draw_pane( -                    pane, -                    renderer, -                    default_style, -                    pane_layout, -                    pane_cursor, -                    viewport, -                ); - -                if picked_pane.is_some() && pane_in_edge.is_none() { -                    if let Some(region) = -                        cursor.position().and_then(|cursor_position| { -                            layout_region(pane_layout, cursor_position) -                        }) -                    { -                        let bounds = layout_region_bounds(pane_layout, region); -                        let hovered_region_style = theme.hovered_region(style); - -                        renderer.fill_quad( -                            renderer::Quad { -                                bounds, -                                border: hovered_region_style.border, -                                ..renderer::Quad::default() -                            }, -                            theme.hovered_region(style).background, -                        ); -                    } -                } -            } -            _ => { -                draw_pane( -                    pane, -                    renderer, -                    default_style, -                    pane_layout, -                    pane_cursor, -                    viewport, -                ); -            } -        } -    } - -    if let Some(edge) = pane_in_edge { -        let hovered_region_style = theme.hovered_region(style); -        let bounds = edge_bounds(layout, edge); - -        renderer.fill_quad( -            renderer::Quad { -                bounds, -                border: hovered_region_style.border, -                ..renderer::Quad::default() -            }, -            theme.hovered_region(style).background, -        ); -    } - -    // Render picked pane last -    if let Some((pane, origin, layout)) = render_picked_pane { -        if let Some(cursor_position) = cursor.position() { -            let bounds = layout.bounds(); - -            let translation = cursor_position - Point::new(origin.x, origin.y); - -            renderer.with_translation(translation, |renderer| { -                renderer.with_layer(bounds, |renderer| { -                    draw_pane( -                        pane, -                        renderer, -                        default_style, -                        layout, -                        pane_cursor, -                        viewport, -                    ); -                }); -            }); -        } -    } - -    if picked_pane.is_none() { -        if let Some((axis, split_region, is_picked)) = picked_split { -            let highlight = if is_picked { -                theme.picked_split(style) -            } else { -                theme.hovered_split(style) -            }; - -            if let Some(highlight) = highlight { -                renderer.fill_quad( -                    renderer::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, -                            }, -                        }, -                        ..renderer::Quad::default() -                    }, -                    highlight.color, -                ); -            } -        } -    } -} -  fn in_edge(layout: Layout<'_>, cursor: Point) -> Option<Edge> {      let bounds = layout.bounds(); @@ -1214,3 +1115,73 @@ impl<'a, T> Contents<'a, T> {          matches!(self, Self::Maximized(..))      }  } + +/// The appearance of a [`PaneGrid`]. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Appearance { +    /// The appearance of a hovered region highlight. +    hovered_region: Highlight, +    /// The appearance of a picked split. +    picked_split: Line, +    /// The appearance of a hovered split. +    hovered_split: Line, +} + +/// The appearance of a highlight of the [`PaneGrid`]. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Highlight { +    /// The [`Background`] of the pane region. +    pub background: Background, +    /// The [`Border`] of the pane region. +    pub border: Border, +} + +/// 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, +} + +/// The definiton of the default style of a [`PaneGrid`]. +pub trait Style { +    /// Returns the default style of a [`PaneGrid`]. +    fn style() -> fn(&Self) -> Appearance; +} + +impl Style for Theme { +    fn style() -> fn(&Self) -> Appearance { +        default +    } +} + +/// The default style of a [`PaneGrid`]. +pub fn default(theme: &Theme) -> Appearance { +    let palette = theme.extended_palette(); + +    Appearance { +        hovered_region: Highlight { +            background: Background::Color(Color { +                a: 0.5, +                ..palette.primary.base.color +            }), +            border: Border { +                width: 2.0, +                color: palette.primary.strong.color, +                radius: 0.0.into(), +            }, +        }, +        hovered_split: Line { +            color: palette.primary.base.color, +            width: 2.0, +        }, +        picked_split: Line { +            color: palette.primary.strong.color, +            width: 2.0, +        }, +    } +} | 
