From e6f8b32583ae00eb77d6e10e3a43c899ecadc88f Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Mon, 24 May 2021 14:28:52 -0500 Subject: [PATCH 1/3] Example: Add close button to pane grid controls Refactors the state data structure to hold content and controls in separate fields. Adds a new button style for the control button. --- examples/pane_grid/src/main.rs | 62 +++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 4b87a568..7c0d6582 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -11,7 +11,7 @@ pub fn main() -> iced::Result { } struct Example { - panes: pane_grid::State, + panes: pane_grid::State, panes_created: usize, focus: Option, } @@ -34,7 +34,7 @@ impl Application for Example { type Flags = (); fn new(_flags: ()) -> (Self, Command) { - let (panes, _) = pane_grid::State::new(Content::new(0)); + let (panes, _) = pane_grid::State::new(Pane::new(0)); ( Example { @@ -60,7 +60,7 @@ impl Application for Example { let result = self.panes.split( axis, &pane, - Content::new(self.panes_created), + Pane::new(self.panes_created), ); if let Some((pane, _)) = result { @@ -74,7 +74,7 @@ impl Application for Example { let result = self.panes.split( axis, &pane, - Content::new(self.panes_created), + Pane::new(self.panes_created), ); if let Some((pane, _)) = result { @@ -143,12 +143,12 @@ impl Application for Example { let focus = self.focus; let total_panes = self.panes.len(); - let pane_grid = PaneGrid::new(&mut self.panes, |pane, content| { - let is_focused = focus == Some(pane); + let pane_grid = PaneGrid::new(&mut self.panes, |id, pane| { + let is_focused = focus == Some(id); let title = Row::with_children(vec![ Text::new("Pane").into(), - Text::new(content.id.to_string()) + Text::new(pane.content.id.to_string()) .color(if is_focused { PANE_ID_COLOR_FOCUSED } else { @@ -159,10 +159,11 @@ impl Application for Example { .spacing(5); let title_bar = pane_grid::TitleBar::new(title) + .controls(pane.controls.view(id, total_panes)) .padding(10) .style(style::TitleBar { is_focused }); - pane_grid::Content::new(content.view(pane, total_panes)) + pane_grid::Content::new(pane.content.view(id, total_panes)) .title_bar(title_bar) .style(style::Pane { is_focused }) }) @@ -212,6 +213,11 @@ fn handle_hotkey(key_code: keyboard::KeyCode) -> Option { } } +struct Pane { + pub content: Content, + pub controls: Controls, +} + struct Content { id: usize, scroll: scrollable::State, @@ -220,6 +226,19 @@ struct Content { close: button::State, } +struct Controls { + close: button::State, +} + +impl Pane { + fn new(id: usize) -> Self { + Self { + content: Content::new(id), + controls: Controls::new(), + } + } +} + impl Content { fn new(id: usize) -> Self { Content { @@ -297,7 +316,31 @@ impl Content { } } +impl Controls { + fn new() -> Self { + Self { + close: button::State::new(), + } + } + + pub fn view( + &mut self, + pane: pane_grid::Pane, + total_panes: usize, + ) -> Element { + let mut button = + Button::new(&mut self.close, Text::new("Close").size(14)) + .style(style::Button::Control) + .padding(3); + if total_panes > 1 { + button = button.on_press(Message::Close(pane)); + } + button.into() + } +} + mod style { + use crate::PANE_ID_COLOR_FOCUSED; use iced::{button, container, Background, Color, Vector}; const SURFACE: Color = Color::from_rgb( @@ -359,6 +402,7 @@ mod style { pub enum Button { Primary, Destructive, + Control, } impl button::StyleSheet for Button { @@ -368,6 +412,7 @@ mod style { Button::Destructive => { (None, Color::from_rgb8(0xFF, 0x47, 0x47)) } + Button::Control => (Some(PANE_ID_COLOR_FOCUSED), Color::WHITE), }; button::Style { @@ -388,6 +433,7 @@ mod style { a: 0.2, ..active.text_color }), + Button::Control => Some(PANE_ID_COLOR_FOCUSED), }; button::Style { From 1a2fd4e743fe2b5237a4b51fa0ab2ddc660dfd5a Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Mon, 24 May 2021 15:53:20 -0500 Subject: [PATCH 2/3] Example: Add Pin button to prevent closing a pane Functionality will not work until PaneGrid implementation is updated to support events within the title area. --- examples/pane_grid/src/main.rs | 50 ++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 7c0d6582..81cf1770 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -24,6 +24,7 @@ enum Message { Clicked(pane_grid::Pane), Dragged(pane_grid::DragEvent), Resized(pane_grid::ResizeEvent), + TogglePin(pane_grid::Pane), Close(pane_grid::Pane), CloseFocused, } @@ -106,6 +107,12 @@ impl Application for Example { self.panes.swap(&pane, &target); } Message::Dragged(_) => {} + Message::TogglePin(pane) => { + if let Some(Pane { is_pinned, .. }) = self.panes.get_mut(&pane) + { + *is_pinned = !*is_pinned; + } + } Message::Close(pane) => { if let Some((_, sibling)) = self.panes.close(&pane) { self.focus = Some(sibling); @@ -113,8 +120,14 @@ impl Application for Example { } Message::CloseFocused => { if let Some(pane) = self.focus { - if let Some((_, sibling)) = self.panes.close(&pane) { - self.focus = Some(sibling); + if let Some(Pane { is_pinned, .. }) = self.panes.get(&pane) + { + if !is_pinned { + if let Some((_, sibling)) = self.panes.close(&pane) + { + self.focus = Some(sibling); + } + } } } } @@ -146,7 +159,15 @@ impl Application for Example { let pane_grid = PaneGrid::new(&mut self.panes, |id, pane| { let is_focused = focus == Some(id); + let text = if pane.is_pinned { "Unpin" } else { "Pin" }; + let pin_button = + Button::new(&mut pane.pin_button, Text::new(text).size(14)) + .on_press(Message::TogglePin(id)) + .style(style::Button::Pin) + .padding(3); + let title = Row::with_children(vec![ + pin_button.into(), Text::new("Pane").into(), Text::new(pane.content.id.to_string()) .color(if is_focused { @@ -159,13 +180,17 @@ impl Application for Example { .spacing(5); let title_bar = pane_grid::TitleBar::new(title) - .controls(pane.controls.view(id, total_panes)) + .controls(pane.controls.view(id, total_panes, pane.is_pinned)) .padding(10) .style(style::TitleBar { is_focused }); - pane_grid::Content::new(pane.content.view(id, total_panes)) - .title_bar(title_bar) - .style(style::Pane { is_focused }) + pane_grid::Content::new(pane.content.view( + id, + total_panes, + pane.is_pinned, + )) + .title_bar(title_bar) + .style(style::Pane { is_focused }) }) .width(Length::Fill) .height(Length::Fill) @@ -214,6 +239,8 @@ fn handle_hotkey(key_code: keyboard::KeyCode) -> Option { } struct Pane { + pub is_pinned: bool, + pub pin_button: button::State, pub content: Content, pub controls: Controls, } @@ -233,6 +260,8 @@ struct Controls { impl Pane { fn new(id: usize) -> Self { Self { + is_pinned: false, + pin_button: button::State::new(), content: Content::new(id), controls: Controls::new(), } @@ -253,6 +282,7 @@ impl Content { &mut self, pane: pane_grid::Pane, total_panes: usize, + is_pinned: bool, ) -> Element { let Content { scroll, @@ -292,7 +322,7 @@ impl Content { style::Button::Primary, )); - if total_panes > 1 { + if total_panes > 1 && !is_pinned { controls = controls.push(button( close, "Close", @@ -327,12 +357,13 @@ impl Controls { &mut self, pane: pane_grid::Pane, total_panes: usize, + is_pinned: bool, ) -> Element { let mut button = Button::new(&mut self.close, Text::new("Close").size(14)) .style(style::Button::Control) .padding(3); - if total_panes > 1 { + if total_panes > 1 && !is_pinned { button = button.on_press(Message::Close(pane)); } button.into() @@ -403,6 +434,7 @@ mod style { Primary, Destructive, Control, + Pin, } impl button::StyleSheet for Button { @@ -413,6 +445,7 @@ mod style { (None, Color::from_rgb8(0xFF, 0x47, 0x47)) } Button::Control => (Some(PANE_ID_COLOR_FOCUSED), Color::WHITE), + Button::Pin => (Some(ACTIVE), Color::WHITE), }; button::Style { @@ -434,6 +467,7 @@ mod style { ..active.text_color }), Button::Control => Some(PANE_ID_COLOR_FOCUSED), + Button::Pin => Some(HOVERED), }; button::Style { From d4c5f3ee950262c578c7b9b2a4aab60d3c5edaed Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Mon, 24 May 2021 16:37:47 -0500 Subject: [PATCH 3/3] Enable event handling within the title elements Shrink the pick area to avoid both the controls and the title elements. Handle events and merge title area event status with control events. --- graphics/src/widget/pane_grid.rs | 6 ++--- native/src/widget/pane_grid/title_bar.rs | 33 +++++++++++++++++------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/graphics/src/widget/pane_grid.rs b/graphics/src/widget/pane_grid.rs index d06f8c6c..93b4b672 100644 --- a/graphics/src/widget/pane_grid.rs +++ b/graphics/src/widget/pane_grid.rs @@ -218,10 +218,10 @@ where body_primitive, ], }, - if is_over_pick_area { - mouse::Interaction::Grab - } else if title_bar_interaction > body_interaction { + if title_bar_interaction > body_interaction { title_bar_interaction + } else if is_over_pick_area { + mouse::Interaction::Grab } else { body_interaction }, diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs index a1e5107e..8e42ce38 100644 --- a/native/src/widget/pane_grid/title_bar.rs +++ b/native/src/widget/pane_grid/title_bar.rs @@ -129,15 +129,16 @@ where 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 mut children = padded.children(); - let _ = children.next().unwrap(); let controls_layout = children.next().unwrap(); !controls_layout.bounds().contains(cursor_position) + && !title_layout.bounds().contains(cursor_position) } else { - true + !title_layout.bounds().contains(cursor_position) } } else { false @@ -205,16 +206,17 @@ where clipboard: &mut dyn Clipboard, messages: &mut Vec, ) -> event::Status { - if let Some(controls) = &mut self.controls { - let mut children = layout.children(); - let padded = children.next().unwrap(); + let mut children = layout.children(); + let padded = children.next().unwrap(); - let mut children = padded.children(); - let _ = children.next(); + let mut children = padded.children(); + let title_layout = children.next().unwrap(); + + let control_status = if let Some(controls) = &mut self.controls { let controls_layout = children.next().unwrap(); controls.on_event( - event, + event.clone(), controls_layout, cursor_position, renderer, @@ -223,6 +225,17 @@ where ) } else { event::Status::Ignored - } + }; + + let title_status = self.content.on_event( + event, + title_layout, + cursor_position, + renderer, + clipboard, + messages, + ); + + control_status.merge(title_status) } }