diff options
Diffstat (limited to '')
| -rw-r--r-- | native/src/lib.rs | 6 | ||||
| -rw-r--r-- | native/src/widget/pane_grid.rs | 160 | ||||
| -rw-r--r-- | native/src/widget/pane_grid/axis.rs | 3 | ||||
| -rw-r--r-- | native/src/widget/pane_grid/direction.rs | 5 | ||||
| -rw-r--r-- | native/src/widget/pane_grid/pane.rs | 3 | ||||
| -rw-r--r-- | native/src/widget/pane_grid/split.rs | 3 | ||||
| -rw-r--r-- | native/src/widget/pane_grid/state.rs | 104 | 
7 files changed, 273 insertions, 11 deletions
| diff --git a/native/src/lib.rs b/native/src/lib.rs index 4551a982..d17dd918 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -21,8 +21,8 @@  //! # Usage  //! The strategy to use this crate depends on your particular use case. If you  //! want to: -//! - Implement a custom shell or integrate it in your own system, you should -//!   check out the [`UserInterface`] type. +//! - Implement a custom shell or integrate it in your own system, check out the +//! [`UserInterface`] type.  //! - Build a new renderer, see the [renderer] module.  //! - Build a custom widget, start at the [`Widget`] trait.  //! @@ -34,7 +34,7 @@  //! [`window::Renderer`]: window/trait.Renderer.html  //! [`UserInterface`]: struct.UserInterface.html  //! [renderer]: renderer/index.html -//#![deny(missing_docs)] +#![deny(missing_docs)]  #![deny(missing_debug_implementations)]  #![deny(unused_results)]  #![forbid(unsafe_code)] diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 3e61642e..d33573ca 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -1,3 +1,6 @@ +//! Let your users split regions of your application and organize layout dynamically. +//! +//! [](https://gfycat.com/mixedflatjellyfish)  mod axis;  mod direction;  mod node; @@ -17,6 +20,57 @@ use crate::{      Widget,  }; +/// A collection of panes distributed using either vertical or horizontal splits +/// to completely fill the space available. +/// +/// [](https://gfycat.com/mixedflatjellyfish) +/// +/// This distribution of space is common in tiling window managers (like +/// [`awesome`](https://awesomewm.org/), [`i3`](https://i3wm.org/), or even +/// [`tmux`](https://github.com/tmux/tmux)). +/// +/// A [`PaneGrid`] supports: +/// +/// * Vertical and horizontal splits +/// * Tracking of the last active pane +/// * Mouse-based resizing +/// * Drag and drop to reorganize panes +/// * Hotkey support +/// * Configurable modifier keys +/// * [`State`] API to perform actions programmatically (`split`, `swap`, `resize`, etc.) +/// +/// ## Example +/// +/// ``` +/// # use iced_native::{pane_grid, Text}; +/// # +/// # type PaneGrid<'a, Message> = +/// #     iced_native::PaneGrid<'a, Message, iced_native::renderer::Null>; +/// # +/// enum PaneState { +///     SomePane, +///     AnotherKindOfPane, +/// } +/// +/// enum Message { +///     PaneDragged(pane_grid::DragEvent), +///     PaneResized(pane_grid::ResizeEvent), +/// } +/// +/// let (mut state, _) = pane_grid::State::new(PaneState::SomePane); +/// +/// let pane_grid = PaneGrid::new(&mut state, |pane, state, focus| { +///     match state { +///         PaneState::SomePane => Text::new("This is some pane"), +///         PaneState::AnotherKindOfPane => Text::new("This is another kind of pane"), +///     }.into() +/// }) +///     .on_drag(Message::PaneDragged) +///     .on_resize(Message::PaneResized); +/// ``` +/// +/// [`PaneGrid`]: struct.PaneGrid.html +/// [`State`]: struct.State.html  #[allow(missing_debug_implementations)]  pub struct PaneGrid<'a, Message, Renderer> {      state: &'a mut state::Internal, @@ -32,6 +86,13 @@ pub struct PaneGrid<'a, Message, Renderer> {  }  impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> { +    /// Creates a [`PaneGrid`] with the given [`State`] and view function. +    /// +    /// The view function will be called to display each [`Pane`] present in the +    /// [`State`]. +    /// +    /// [`PaneGrid`]: struct.PaneGrid.html +    /// [`State`]: struct.State.html      pub fn new<T>(          state: &'a mut State<T>,          view: impl Fn( @@ -81,7 +142,7 @@ impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> {      /// Sets the width of the [`PaneGrid`].      /// -    /// [`PaneGrid`]: struct.Column.html +    /// [`PaneGrid`]: struct.PaneGrid.html      pub fn width(mut self, width: Length) -> Self {          self.width = width;          self @@ -89,7 +150,7 @@ impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> {      /// Sets the height of the [`PaneGrid`].      /// -    /// [`PaneGrid`]: struct.Column.html +    /// [`PaneGrid`]: struct.PaneGrid.html      pub fn height(mut self, height: Length) -> Self {          self.height = height;          self @@ -97,12 +158,20 @@ impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> {      /// Sets the spacing _between_ the panes of the [`PaneGrid`].      /// -    /// [`PaneGrid`]: struct.Column.html +    /// [`PaneGrid`]: struct.PaneGrid.html      pub fn spacing(mut self, units: u16) -> Self {          self.spacing = units;          self      } +    /// Sets the modifier keys of the [`PaneGrid`]. +    /// +    /// The modifier keys will need to be pressed to trigger dragging, resizing, +    /// and key events. +    /// +    /// The default modifier key is `Ctrl`. +    /// +    /// [`PaneGrid`]: struct.PaneGrid.html      pub fn modifier_keys(          mut self,          modifier_keys: keyboard::ModifiersState, @@ -111,6 +180,12 @@ impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> {          self      } +    /// 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(          mut self,          f: impl Fn(DragEvent) -> Message + 'static, @@ -119,6 +194,12 @@ impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> {          self      } +    /// 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`. +    /// +    /// [`PaneGrid`]: struct.PaneGrid.html      pub fn on_resize(          mut self,          f: impl Fn(ResizeEvent) -> Message + 'static, @@ -127,6 +208,23 @@ impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> {          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 function 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(          mut self,          f: impl Fn(KeyPressEvent) -> Option<Message> + 'static, @@ -173,22 +271,72 @@ impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> {      }  } +/// An event produced during a drag and drop interaction of a [`PaneGrid`]. +/// +/// [`PaneGrid`]: struct.PaneGrid.html  #[derive(Debug, Clone, Copy)]  pub enum DragEvent { -    Picked { pane: Pane }, -    Dropped { pane: Pane, target: Pane }, -    Canceled { pane: Pane }, +    /// A [`Pane`] was picked for dragging. +    /// +    /// [`Pane`]: struct.Pane.html +    Picked { +        /// The picked [`Pane`]. +        /// +        /// [`Pane`]: struct.Pane.html +        pane: Pane, +    }, + +    /// A [`Pane`] was dropped on top of another [`Pane`]. +    /// +    /// [`Pane`]: struct.Pane.html +    Dropped { +        /// The picked [`Pane`]. +        /// +        /// [`Pane`]: struct.Pane.html +        pane: Pane, + +        /// The [`Pane`] where the picked one was dropped on. +        /// +        /// [`Pane`]: struct.Pane.html +        target: Pane, +    }, + +    /// A [`Pane`] was picked and then dropped outside of other [`Pane`] +    /// boundaries. +    /// +    /// [`Pane`]: struct.Pane.html +    Canceled { +        /// The picked [`Pane`]. +        /// +        /// [`Pane`]: struct.Pane.html +        pane: Pane, +    },  } +/// An event produced during a resize interaction of a [`PaneGrid`]. +/// +/// [`PaneGrid`]: struct.PaneGrid.html  #[derive(Debug, Clone, Copy)]  pub struct ResizeEvent { +    /// The [`Split`] that is being dragged for resizing.      pub split: Split, + +    /// The new ratio of the [`Split`]. +    /// +    /// The ratio is a value in [0, 1], representing the exact position of a +    /// [`Split`] between two panes.      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,  } diff --git a/native/src/widget/pane_grid/axis.rs b/native/src/widget/pane_grid/axis.rs index a17d0c12..f0e3f362 100644 --- a/native/src/widget/pane_grid/axis.rs +++ b/native/src/widget/pane_grid/axis.rs @@ -1,8 +1,11 @@  use crate::Rectangle; +/// A fixed reference line for the measurement of coordinates.  #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]  pub enum Axis { +    /// The horizontal axis: —      Horizontal, +    /// The vertical axis: |      Vertical,  } diff --git a/native/src/widget/pane_grid/direction.rs b/native/src/widget/pane_grid/direction.rs index 0ee90557..b31a8737 100644 --- a/native/src/widget/pane_grid/direction.rs +++ b/native/src/widget/pane_grid/direction.rs @@ -1,7 +1,12 @@ +/// A four cardinal direction.  #[derive(Debug, Clone, Copy, PartialEq, Eq)]  pub enum Direction { +    /// ↑      Up, +    /// ↓      Down, +    /// ←      Left, +    /// →      Right,  } diff --git a/native/src/widget/pane_grid/pane.rs b/native/src/widget/pane_grid/pane.rs index cfca3b03..f9866407 100644 --- a/native/src/widget/pane_grid/pane.rs +++ b/native/src/widget/pane_grid/pane.rs @@ -1,2 +1,5 @@ +/// A rectangular region in a [`PaneGrid`] used to display widgets. +/// +/// [`PaneGrid`]: struct.PaneGrid.html  #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]  pub struct Pane(pub(super) usize); diff --git a/native/src/widget/pane_grid/split.rs b/native/src/widget/pane_grid/split.rs index c2dad980..d020c510 100644 --- a/native/src/widget/pane_grid/split.rs +++ b/native/src/widget/pane_grid/split.rs @@ -1,2 +1,5 @@ +/// A divider that splits a region in a [`PaneGrid`] into two different panes. +/// +/// [`PaneGrid`]: struct.PaneGrid.html  #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]  pub struct Split(pub(super) usize); diff --git a/native/src/widget/pane_grid/state.rs b/native/src/widget/pane_grid/state.rs index 9103dcd0..6c80cacc 100644 --- a/native/src/widget/pane_grid/state.rs +++ b/native/src/widget/pane_grid/state.rs @@ -6,6 +6,20 @@ use crate::{  use std::collections::HashMap; +/// The state of a [`PaneGrid`]. +/// +/// It keeps track of the state of each [`Pane`] and the position of each +/// [`Split`]. +/// +/// The [`State`] needs to own any mutable contents a [`Pane`] may need. This is +/// why this struct is generic over the type `T`. Values of this type are +/// provided to the view function of [`PaneGrid::new`] for displaying each +/// [`Pane`]. +/// +/// [`PaneGrid`]: struct.PaneGrid.html +/// [`PaneGrid::new`]: struct.PaneGrid.html#method.new +/// [`State`]: struct.State.html +/// [`Pane`]: struct.Pane.html  #[derive(Debug)]  pub struct State<T> {      pub(super) panes: HashMap<Pane, T>, @@ -13,13 +27,28 @@ pub struct State<T> {      pub(super) modifiers: keyboard::ModifiersState,  } +/// The current focus of a [`Pane`].  #[derive(Debug, Clone, Copy, PartialEq, Eq)]  pub enum Focus { +    /// The [`Pane`] is just focused. +    /// +    /// [`Pane`]: struct.Pane.html      Idle, + +    /// The [`Pane`] is being dragged. +    /// +    /// [`Pane`]: struct.Pane.html      Dragging,  }  impl<T> State<T> { +    /// Creates a new [`State`], initializing the first pane with the provided +    /// state. +    /// +    /// Alongside the [`State`], it returns the first [`Pane`] identifier. +    /// +    /// [`State`]: struct.State.html +    /// [`Pane`]: struct.Pane.html      pub fn new(first_pane_state: T) -> (Self, Pane) {          let first_pane = Pane(0); @@ -40,22 +69,42 @@ impl<T> State<T> {          )      } +    /// Returns the total amount of panes in the [`State`]. +    /// +    /// [`State`]: struct.State.html      pub fn len(&self) -> usize {          self.panes.len()      } +    /// Returns the internal state of the given [`Pane`], if it exists. +    /// +    /// [`Pane`]: struct.Pane.html      pub fn get_mut(&mut self, pane: &Pane) -> Option<&mut T> {          self.panes.get_mut(pane)      } +    /// Returns an iterator over all the panes of the [`State`], alongside its +    /// internal state. +    /// +    /// [`State`]: struct.State.html      pub fn iter(&self) -> impl Iterator<Item = (&Pane, &T)> {          self.panes.iter()      } +    /// Returns a mutable iterator over all the panes of the [`State`], +    /// alongside its internal state. +    /// +    /// [`State`]: struct.State.html      pub fn iter_mut(&mut self) -> impl Iterator<Item = (&Pane, &mut T)> {          self.panes.iter_mut()      } +    /// 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> {          match self.internal.action {              Action::Idle { focus } => focus, @@ -63,6 +112,27 @@ impl<T> State<T> {          }      } +    /// Returns the adjacent [`Pane`] of another [`Pane`] in the given +    /// 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 +    /// [`State::active`]: struct.State.html#method.active      pub fn adjacent(&self, pane: &Pane, direction: Direction) -> Option<Pane> {          let regions =              self.internal.layout.regions(0.0, Size::new(4096.0, 4096.0)); @@ -94,10 +164,18 @@ impl<T> State<T> {          Some(*pane)      } +    /// Focuses the given [`Pane`]. +    /// +    /// [`Pane`]: struct.Pane.html      pub fn focus(&mut self, pane: &Pane) {          self.internal.focus(pane);      } +    /// Splits the given [`Pane`] into two in the given [`Axis`] and +    /// initializing the new [`Pane`] with the provided internal state. +    /// +    /// [`Pane`]: struct.Pane.html +    /// [`Axis`]: enum.Axis.html      pub fn split(&mut self, axis: Axis, pane: &Pane, state: T) -> Option<Pane> {          let node = self.internal.layout.find(pane)?; @@ -121,6 +199,14 @@ impl<T> State<T> {          Some(new_pane)      } +    /// Swaps the position of the provided panes in the [`State`]. +    /// +    /// If you want to swap panes on drag and drop in your [`PaneGrid`], you +    /// will need to call this method when handling a [`DragEvent`]. +    /// +    /// [`State`]: struct.State.html +    /// [`PaneGrid`]: struct.PaneGrid.html +    /// [`DragEvent`]: struct.DragEvent.html      pub fn swap(&mut self, a: &Pane, b: &Pane) {          self.internal.layout.update(&|node| match node {              Node::Split { .. } => {} @@ -134,10 +220,24 @@ impl<T> State<T> {          });      } -    pub fn resize(&mut self, split: &Split, percentage: f32) { -        let _ = self.internal.layout.resize(split, percentage); +    /// Resizes two panes by setting the position of the provided [`Split`]. +    /// +    /// The ratio is a value in [0, 1], representing the exact position of a +    /// [`Split`] between two panes. +    /// +    /// If you want to enable resize interactions in your [`PaneGrid`], you will +    /// need to call this method when handling a [`ResizeEvent`]. +    /// +    /// [`Split`]: struct.Split.html +    /// [`PaneGrid`]: struct.PaneGrid.html +    /// [`ResizeEvent`]: struct.ResizeEvent.html +    pub fn resize(&mut self, split: &Split, ratio: f32) { +        let _ = self.internal.layout.resize(split, ratio);      } +    /// Closes the given [`Pane`] and returns its internal state, if it exists. +    /// +    /// [`Pane`]: struct.Pane.html      pub fn close(&mut self, pane: &Pane) -> Option<T> {          if let Some(sibling) = self.internal.layout.remove(pane) {              self.focus(&sibling); | 
