Write documentation for pane_grid
This commit is contained in:
parent
a820b8ce7b
commit
bd74c4e577
@ -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)]
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,12 @@
|
||||
/// A four cardinal direction.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Direction {
|
||||
/// ↑
|
||||
Up,
|
||||
/// ↓
|
||||
Down,
|
||||
/// ←
|
||||
Left,
|
||||
/// →
|
||||
Right,
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user