summaryrefslogtreecommitdiffstats
path: root/native/src
diff options
context:
space:
mode:
Diffstat (limited to 'native/src')
-rw-r--r--native/src/lib.rs6
-rw-r--r--native/src/widget/pane_grid.rs160
-rw-r--r--native/src/widget/pane_grid/axis.rs3
-rw-r--r--native/src/widget/pane_grid/direction.rs5
-rw-r--r--native/src/widget/pane_grid/pane.rs3
-rw-r--r--native/src/widget/pane_grid/split.rs3
-rw-r--r--native/src/widget/pane_grid/state.rs104
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.
+//!
+//! [![Pane grid - Iced](https://thumbs.gfycat.com/MixedFlatJellyfish-small.gif)](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.
+///
+/// [![Pane grid - Iced](https://thumbs.gfycat.com/MixedFlatJellyfish-small.gif)](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);