diff options
author | 2020-06-08 18:33:36 +0200 | |
---|---|---|
committer | 2020-06-08 18:33:36 +0200 | |
commit | 8493ccec7fd4639aef9e02ad3519a7a3707cd488 (patch) | |
tree | 528fc7245a21fc5f362cca2bec609e8e9aaed4fb /native | |
parent | 4dc5bffdfbfb09a017f35c12b484301fcf044876 (diff) | |
parent | dcc4bb77e942f8550b0d7ee08fa5a2882fd22ecd (diff) | |
download | iced-8493ccec7fd4639aef9e02ad3519a7a3707cd488.tar.gz iced-8493ccec7fd4639aef9e02ad3519a7a3707cd488.tar.bz2 iced-8493ccec7fd4639aef9e02ad3519a7a3707cd488.zip |
Merge branch 'intuitive-pane-grid-resize' into feature/pane-grid-titlebar
Diffstat (limited to 'native')
-rw-r--r-- | native/src/widget/pane_grid.rs | 237 | ||||
-rw-r--r-- | native/src/widget/pane_grid/axis.rs | 24 | ||||
-rw-r--r-- | native/src/widget/slider.rs | 25 |
3 files changed, 179 insertions, 107 deletions
diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index c0c3839e..37b778cc 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -95,7 +95,7 @@ pub struct PaneGrid<'a, Message, Renderer: container::Renderer> { spacing: u16, modifier_keys: keyboard::ModifiersState, on_drag: Option<Box<dyn Fn(DragEvent) -> Message + 'a>>, - on_resize: Option<Box<dyn Fn(ResizeEvent) -> Message + 'a>>, + on_resize: Option<(u16, Box<dyn Fn(ResizeEvent) -> Message + 'a>)>, on_key_press: Option<Box<dyn Fn(KeyPressEvent) -> Option<Message> + 'a>>, } @@ -184,8 +184,8 @@ where /// Sets the modifier keys of the [`PaneGrid`]. /// - /// The modifier keys will need to be pressed to trigger dragging, resizing, - /// and key events. + /// The modifier keys will need to be pressed to trigger dragging, and key + /// events. /// /// The default modifier key is `Ctrl`. /// @@ -201,8 +201,6 @@ where /// Enables the drag and drop interactions of the [`PaneGrid`], which will /// use the provided function to produce messages. /// - /// Panes can be dragged using `Modifier keys + Left click`. - /// /// [`PaneGrid`]: struct.PaneGrid.html pub fn on_drag<F>(mut self, f: F) -> Self where @@ -215,14 +213,15 @@ where /// Enables the resize interactions of the [`PaneGrid`], which will /// use the provided function to produce messages. /// - /// Panes can be resized using `Modifier keys + Right click`. + /// The `leeway` describes the amount of space around a split that can be + /// used to grab it. /// /// [`PaneGrid`]: struct.PaneGrid.html - pub fn on_resize<F>(mut self, f: F) -> Self + pub fn on_resize<F>(mut self, leeway: u16, f: F) -> Self where F: 'a + Fn(ResizeEvent) -> Message, { - self.on_resize = Some(Box::new(f)); + self.on_resize = Some((leeway, Box::new(f))); self } @@ -250,6 +249,45 @@ where self.on_key_press = Some(Box::new(f)); self } +} + +impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> +where + Renderer: self::Renderer, +{ + fn click_pane( + &mut self, + layout: Layout<'_>, + cursor_position: Point, + messages: &mut Vec<Message>, + ) { + let mut clicked_region = + self.elements.iter().zip(layout.children()).filter( + |(_, layout)| layout.bounds().contains(cursor_position), + ); + + if let Some(((pane, content), layout)) = clicked_region.next() { + match &self.on_drag { + Some(on_drag) => { + if let Some(origin) = + content.drag_origin(layout, cursor_position) + { + self.state.pick_pane(pane, origin); + + messages + .push(on_drag(DragEvent::Picked { pane: *pane })); + } else { + self.state.focus(pane); + } + } + None => { + self.state.focus(pane); + } + } + } else { + self.state.unfocus(); + } + } fn trigger_resize( &mut self, @@ -257,7 +295,7 @@ where cursor_position: Point, messages: &mut Vec<Message>, ) { - if let Some(on_resize) = &self.on_resize { + if let Some((_, on_resize)) = &self.on_resize { if let Some((split, _)) = self.state.picked_split() { let bounds = layout.bounds(); @@ -416,36 +454,45 @@ where match event { Event::Mouse(mouse_event) => match mouse_event { mouse::Event::ButtonPressed(mouse::Button::Left) => { - let mut clicked_region = - self.elements.iter().zip(layout.children()).filter( - |(_, layout)| { - layout.bounds().contains(cursor_position) - }, - ); - - if let Some(((pane, content), layout)) = - clicked_region.next() - { - match &self.on_drag { - Some(on_drag) => { - if let Some(origin) = - content.drag_origin(layout, cursor_position) - { - self.state.pick_pane(pane, origin); - - messages.push(on_drag(DragEvent::Picked { - pane: *pane, - })); + let bounds = layout.bounds(); + + if bounds.contains(cursor_position) { + match self.on_resize { + Some((leeway, _)) => { + let relative_cursor = Point::new( + cursor_position.x - bounds.x, + cursor_position.y - bounds.y, + ); + + let splits = self.state.splits( + f32::from(self.spacing), + Size::new(bounds.width, bounds.height), + ); + + let clicked_split = hovered_split( + splits.iter(), + f32::from(self.spacing + leeway), + relative_cursor, + ); + + if let Some((split, axis)) = clicked_split { + self.state.pick_split(&split, axis); } else { - self.state.focus(pane); + self.click_pane( + layout, + cursor_position, + messages, + ); } } - _ => { - self.state.focus(pane); + None => { + self.click_pane( + layout, + cursor_position, + messages, + ); } } - } else { - self.state.unfocus(); } } mouse::Event::ButtonReleased(mouse::Button::Left) => { @@ -473,78 +520,10 @@ where messages.push(on_drag(event)); } + } else if self.state.picked_split().is_some() { + self.state.drop_split(); } } - mouse::Event::ButtonPressed(mouse::Button::Right) - if self.on_resize.is_some() - && self.state.picked_pane().is_none() - && self - .pressed_modifiers - .matches(self.modifier_keys) => - { - let bounds = layout.bounds(); - - if bounds.contains(cursor_position) { - let relative_cursor = Point::new( - cursor_position.x - bounds.x, - cursor_position.y - bounds.y, - ); - - let splits = self.state.splits( - f32::from(self.spacing), - Size::new(bounds.width, bounds.height), - ); - - let mut sorted_splits: Vec<_> = splits - .iter() - .filter(|(_, (axis, rectangle, _))| match axis { - Axis::Horizontal => { - relative_cursor.x > rectangle.x - && relative_cursor.x - < rectangle.x + rectangle.width - } - Axis::Vertical => { - relative_cursor.y > rectangle.y - && relative_cursor.y - < rectangle.y + rectangle.height - } - }) - .collect(); - - sorted_splits.sort_by_key( - |(_, (axis, rectangle, ratio))| { - let distance = match axis { - Axis::Horizontal => (relative_cursor.y - - (rectangle.y - + rectangle.height * ratio)) - .abs(), - Axis::Vertical => (relative_cursor.x - - (rectangle.x - + rectangle.width * ratio)) - .abs(), - }; - - distance.round() as u32 - }, - ); - - if let Some((split, (axis, _, _))) = - sorted_splits.first() - { - self.state.pick_split(split, *axis); - self.trigger_resize( - layout, - cursor_position, - messages, - ); - } - } - } - mouse::Event::ButtonReleased(mouse::Button::Right) - if self.state.picked_split().is_some() => - { - self.state.drop_split(); - } mouse::Event::CursorMoved { .. } => { self.trigger_resize(layout, cursor_position, messages); } @@ -608,12 +587,38 @@ where layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { + let picked_split = self + .state + .picked_split() + .or_else(|| match self.on_resize { + Some((leeway, _)) => { + let bounds = layout.bounds(); + + let relative_cursor = Point::new( + cursor_position.x - bounds.x, + cursor_position.y - bounds.y, + ); + + let splits = self + .state + .splits(f32::from(self.spacing), bounds.size()); + + hovered_split( + splits.iter(), + f32::from(self.spacing + leeway), + relative_cursor, + ) + } + None => None, + }) + .map(|(_, axis)| axis); + self::Renderer::draw( renderer, defaults, &self.elements, self.state.picked_pane(), - self.state.picked_split().map(|(_, axis)| axis), + picked_split, layout, cursor_position, ) @@ -707,3 +712,25 @@ where Element::new(pane_grid) } } + +/* + * Helpers + */ +fn hovered_split<'a>( + splits: impl Iterator<Item = (&'a Split, &'a (Axis, Rectangle, f32))>, + spacing: f32, + cursor_position: Point, +) -> Option<(Split, Axis)> { + splits + .filter_map(|(split, (axis, region, ratio))| { + let bounds = + axis.split_line_bounds(*region, *ratio, f32::from(spacing)); + + if bounds.contains(cursor_position) { + Some((*split, *axis)) + } else { + None + } + }) + .next() +} diff --git a/native/src/widget/pane_grid/axis.rs b/native/src/widget/pane_grid/axis.rs index b3a306d5..2320cb7c 100644 --- a/native/src/widget/pane_grid/axis.rs +++ b/native/src/widget/pane_grid/axis.rs @@ -53,6 +53,30 @@ impl Axis { } } } + + pub(super) 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)] diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 8cdfc3de..753a49fe 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -40,6 +40,7 @@ pub struct Slider<'a, Message, Renderer: self::Renderer> { range: RangeInclusive<f32>, value: f32, on_change: Box<dyn Fn(f32) -> Message>, + on_release: Option<Message>, width: Length, style: Renderer::Style, } @@ -71,11 +72,25 @@ impl<'a, Message, Renderer: self::Renderer> Slider<'a, Message, Renderer> { value: value.max(*range.start()).min(*range.end()), range, on_change: Box::new(on_change), + on_release: None, width: Length::Fill, style: Renderer::Style::default(), } } + /// Sets the release message of the [`Slider`]. + /// This is called when the mouse is released from the slider. + /// + /// Typically, the user's interaction with the slider is finished when this message is produced. + /// This is useful if you need to spawn a long-running task from the slider's result, where + /// the default on_change message could create too many events. + /// + /// [`Slider`]: struct.Slider.html + pub fn on_release(mut self, on_release: Message) -> Self { + self.on_release = Some(on_release); + self + } + /// Sets the width of the [`Slider`]. /// /// [`Slider`]: struct.Slider.html @@ -114,6 +129,7 @@ impl<'a, Message, Renderer> Widget<Message, Renderer> for Slider<'a, Message, Renderer> where Renderer: self::Renderer, + Message: Clone, { fn width(&self) -> Length { self.width @@ -171,7 +187,12 @@ where } } mouse::Event::ButtonReleased(mouse::Button::Left) => { - self.state.is_dragging = false; + if self.state.is_dragging { + if let Some(on_release) = self.on_release.clone() { + messages.push(on_release); + } + self.state.is_dragging = false; + } } mouse::Event::CursorMoved { .. } => { if self.state.is_dragging { @@ -252,7 +273,7 @@ impl<'a, Message, Renderer> From<Slider<'a, Message, Renderer>> for Element<'a, Message, Renderer> where Renderer: 'a + self::Renderer, - Message: 'a, + Message: 'a + Clone, { fn from( slider: Slider<'a, Message, Renderer>, |