Merge pull request #608 from hecrj/remove-pane-grid-focus
Improve flexibility of `PaneGrid`
This commit is contained in:
		
						commit
						2f5a3dacd9
					
				| @ -15,6 +15,26 @@ pub struct ModifiersState { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl ModifiersState { | impl ModifiersState { | ||||||
|  |     /// Returns true if the current [`ModifiersState`] has a "command key"
 | ||||||
|  |     /// pressed.
 | ||||||
|  |     ///
 | ||||||
|  |     /// The "command key" is the main modifier key used to issue commands in the
 | ||||||
|  |     /// current platform. Specifically:
 | ||||||
|  |     ///
 | ||||||
|  |     /// - It is the `logo` or command key (⌘) on macOS
 | ||||||
|  |     /// - It is the `control` key on other platforms
 | ||||||
|  |     ///
 | ||||||
|  |     /// [`ModifiersState`]: struct.ModifiersState.html
 | ||||||
|  |     pub fn is_command_pressed(self) -> bool { | ||||||
|  |         #[cfg(target_os = "macos")] | ||||||
|  |         let is_pressed = self.logo; | ||||||
|  | 
 | ||||||
|  |         #[cfg(not(target_os = "macos"))] | ||||||
|  |         let is_pressed = self.control; | ||||||
|  | 
 | ||||||
|  |         is_pressed | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /// Returns true if the current [`ModifiersState`] has at least the same
 |     /// Returns true if the current [`ModifiersState`] has at least the same
 | ||||||
|     /// modifiers enabled as the given value, and false otherwise.
 |     /// modifiers enabled as the given value, and false otherwise.
 | ||||||
|     ///
 |     ///
 | ||||||
|  | |||||||
| @ -6,4 +6,5 @@ edition = "2018" | |||||||
| publish = false | publish = false | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| iced = { path = "../.." } | iced = { path = "../..", features = ["debug"] } | ||||||
|  | iced_native = { path = "../../native" } | ||||||
|  | |||||||
| @ -1,8 +1,9 @@ | |||||||
| use iced::{ | use iced::{ | ||||||
|     button, keyboard, pane_grid, scrollable, Align, Button, Column, Container, |     button, executor, keyboard, pane_grid, scrollable, Align, Application, | ||||||
|     Element, HorizontalAlignment, Length, PaneGrid, Sandbox, Scrollable, |     Button, Column, Command, Container, Element, HorizontalAlignment, Length, | ||||||
|     Settings, Text, |     PaneGrid, Scrollable, Settings, Subscription, Text, | ||||||
| }; | }; | ||||||
|  | use iced_native::{subscription, Event}; | ||||||
| 
 | 
 | ||||||
| pub fn main() -> iced::Result { | pub fn main() -> iced::Result { | ||||||
|     Example::run(Settings::default()) |     Example::run(Settings::default()) | ||||||
| @ -11,6 +12,7 @@ pub fn main() -> iced::Result { | |||||||
| struct Example { | struct Example { | ||||||
|     panes: pane_grid::State<Content>, |     panes: pane_grid::State<Content>, | ||||||
|     panes_created: usize, |     panes_created: usize, | ||||||
|  |     focus: Option<pane_grid::Pane>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Clone, Copy)] | #[derive(Debug, Clone, Copy)] | ||||||
| @ -18,59 +20,77 @@ enum Message { | |||||||
|     Split(pane_grid::Axis, pane_grid::Pane), |     Split(pane_grid::Axis, pane_grid::Pane), | ||||||
|     SplitFocused(pane_grid::Axis), |     SplitFocused(pane_grid::Axis), | ||||||
|     FocusAdjacent(pane_grid::Direction), |     FocusAdjacent(pane_grid::Direction), | ||||||
|  |     Clicked(pane_grid::Pane), | ||||||
|     Dragged(pane_grid::DragEvent), |     Dragged(pane_grid::DragEvent), | ||||||
|     Resized(pane_grid::ResizeEvent), |     Resized(pane_grid::ResizeEvent), | ||||||
|     Close(pane_grid::Pane), |     Close(pane_grid::Pane), | ||||||
|     CloseFocused, |     CloseFocused, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Sandbox for Example { | impl Application for Example { | ||||||
|     type Message = Message; |     type Message = Message; | ||||||
|  |     type Executor = executor::Default; | ||||||
|  |     type Flags = (); | ||||||
| 
 | 
 | ||||||
|     fn new() -> Self { |     fn new(_flags: ()) -> (Self, Command<Message>) { | ||||||
|         let (panes, _) = pane_grid::State::new(Content::new(0)); |         let (panes, _) = pane_grid::State::new(Content::new(0)); | ||||||
| 
 | 
 | ||||||
|  |         ( | ||||||
|             Example { |             Example { | ||||||
|                 panes, |                 panes, | ||||||
|                 panes_created: 1, |                 panes_created: 1, | ||||||
|         } |                 focus: None, | ||||||
|  |             }, | ||||||
|  |             Command::none(), | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn title(&self) -> String { |     fn title(&self) -> String { | ||||||
|         String::from("Pane grid - Iced") |         String::from("Pane grid - Iced") | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn update(&mut self, message: Message) { |     fn update(&mut self, message: Message) -> Command<Message> { | ||||||
|         match message { |         match message { | ||||||
|             Message::Split(axis, pane) => { |             Message::Split(axis, pane) => { | ||||||
|                 let _ = self.panes.split( |                 let result = self.panes.split( | ||||||
|                     axis, |                     axis, | ||||||
|                     &pane, |                     &pane, | ||||||
|                     Content::new(self.panes_created), |                     Content::new(self.panes_created), | ||||||
|                 ); |                 ); | ||||||
| 
 | 
 | ||||||
|  |                 if let Some((pane, _)) = result { | ||||||
|  |                     self.focus = Some(pane); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|                 self.panes_created += 1; |                 self.panes_created += 1; | ||||||
|             } |             } | ||||||
|             Message::SplitFocused(axis) => { |             Message::SplitFocused(axis) => { | ||||||
|                 if let Some(pane) = self.panes.active() { |                 if let Some(pane) = self.focus { | ||||||
|                     let _ = self.panes.split( |                     let result = self.panes.split( | ||||||
|                         axis, |                         axis, | ||||||
|                         &pane, |                         &pane, | ||||||
|                         Content::new(self.panes_created), |                         Content::new(self.panes_created), | ||||||
|                     ); |                     ); | ||||||
| 
 | 
 | ||||||
|  |                     if let Some((pane, _)) = result { | ||||||
|  |                         self.focus = Some(pane); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|                     self.panes_created += 1; |                     self.panes_created += 1; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             Message::FocusAdjacent(direction) => { |             Message::FocusAdjacent(direction) => { | ||||||
|                 if let Some(pane) = self.panes.active() { |                 if let Some(pane) = self.focus { | ||||||
|                     if let Some(adjacent) = |                     if let Some(adjacent) = | ||||||
|                         self.panes.adjacent(&pane, direction) |                         self.panes.adjacent(&pane, direction) | ||||||
|                     { |                     { | ||||||
|                         self.panes.focus(&adjacent); |                         self.focus = Some(adjacent); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |             Message::Clicked(pane) => { | ||||||
|  |                 self.focus = Some(pane); | ||||||
|  |             } | ||||||
|             Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { |             Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { | ||||||
|                 self.panes.resize(&split, ratio); |                 self.panes.resize(&split, ratio); | ||||||
|             } |             } | ||||||
| @ -82,22 +102,39 @@ impl Sandbox for Example { | |||||||
|             } |             } | ||||||
|             Message::Dragged(_) => {} |             Message::Dragged(_) => {} | ||||||
|             Message::Close(pane) => { |             Message::Close(pane) => { | ||||||
|                 let _ = self.panes.close(&pane); |                 if let Some((_, sibling)) = self.panes.close(&pane) { | ||||||
|  |                     self.focus = Some(sibling); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|             Message::CloseFocused => { |             Message::CloseFocused => { | ||||||
|                 if let Some(pane) = self.panes.active() { |                 if let Some(pane) = self.focus { | ||||||
|                     let _ = self.panes.close(&pane); |                     if let Some((_, sibling)) = self.panes.close(&pane) { | ||||||
|  |                         self.focus = Some(sibling); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         Command::none() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn subscription(&self) -> Subscription<Message> { | ||||||
|  |         subscription::events_with(|event| match event { | ||||||
|  |             Event::Keyboard(keyboard::Event::KeyPressed { | ||||||
|  |                 modifiers, | ||||||
|  |                 key_code, | ||||||
|  |             }) if modifiers.is_command_pressed() => handle_hotkey(key_code), | ||||||
|  |             _ => None, | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     fn view(&mut self) -> Element<Message> { |     fn view(&mut self) -> Element<Message> { | ||||||
|  |         let focus = self.focus; | ||||||
|         let total_panes = self.panes.len(); |         let total_panes = self.panes.len(); | ||||||
| 
 | 
 | ||||||
|         let pane_grid = |         let pane_grid = PaneGrid::new(&mut self.panes, |pane, content| { | ||||||
|             PaneGrid::new(&mut self.panes, |pane, content, focus| { |             let is_focused = focus == Some(pane); | ||||||
|                 let is_focused = focus.is_some(); | 
 | ||||||
|             let title_bar = |             let title_bar = | ||||||
|                 pane_grid::TitleBar::new(format!("Pane {}", content.id)) |                 pane_grid::TitleBar::new(format!("Pane {}", content.id)) | ||||||
|                     .padding(10) |                     .padding(10) | ||||||
| @ -110,9 +147,9 @@ impl Sandbox for Example { | |||||||
|         .width(Length::Fill) |         .width(Length::Fill) | ||||||
|         .height(Length::Fill) |         .height(Length::Fill) | ||||||
|         .spacing(10) |         .spacing(10) | ||||||
|  |         .on_click(Message::Clicked) | ||||||
|         .on_drag(Message::Dragged) |         .on_drag(Message::Dragged) | ||||||
|             .on_resize(10, Message::Resized) |         .on_resize(10, Message::Resized); | ||||||
|             .on_key_press(handle_hotkey); |  | ||||||
| 
 | 
 | ||||||
|         Container::new(pane_grid) |         Container::new(pane_grid) | ||||||
|             .width(Length::Fill) |             .width(Length::Fill) | ||||||
| @ -122,11 +159,11 @@ impl Sandbox for Example { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn handle_hotkey(event: pane_grid::KeyPressEvent) -> Option<Message> { | fn handle_hotkey(key_code: keyboard::KeyCode) -> Option<Message> { | ||||||
|     use keyboard::KeyCode; |     use keyboard::KeyCode; | ||||||
|     use pane_grid::{Axis, Direction}; |     use pane_grid::{Axis, Direction}; | ||||||
| 
 | 
 | ||||||
|     let direction = match event.key_code { |     let direction = match key_code { | ||||||
|         KeyCode::Up => Some(Direction::Up), |         KeyCode::Up => Some(Direction::Up), | ||||||
|         KeyCode::Down => Some(Direction::Down), |         KeyCode::Down => Some(Direction::Down), | ||||||
|         KeyCode::Left => Some(Direction::Left), |         KeyCode::Left => Some(Direction::Left), | ||||||
| @ -134,7 +171,7 @@ fn handle_hotkey(event: pane_grid::KeyPressEvent) -> Option<Message> { | |||||||
|         _ => None, |         _ => None, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     match event.key_code { |     match key_code { | ||||||
|         KeyCode::V => Some(Message::SplitFocused(Axis::Vertical)), |         KeyCode::V => Some(Message::SplitFocused(Axis::Vertical)), | ||||||
|         KeyCode::H => Some(Message::SplitFocused(Axis::Horizontal)), |         KeyCode::H => Some(Message::SplitFocused(Axis::Horizontal)), | ||||||
|         KeyCode::W => Some(Message::CloseFocused), |         KeyCode::W => Some(Message::CloseFocused), | ||||||
|  | |||||||
| @ -11,8 +11,8 @@ | |||||||
| use crate::Renderer; | use crate::Renderer; | ||||||
| 
 | 
 | ||||||
| pub use iced_native::pane_grid::{ | pub use iced_native::pane_grid::{ | ||||||
|     Axis, Configuration, Direction, DragEvent, Focus, KeyPressEvent, Node, |     Axis, Configuration, Direction, DragEvent, Focus, Node, Pane, ResizeEvent, | ||||||
|     Pane, ResizeEvent, Split, State, |     Split, State, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// A collection of panes distributed using either vertical or horizontal splits
 | /// A collection of panes distributed using either vertical or horizontal splits
 | ||||||
|  | |||||||
| @ -20,8 +20,8 @@ use iced_native::{ | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| pub use iced_native::pane_grid::{ | pub use iced_native::pane_grid::{ | ||||||
|     Axis, Configuration, Content, Direction, DragEvent, Focus, KeyPressEvent, |     Axis, Configuration, Content, Direction, DragEvent, Focus, Pane, | ||||||
|     Pane, ResizeEvent, Split, State, TitleBar, |     ResizeEvent, Split, State, TitleBar, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// A collection of panes distributed using either vertical or horizontal splits
 | /// A collection of panes distributed using either vertical or horizontal splits
 | ||||||
|  | |||||||
| @ -43,5 +43,25 @@ use events::Events; | |||||||
| /// [`Subscription`]: type.Subscription.html
 | /// [`Subscription`]: type.Subscription.html
 | ||||||
| /// [`Event`]: ../enum.Event.html
 | /// [`Event`]: ../enum.Event.html
 | ||||||
| pub fn events() -> Subscription<Event> { | pub fn events() -> Subscription<Event> { | ||||||
|     Subscription::from_recipe(Events) |     Subscription::from_recipe(Events { f: Some }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Returns a [`Subscription`] that filters all the runtime events with the
 | ||||||
|  | /// provided function, producing messages accordingly.
 | ||||||
|  | ///
 | ||||||
|  | /// This subscription will call the provided function for every [`Event`]
 | ||||||
|  | /// handled by the runtime. If the function:
 | ||||||
|  | ///
 | ||||||
|  | /// - Returns `None`, the [`Event`] will be discarded.
 | ||||||
|  | /// - Returns `Some` message, the `Message` will be produced.
 | ||||||
|  | ///
 | ||||||
|  | /// [`Subscription`]: type.Subscription.html
 | ||||||
|  | /// [`Event`]: ../enum.Event.html
 | ||||||
|  | pub fn events_with<Message>( | ||||||
|  |     f: fn(Event) -> Option<Message>, | ||||||
|  | ) -> Subscription<Message> | ||||||
|  | where | ||||||
|  |     Message: 'static + Send, | ||||||
|  | { | ||||||
|  |     Subscription::from_recipe(Events { f }) | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,17 +2,26 @@ use crate::{ | |||||||
|     subscription::{EventStream, Recipe}, |     subscription::{EventStream, Recipe}, | ||||||
|     Event, Hasher, |     Event, Hasher, | ||||||
| }; | }; | ||||||
|  | use iced_futures::futures::future; | ||||||
|  | use iced_futures::futures::StreamExt; | ||||||
| use iced_futures::BoxStream; | use iced_futures::BoxStream; | ||||||
| 
 | 
 | ||||||
| pub struct Events; | pub struct Events<Message> { | ||||||
|  |     pub(super) f: fn(Event) -> Option<Message>, | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| impl Recipe<Hasher, Event> for Events { | impl<Message> Recipe<Hasher, Event> for Events<Message> | ||||||
|     type Output = Event; | where | ||||||
|  |     Message: 'static + Send, | ||||||
|  | { | ||||||
|  |     type Output = Message; | ||||||
| 
 | 
 | ||||||
|     fn hash(&self, state: &mut Hasher) { |     fn hash(&self, state: &mut Hasher) { | ||||||
|         use std::hash::Hash; |         use std::hash::Hash; | ||||||
| 
 | 
 | ||||||
|         std::any::TypeId::of::<Self>().hash(state); |         struct Marker; | ||||||
|  |         std::any::TypeId::of::<Marker>().hash(state); | ||||||
|  |         self.f.hash(state); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn stream( |     fn stream( | ||||||
| @ -20,5 +29,7 @@ impl Recipe<Hasher, Event> for Events { | |||||||
|         event_stream: EventStream, |         event_stream: EventStream, | ||||||
|     ) -> BoxStream<Self::Output> { |     ) -> BoxStream<Self::Output> { | ||||||
|         event_stream |         event_stream | ||||||
|  |             .filter_map(move |event| future::ready((self.f)(event))) | ||||||
|  |             .boxed() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -29,8 +29,8 @@ pub use state::{Focus, State}; | |||||||
| pub use title_bar::TitleBar; | pub use title_bar::TitleBar; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     container, keyboard, layout, mouse, overlay, row, text, Clipboard, Element, |     container, layout, mouse, overlay, row, text, Clipboard, Element, Event, | ||||||
|     Event, Hasher, Layout, Length, Point, Rectangle, Size, Vector, Widget, |     Hasher, Layout, Length, Point, Rectangle, Size, Vector, Widget, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// A collection of panes distributed using either vertical or horizontal splits
 | /// A collection of panes distributed using either vertical or horizontal splits
 | ||||||
| @ -73,7 +73,7 @@ use crate::{ | |||||||
| /// let (mut state, _) = pane_grid::State::new(PaneState::SomePane);
 | /// let (mut state, _) = pane_grid::State::new(PaneState::SomePane);
 | ||||||
| ///
 | ///
 | ||||||
| /// let pane_grid =
 | /// let pane_grid =
 | ||||||
| ///     PaneGrid::new(&mut state, |pane, state, focus| {
 | ///     PaneGrid::new(&mut state, |pane, state| {
 | ||||||
| ///         pane_grid::Content::new(match state {
 | ///         pane_grid::Content::new(match state {
 | ||||||
| ///             PaneState::SomePane => Text::new("This is some pane"),
 | ///             PaneState::SomePane => Text::new("This is some pane"),
 | ||||||
| ///             PaneState::AnotherKindOfPane => Text::new("This is another kind of pane"),
 | ///             PaneState::AnotherKindOfPane => Text::new("This is another kind of pane"),
 | ||||||
| @ -92,10 +92,9 @@ pub struct PaneGrid<'a, Message, Renderer: self::Renderer> { | |||||||
|     width: Length, |     width: Length, | ||||||
|     height: Length, |     height: Length, | ||||||
|     spacing: u16, |     spacing: u16, | ||||||
|     modifier_keys: keyboard::ModifiersState, |     on_click: Option<Box<dyn Fn(Pane) -> Message + 'a>>, | ||||||
|     on_drag: Option<Box<dyn Fn(DragEvent) -> Message + 'a>>, |     on_drag: Option<Box<dyn Fn(DragEvent) -> Message + 'a>>, | ||||||
|     on_resize: Option<(u16, 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>>, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> | impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> | ||||||
| @ -112,31 +111,13 @@ where | |||||||
|     /// [`Pane`]: struct.Pane.html
 |     /// [`Pane`]: struct.Pane.html
 | ||||||
|     pub fn new<T>( |     pub fn new<T>( | ||||||
|         state: &'a mut State<T>, |         state: &'a mut State<T>, | ||||||
|         view: impl Fn( |         view: impl Fn(Pane, &'a mut T) -> Content<'a, Message, Renderer>, | ||||||
|             Pane, |  | ||||||
|             &'a mut T, |  | ||||||
|             Option<Focus>, |  | ||||||
|         ) -> Content<'a, Message, Renderer>, |  | ||||||
|     ) -> Self { |     ) -> Self { | ||||||
|         let elements = { |         let elements = { | ||||||
|             let action = state.internal.action(); |  | ||||||
|             let current_focus = action.focus(); |  | ||||||
| 
 |  | ||||||
|             state |             state | ||||||
|                 .panes |                 .panes | ||||||
|                 .iter_mut() |                 .iter_mut() | ||||||
|                 .map(move |(pane, pane_state)| { |                 .map(|(pane, pane_state)| (*pane, view(*pane, pane_state))) | ||||||
|                     let focus = match current_focus { |  | ||||||
|                         Some((focused_pane, focus)) |  | ||||||
|                             if *pane == focused_pane => |  | ||||||
|                         { |  | ||||||
|                             Some(focus) |  | ||||||
|                         } |  | ||||||
|                         _ => None, |  | ||||||
|                     }; |  | ||||||
| 
 |  | ||||||
|                     (*pane, view(*pane, pane_state, focus)) |  | ||||||
|                 }) |  | ||||||
|                 .collect() |                 .collect() | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
| @ -146,13 +127,9 @@ where | |||||||
|             width: Length::Fill, |             width: Length::Fill, | ||||||
|             height: Length::Fill, |             height: Length::Fill, | ||||||
|             spacing: 0, |             spacing: 0, | ||||||
|             modifier_keys: keyboard::ModifiersState { |             on_click: None, | ||||||
|                 control: true, |  | ||||||
|                 ..Default::default() |  | ||||||
|             }, |  | ||||||
|             on_drag: None, |             on_drag: None, | ||||||
|             on_resize: None, |             on_resize: None, | ||||||
|             on_key_press: None, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -180,18 +157,16 @@ where | |||||||
|         self |         self | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Sets the modifier keys of the [`PaneGrid`].
 |     /// Sets the message that will be produced when a [`Pane`] of the
 | ||||||
|     ///
 |     /// [`PaneGrid`] is clicked.
 | ||||||
|     /// The modifier keys will need to be pressed to trigger key events.
 |  | ||||||
|     ///
 |  | ||||||
|     /// The default modifier key is `Ctrl`.
 |  | ||||||
|     ///
 |     ///
 | ||||||
|  |     /// [`Pane`]: struct.Pane.html
 | ||||||
|     /// [`PaneGrid`]: struct.PaneGrid.html
 |     /// [`PaneGrid`]: struct.PaneGrid.html
 | ||||||
|     pub fn modifier_keys( |     pub fn on_click<F>(mut self, f: F) -> Self | ||||||
|         mut self, |     where | ||||||
|         modifier_keys: keyboard::ModifiersState, |         F: 'a + Fn(Pane) -> Message, | ||||||
|     ) -> Self { |     { | ||||||
|         self.modifier_keys = modifier_keys; |         self.on_click = Some(Box::new(f)); | ||||||
|         self |         self | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -225,31 +200,6 @@ where | |||||||
|         self.on_resize = Some((leeway, Box::new(f))); |         self.on_resize = Some((leeway, Box::new(f))); | ||||||
|         self |         self | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     /// Captures hotkey interactions with the [`PaneGrid`], using the provided
 |  | ||||||
|     /// function to produce messages.
 |  | ||||||
|     ///
 |  | ||||||
|     /// The function will be called when:
 |  | ||||||
|     ///   - a [`Pane`] is focused
 |  | ||||||
|     ///   - a key is pressed
 |  | ||||||
|     ///   - all the modifier keys are pressed
 |  | ||||||
|     ///
 |  | ||||||
|     /// If the function returns `None`, the key press event will be discarded
 |  | ||||||
|     /// without producing any message.
 |  | ||||||
|     ///
 |  | ||||||
|     /// This method is particularly useful to implement hotkey interactions.
 |  | ||||||
|     /// For instance, you can use it to enable splitting, swapping, or resizing
 |  | ||||||
|     /// panes by pressing combinations of keys.
 |  | ||||||
|     ///
 |  | ||||||
|     /// [`PaneGrid`]: struct.PaneGrid.html
 |  | ||||||
|     /// [`Pane`]: struct.Pane.html
 |  | ||||||
|     pub fn on_key_press<F>(mut self, f: F) -> Self |  | ||||||
|     where |  | ||||||
|         F: 'a + Fn(KeyPressEvent) -> Option<Message>, |  | ||||||
|     { |  | ||||||
|         self.on_key_press = Some(Box::new(f)); |  | ||||||
|         self |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> | impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> | ||||||
| @ -268,8 +218,11 @@ where | |||||||
|             ); |             ); | ||||||
| 
 | 
 | ||||||
|         if let Some(((pane, content), layout)) = clicked_region.next() { |         if let Some(((pane, content), layout)) = clicked_region.next() { | ||||||
|             match &self.on_drag { |             if let Some(on_click) = &self.on_click { | ||||||
|                 Some(on_drag) => { |                 messages.push(on_click(*pane)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if let Some(on_drag) = &self.on_drag { | ||||||
|                 if content.can_be_picked_at(layout, cursor_position) { |                 if content.can_be_picked_at(layout, cursor_position) { | ||||||
|                     let pane_position = layout.position(); |                     let pane_position = layout.position(); | ||||||
| 
 | 
 | ||||||
| @ -278,14 +231,7 @@ where | |||||||
| 
 | 
 | ||||||
|                     self.state.pick_pane(pane, origin); |                     self.state.pick_pane(pane, origin); | ||||||
| 
 | 
 | ||||||
|                         messages |                     messages.push(on_drag(DragEvent::Picked { pane: *pane })); | ||||||
|                             .push(on_drag(DragEvent::Picked { pane: *pane })); |  | ||||||
|                     } else { |  | ||||||
|                         self.state.focus(pane); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 None => { |  | ||||||
|                     self.state.focus(pane); |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -390,18 +336,6 @@ pub struct ResizeEvent { | |||||||
|     pub ratio: f32, |     pub ratio: f32, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// An event produced during a key press interaction of a [`PaneGrid`].
 |  | ||||||
| ///
 |  | ||||||
| /// [`PaneGrid`]: struct.PaneGrid.html
 |  | ||||||
| #[derive(Debug, Clone, Copy)] |  | ||||||
| pub struct KeyPressEvent { |  | ||||||
|     /// The key that was pressed.
 |  | ||||||
|     pub key_code: keyboard::KeyCode, |  | ||||||
| 
 |  | ||||||
|     /// The state of the modifier keys when the key was pressed.
 |  | ||||||
|     pub modifiers: keyboard::ModifiersState, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<'a, Message, Renderer> Widget<Message, Renderer> | impl<'a, Message, Renderer> Widget<Message, Renderer> | ||||||
|     for PaneGrid<'a, Message, Renderer> |     for PaneGrid<'a, Message, Renderer> | ||||||
| where | where | ||||||
| @ -495,17 +429,10 @@ where | |||||||
|                                 ); |                                 ); | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                     } else { |  | ||||||
|                         // TODO: Encode cursor availability in the type system
 |  | ||||||
|                         if cursor_position.x > 0.0 && cursor_position.y > 0.0 { |  | ||||||
|                             self.state.unfocus(); |  | ||||||
|                         } |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 mouse::Event::ButtonReleased(mouse::Button::Left) => { |                 mouse::Event::ButtonReleased(mouse::Button::Left) => { | ||||||
|                     if let Some((pane, _)) = self.state.picked_pane() { |                     if let Some((pane, _)) = self.state.picked_pane() { | ||||||
|                         self.state.focus(&pane); |  | ||||||
| 
 |  | ||||||
|                         if let Some(on_drag) = &self.on_drag { |                         if let Some(on_drag) = &self.on_drag { | ||||||
|                             let mut dropped_region = self |                             let mut dropped_region = self | ||||||
|                                 .elements |                                 .elements | ||||||
| @ -527,8 +454,10 @@ where | |||||||
| 
 | 
 | ||||||
|                             messages.push(on_drag(event)); |                             messages.push(on_drag(event)); | ||||||
|                         } |                         } | ||||||
|  | 
 | ||||||
|  |                         self.state.idle(); | ||||||
|                     } else if self.state.picked_split().is_some() { |                     } else if self.state.picked_split().is_some() { | ||||||
|                         self.state.drop_split(); |                         self.state.idle(); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 mouse::Event::CursorMoved { .. } => { |                 mouse::Event::CursorMoved { .. } => { | ||||||
| @ -536,31 +465,6 @@ where | |||||||
|                 } |                 } | ||||||
|                 _ => {} |                 _ => {} | ||||||
|             }, |             }, | ||||||
|             Event::Keyboard(keyboard_event) => { |  | ||||||
|                 match keyboard_event { |  | ||||||
|                     keyboard::Event::KeyPressed { |  | ||||||
|                         modifiers, |  | ||||||
|                         key_code, |  | ||||||
|                     } => { |  | ||||||
|                         if let Some(on_key_press) = &self.on_key_press { |  | ||||||
|                             // TODO: Discard when event is captured
 |  | ||||||
|                             if let Some(_) = self.state.active_pane() { |  | ||||||
|                                 if modifiers.matches(self.modifier_keys) { |  | ||||||
|                                     if let Some(message) = |  | ||||||
|                                         on_key_press(KeyPressEvent { |  | ||||||
|                                             key_code, |  | ||||||
|                                             modifiers, |  | ||||||
|                                         }) |  | ||||||
|                                     { |  | ||||||
|                                         messages.push(message); |  | ||||||
|                                     } |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     _ => {} |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             _ => {} |             _ => {} | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -41,9 +41,9 @@ where | |||||||
|         self |         self | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Sets the style of the [`TitleBar`].
 |     /// Sets the style of the [`Content`].
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// [`TitleBar`]: struct.TitleBar.html
 |     /// [`Content`]: struct.Content.html
 | ||||||
|     pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self { |     pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self { | ||||||
|         self.style = style.into(); |         self.style = style.into(); | ||||||
|         self |         self | ||||||
|  | |||||||
| @ -72,7 +72,7 @@ impl<T> State<T> { | |||||||
|             internal: Internal { |             internal: Internal { | ||||||
|                 layout, |                 layout, | ||||||
|                 last_id, |                 last_id, | ||||||
|                 action: Action::Idle { focus: None }, |                 action: Action::Idle, | ||||||
|             }, |             }, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -122,45 +122,10 @@ impl<T> State<T> { | |||||||
|         &self.internal.layout |         &self.internal.layout | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns the focused [`Pane`] of the [`State`], if there is one.
 |  | ||||||
|     ///
 |  | ||||||
|     /// [`Pane`]: struct.Pane.html
 |  | ||||||
|     /// [`State`]: struct.State.html
 |  | ||||||
|     pub fn focused(&self) -> Option<Pane> { |  | ||||||
|         self.internal.focused_pane() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Returns the active [`Pane`] of the [`State`], if there is one.
 |  | ||||||
|     ///
 |  | ||||||
|     /// A [`Pane`] is active if it is focused and is __not__ being dragged.
 |  | ||||||
|     ///
 |  | ||||||
|     /// [`Pane`]: struct.Pane.html
 |  | ||||||
|     /// [`State`]: struct.State.html
 |  | ||||||
|     pub fn active(&self) -> Option<Pane> { |  | ||||||
|         self.internal.active_pane() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Returns the adjacent [`Pane`] of another [`Pane`] in the given
 |     /// Returns the adjacent [`Pane`] of another [`Pane`] in the given
 | ||||||
|     /// direction, if there is one.
 |     /// direction, if there is one.
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// ## Example
 |  | ||||||
|     /// You can combine this with [`State::active`] to find the pane that is
 |  | ||||||
|     /// adjacent to the current active one, and then swap them. For instance:
 |  | ||||||
|     ///
 |  | ||||||
|     /// ```
 |  | ||||||
|     /// # use iced_native::pane_grid;
 |  | ||||||
|     /// #
 |  | ||||||
|     /// # let (mut state, _) = pane_grid::State::new(());
 |  | ||||||
|     /// #
 |  | ||||||
|     /// if let Some(active) = state.active() {
 |  | ||||||
|     ///     if let Some(adjacent) = state.adjacent(&active, pane_grid::Direction::Right) {
 |  | ||||||
|     ///         state.swap(&active, &adjacent);
 |  | ||||||
|     ///     }
 |  | ||||||
|     /// }
 |  | ||||||
|     /// ```
 |  | ||||||
|     ///
 |  | ||||||
|     /// [`Pane`]: struct.Pane.html
 |     /// [`Pane`]: struct.Pane.html
 | ||||||
|     /// [`State::active`]: struct.State.html#method.active
 |  | ||||||
|     pub fn adjacent(&self, pane: &Pane, direction: Direction) -> Option<Pane> { |     pub fn adjacent(&self, pane: &Pane, direction: Direction) -> Option<Pane> { | ||||||
|         let regions = self |         let regions = self | ||||||
|             .internal |             .internal | ||||||
| @ -194,20 +159,6 @@ impl<T> State<T> { | |||||||
|         Some(*pane) |         Some(*pane) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Focuses the given [`Pane`].
 |  | ||||||
|     ///
 |  | ||||||
|     /// [`Pane`]: struct.Pane.html
 |  | ||||||
|     pub fn focus(&mut self, pane: &Pane) { |  | ||||||
|         self.internal.focus(pane); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Unfocuses the current focused [`Pane`].
 |  | ||||||
|     ///
 |  | ||||||
|     /// [`Pane`]: struct.Pane.html
 |  | ||||||
|     pub fn unfocus(&mut self) { |  | ||||||
|         self.internal.unfocus(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Splits the given [`Pane`] into two in the given [`Axis`] and
 |     /// Splits the given [`Pane`] into two in the given [`Axis`] and
 | ||||||
|     /// initializing the new [`Pane`] with the provided internal state.
 |     /// initializing the new [`Pane`] with the provided internal state.
 | ||||||
|     ///
 |     ///
 | ||||||
| @ -236,7 +187,6 @@ impl<T> State<T> { | |||||||
|         node.split(new_split, axis, new_pane); |         node.split(new_split, axis, new_pane); | ||||||
| 
 | 
 | ||||||
|         let _ = self.panes.insert(new_pane, state); |         let _ = self.panes.insert(new_pane, state); | ||||||
|         self.focus(&new_pane); |  | ||||||
| 
 | 
 | ||||||
|         Some((new_pane, new_split)) |         Some((new_pane, new_split)) | ||||||
|     } |     } | ||||||
| @ -277,13 +227,13 @@ impl<T> State<T> { | |||||||
|         let _ = self.internal.layout.resize(split, ratio); |         let _ = self.internal.layout.resize(split, ratio); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Closes the given [`Pane`] and returns its internal state, if it exists.
 |     /// Closes the given [`Pane`] and returns its internal state and its closest
 | ||||||
|  |     /// sibling, if it exists.
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// [`Pane`]: struct.Pane.html
 |     /// [`Pane`]: struct.Pane.html
 | ||||||
|     pub fn close(&mut self, pane: &Pane) -> Option<T> { |     pub fn close(&mut self, pane: &Pane) -> Option<(T, Pane)> { | ||||||
|         if let Some(sibling) = self.internal.layout.remove(pane) { |         if let Some(sibling) = self.internal.layout.remove(pane) { | ||||||
|             self.focus(&sibling); |             self.panes.remove(pane).map(|state| (state, sibling)) | ||||||
|             self.panes.remove(pane) |  | ||||||
|         } else { |         } else { | ||||||
|             None |             None | ||||||
|         } |         } | ||||||
| @ -329,52 +279,12 @@ pub struct Internal { | |||||||
| 
 | 
 | ||||||
| #[derive(Debug, Clone, Copy, PartialEq)] | #[derive(Debug, Clone, Copy, PartialEq)] | ||||||
| pub enum Action { | pub enum Action { | ||||||
|     Idle { |     Idle, | ||||||
|         focus: Option<Pane>, |     Dragging { pane: Pane, origin: Point }, | ||||||
|     }, |     Resizing { split: Split, axis: Axis }, | ||||||
|     Dragging { |  | ||||||
|         pane: Pane, |  | ||||||
|         origin: Point, |  | ||||||
|         focus: Option<Pane>, |  | ||||||
|     }, |  | ||||||
|     Resizing { |  | ||||||
|         split: Split, |  | ||||||
|         axis: Axis, |  | ||||||
|         focus: Option<Pane>, |  | ||||||
|     }, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Action { |  | ||||||
|     pub fn focus(&self) -> Option<(Pane, Focus)> { |  | ||||||
|         match self { |  | ||||||
|             Action::Idle { focus } | Action::Resizing { focus, .. } => { |  | ||||||
|                 focus.map(|pane| (pane, Focus::Idle)) |  | ||||||
|             } |  | ||||||
|             Action::Dragging { pane, .. } => Some((*pane, Focus::Dragging)), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Internal { | impl Internal { | ||||||
|     pub fn action(&self) -> Action { |  | ||||||
|         self.action |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn focused_pane(&self) -> Option<Pane> { |  | ||||||
|         match self.action { |  | ||||||
|             Action::Idle { focus } => focus, |  | ||||||
|             Action::Dragging { focus, .. } => focus, |  | ||||||
|             Action::Resizing { focus, .. } => focus, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn active_pane(&self) -> Option<Pane> { |  | ||||||
|         match self.action { |  | ||||||
|             Action::Idle { focus } => focus, |  | ||||||
|             _ => None, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn picked_pane(&self) -> Option<(Pane, Point)> { |     pub fn picked_pane(&self) -> Option<(Pane, Point)> { | ||||||
|         match self.action { |         match self.action { | ||||||
|             Action::Dragging { pane, origin, .. } => Some((pane, origin)), |             Action::Dragging { pane, origin, .. } => Some((pane, origin)), | ||||||
| @ -405,17 +315,10 @@ impl Internal { | |||||||
|         self.layout.split_regions(spacing, size) |         self.layout.split_regions(spacing, size) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn focus(&mut self, pane: &Pane) { |  | ||||||
|         self.action = Action::Idle { focus: Some(*pane) }; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn pick_pane(&mut self, pane: &Pane, origin: Point) { |     pub fn pick_pane(&mut self, pane: &Pane, origin: Point) { | ||||||
|         let focus = self.focused_pane(); |  | ||||||
| 
 |  | ||||||
|         self.action = Action::Dragging { |         self.action = Action::Dragging { | ||||||
|             pane: *pane, |             pane: *pane, | ||||||
|             origin, |             origin, | ||||||
|             focus, |  | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -426,26 +329,14 @@ impl Internal { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let focus = self.action.focus().map(|(pane, _)| pane); |  | ||||||
| 
 |  | ||||||
|         self.action = Action::Resizing { |         self.action = Action::Resizing { | ||||||
|             split: *split, |             split: *split, | ||||||
|             axis, |             axis, | ||||||
|             focus, |  | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn drop_split(&mut self) { |     pub fn idle(&mut self) { | ||||||
|         match self.action { |         self.action = Action::Idle; | ||||||
|             Action::Resizing { focus, .. } => { |  | ||||||
|                 self.action = Action::Idle { focus }; |  | ||||||
|             } |  | ||||||
|             _ => {} |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn unfocus(&mut self) { |  | ||||||
|         self.action = Action::Idle { focus: None }; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn hash_layout(&self, hasher: &mut Hasher) { |     pub fn hash_layout(&self, hasher: &mut Hasher) { | ||||||
|  | |||||||
| @ -11,8 +11,8 @@ | |||||||
| use crate::Renderer; | use crate::Renderer; | ||||||
| 
 | 
 | ||||||
| pub use iced_native::pane_grid::{ | pub use iced_native::pane_grid::{ | ||||||
|     Axis, Configuration, Direction, DragEvent, Focus, KeyPressEvent, Node, |     Axis, Configuration, Direction, DragEvent, Focus, Node, Pane, ResizeEvent, | ||||||
|     Pane, ResizeEvent, Split, State, |     Split, State, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// A collection of panes distributed using either vertical or horizontal splits
 | /// A collection of panes distributed using either vertical or horizontal splits
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user