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 {
|
||||
/// 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
|
||||
/// modifiers enabled as the given value, and false otherwise.
|
||||
///
|
||||
|
|
|
@ -6,4 +6,5 @@ edition = "2018"
|
|||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../.." }
|
||||
iced = { path = "../..", features = ["debug"] }
|
||||
iced_native = { path = "../../native" }
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use iced::{
|
||||
button, keyboard, pane_grid, scrollable, Align, Button, Column, Container,
|
||||
Element, HorizontalAlignment, Length, PaneGrid, Sandbox, Scrollable,
|
||||
Settings, Text,
|
||||
button, executor, keyboard, pane_grid, scrollable, Align, Application,
|
||||
Button, Column, Command, Container, Element, HorizontalAlignment, Length,
|
||||
PaneGrid, Scrollable, Settings, Subscription, Text,
|
||||
};
|
||||
use iced_native::{subscription, Event};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
Example::run(Settings::default())
|
||||
|
@ -11,6 +12,7 @@ pub fn main() -> iced::Result {
|
|||
struct Example {
|
||||
panes: pane_grid::State<Content>,
|
||||
panes_created: usize,
|
||||
focus: Option<pane_grid::Pane>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -18,59 +20,77 @@ enum Message {
|
|||
Split(pane_grid::Axis, pane_grid::Pane),
|
||||
SplitFocused(pane_grid::Axis),
|
||||
FocusAdjacent(pane_grid::Direction),
|
||||
Clicked(pane_grid::Pane),
|
||||
Dragged(pane_grid::DragEvent),
|
||||
Resized(pane_grid::ResizeEvent),
|
||||
Close(pane_grid::Pane),
|
||||
CloseFocused,
|
||||
}
|
||||
|
||||
impl Sandbox for Example {
|
||||
impl Application for Example {
|
||||
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));
|
||||
|
||||
Example {
|
||||
panes,
|
||||
panes_created: 1,
|
||||
}
|
||||
(
|
||||
Example {
|
||||
panes,
|
||||
panes_created: 1,
|
||||
focus: None,
|
||||
},
|
||||
Command::none(),
|
||||
)
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::from("Pane grid - Iced")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) {
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::Split(axis, pane) => {
|
||||
let _ = self.panes.split(
|
||||
let result = self.panes.split(
|
||||
axis,
|
||||
&pane,
|
||||
Content::new(self.panes_created),
|
||||
);
|
||||
|
||||
if let Some((pane, _)) = result {
|
||||
self.focus = Some(pane);
|
||||
}
|
||||
|
||||
self.panes_created += 1;
|
||||
}
|
||||
Message::SplitFocused(axis) => {
|
||||
if let Some(pane) = self.panes.active() {
|
||||
let _ = self.panes.split(
|
||||
if let Some(pane) = self.focus {
|
||||
let result = self.panes.split(
|
||||
axis,
|
||||
&pane,
|
||||
Content::new(self.panes_created),
|
||||
);
|
||||
|
||||
if let Some((pane, _)) = result {
|
||||
self.focus = Some(pane);
|
||||
}
|
||||
|
||||
self.panes_created += 1;
|
||||
}
|
||||
}
|
||||
Message::FocusAdjacent(direction) => {
|
||||
if let Some(pane) = self.panes.active() {
|
||||
if let Some(pane) = self.focus {
|
||||
if let Some(adjacent) =
|
||||
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 }) => {
|
||||
self.panes.resize(&split, ratio);
|
||||
}
|
||||
|
@ -82,37 +102,54 @@ impl Sandbox for Example {
|
|||
}
|
||||
Message::Dragged(_) => {}
|
||||
Message::Close(pane) => {
|
||||
let _ = self.panes.close(&pane);
|
||||
if let Some((_, sibling)) = self.panes.close(&pane) {
|
||||
self.focus = Some(sibling);
|
||||
}
|
||||
}
|
||||
Message::CloseFocused => {
|
||||
if let Some(pane) = self.panes.active() {
|
||||
let _ = self.panes.close(&pane);
|
||||
if let Some(pane) = self.focus {
|
||||
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> {
|
||||
let focus = self.focus;
|
||||
let total_panes = self.panes.len();
|
||||
|
||||
let pane_grid =
|
||||
PaneGrid::new(&mut self.panes, |pane, content, focus| {
|
||||
let is_focused = focus.is_some();
|
||||
let title_bar =
|
||||
pane_grid::TitleBar::new(format!("Pane {}", content.id))
|
||||
.padding(10)
|
||||
.style(style::TitleBar { is_focused });
|
||||
let pane_grid = PaneGrid::new(&mut self.panes, |pane, content| {
|
||||
let is_focused = focus == Some(pane);
|
||||
|
||||
pane_grid::Content::new(content.view(pane, total_panes))
|
||||
.title_bar(title_bar)
|
||||
.style(style::Pane { is_focused })
|
||||
})
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.spacing(10)
|
||||
.on_drag(Message::Dragged)
|
||||
.on_resize(10, Message::Resized)
|
||||
.on_key_press(handle_hotkey);
|
||||
let title_bar =
|
||||
pane_grid::TitleBar::new(format!("Pane {}", content.id))
|
||||
.padding(10)
|
||||
.style(style::TitleBar { is_focused });
|
||||
|
||||
pane_grid::Content::new(content.view(pane, total_panes))
|
||||
.title_bar(title_bar)
|
||||
.style(style::Pane { is_focused })
|
||||
})
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.spacing(10)
|
||||
.on_click(Message::Clicked)
|
||||
.on_drag(Message::Dragged)
|
||||
.on_resize(10, Message::Resized);
|
||||
|
||||
Container::new(pane_grid)
|
||||
.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 pane_grid::{Axis, Direction};
|
||||
|
||||
let direction = match event.key_code {
|
||||
let direction = match key_code {
|
||||
KeyCode::Up => Some(Direction::Up),
|
||||
KeyCode::Down => Some(Direction::Down),
|
||||
KeyCode::Left => Some(Direction::Left),
|
||||
|
@ -134,7 +171,7 @@ fn handle_hotkey(event: pane_grid::KeyPressEvent) -> Option<Message> {
|
|||
_ => None,
|
||||
};
|
||||
|
||||
match event.key_code {
|
||||
match key_code {
|
||||
KeyCode::V => Some(Message::SplitFocused(Axis::Vertical)),
|
||||
KeyCode::H => Some(Message::SplitFocused(Axis::Horizontal)),
|
||||
KeyCode::W => Some(Message::CloseFocused),
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
use crate::Renderer;
|
||||
|
||||
pub use iced_native::pane_grid::{
|
||||
Axis, Configuration, Direction, DragEvent, Focus, KeyPressEvent, Node,
|
||||
Pane, ResizeEvent, Split, State,
|
||||
Axis, Configuration, Direction, DragEvent, Focus, Node, Pane, ResizeEvent,
|
||||
Split, State,
|
||||
};
|
||||
|
||||
/// A collection of panes distributed using either vertical or horizontal splits
|
||||
|
|
|
@ -20,8 +20,8 @@ use iced_native::{
|
|||
};
|
||||
|
||||
pub use iced_native::pane_grid::{
|
||||
Axis, Configuration, Content, Direction, DragEvent, Focus, KeyPressEvent,
|
||||
Pane, ResizeEvent, Split, State, TitleBar,
|
||||
Axis, Configuration, Content, Direction, DragEvent, Focus, Pane,
|
||||
ResizeEvent, Split, State, TitleBar,
|
||||
};
|
||||
|
||||
/// A collection of panes distributed using either vertical or horizontal splits
|
||||
|
|
|
@ -43,5 +43,25 @@ use events::Events;
|
|||
/// [`Subscription`]: type.Subscription.html
|
||||
/// [`Event`]: ../enum.Event.html
|
||||
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},
|
||||
Event, Hasher,
|
||||
};
|
||||
use iced_futures::futures::future;
|
||||
use iced_futures::futures::StreamExt;
|
||||
use iced_futures::BoxStream;
|
||||
|
||||
pub struct Events;
|
||||
pub struct Events<Message> {
|
||||
pub(super) f: fn(Event) -> Option<Message>,
|
||||
}
|
||||
|
||||
impl Recipe<Hasher, Event> for Events {
|
||||
type Output = Event;
|
||||
impl<Message> Recipe<Hasher, Event> for Events<Message>
|
||||
where
|
||||
Message: 'static + Send,
|
||||
{
|
||||
type Output = Message;
|
||||
|
||||
fn hash(&self, state: &mut Hasher) {
|
||||
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(
|
||||
|
@ -20,5 +29,7 @@ impl Recipe<Hasher, Event> for Events {
|
|||
event_stream: EventStream,
|
||||
) -> BoxStream<Self::Output> {
|
||||
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;
|
||||
|
||||
use crate::{
|
||||
container, keyboard, layout, mouse, overlay, row, text, Clipboard, Element,
|
||||
Event, Hasher, Layout, Length, Point, Rectangle, Size, Vector, Widget,
|
||||
container, layout, mouse, overlay, row, text, Clipboard, Element, Event,
|
||||
Hasher, Layout, Length, Point, Rectangle, Size, Vector, Widget,
|
||||
};
|
||||
|
||||
/// 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 pane_grid =
|
||||
/// PaneGrid::new(&mut state, |pane, state, focus| {
|
||||
/// PaneGrid::new(&mut state, |pane, state| {
|
||||
/// pane_grid::Content::new(match state {
|
||||
/// PaneState::SomePane => Text::new("This is some 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,
|
||||
height: Length,
|
||||
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_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>
|
||||
|
@ -112,31 +111,13 @@ where
|
|||
/// [`Pane`]: struct.Pane.html
|
||||
pub fn new<T>(
|
||||
state: &'a mut State<T>,
|
||||
view: impl Fn(
|
||||
Pane,
|
||||
&'a mut T,
|
||||
Option<Focus>,
|
||||
) -> Content<'a, Message, Renderer>,
|
||||
view: impl Fn(Pane, &'a mut T) -> Content<'a, Message, Renderer>,
|
||||
) -> Self {
|
||||
let elements = {
|
||||
let action = state.internal.action();
|
||||
let current_focus = action.focus();
|
||||
|
||||
state
|
||||
.panes
|
||||
.iter_mut()
|
||||
.map(move |(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))
|
||||
})
|
||||
.map(|(pane, pane_state)| (*pane, view(*pane, pane_state)))
|
||||
.collect()
|
||||
};
|
||||
|
||||
|
@ -146,13 +127,9 @@ where
|
|||
width: Length::Fill,
|
||||
height: Length::Fill,
|
||||
spacing: 0,
|
||||
modifier_keys: keyboard::ModifiersState {
|
||||
control: true,
|
||||
..Default::default()
|
||||
},
|
||||
on_click: None,
|
||||
on_drag: None,
|
||||
on_resize: None,
|
||||
on_key_press: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,18 +157,16 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the modifier keys of the [`PaneGrid`].
|
||||
///
|
||||
/// The modifier keys will need to be pressed to trigger key events.
|
||||
///
|
||||
/// The default modifier key is `Ctrl`.
|
||||
/// Sets the message that will be produced when a [`Pane`] of the
|
||||
/// [`PaneGrid`] is clicked.
|
||||
///
|
||||
/// [`Pane`]: struct.Pane.html
|
||||
/// [`PaneGrid`]: struct.PaneGrid.html
|
||||
pub fn modifier_keys(
|
||||
mut self,
|
||||
modifier_keys: keyboard::ModifiersState,
|
||||
) -> Self {
|
||||
self.modifier_keys = modifier_keys;
|
||||
pub fn on_click<F>(mut self, f: F) -> Self
|
||||
where
|
||||
F: 'a + Fn(Pane) -> Message,
|
||||
{
|
||||
self.on_click = Some(Box::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -225,31 +200,6 @@ where
|
|||
self.on_resize = Some((leeway, Box::new(f)));
|
||||
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>
|
||||
|
@ -268,24 +218,20 @@ where
|
|||
);
|
||||
|
||||
if let Some(((pane, content), layout)) = clicked_region.next() {
|
||||
match &self.on_drag {
|
||||
Some(on_drag) => {
|
||||
if content.can_be_picked_at(layout, cursor_position) {
|
||||
let pane_position = layout.position();
|
||||
if let Some(on_click) = &self.on_click {
|
||||
messages.push(on_click(*pane));
|
||||
}
|
||||
|
||||
let origin = cursor_position
|
||||
- Vector::new(pane_position.x, pane_position.y);
|
||||
if let Some(on_drag) = &self.on_drag {
|
||||
if content.can_be_picked_at(layout, cursor_position) {
|
||||
let pane_position = layout.position();
|
||||
|
||||
self.state.pick_pane(pane, origin);
|
||||
let origin = cursor_position
|
||||
- Vector::new(pane_position.x, pane_position.y);
|
||||
|
||||
messages
|
||||
.push(on_drag(DragEvent::Picked { pane: *pane }));
|
||||
} else {
|
||||
self.state.focus(pane);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.state.focus(pane);
|
||||
self.state.pick_pane(pane, origin);
|
||||
|
||||
messages.push(on_drag(DragEvent::Picked { pane: *pane }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -390,18 +336,6 @@ pub struct ResizeEvent {
|
|||
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>
|
||||
for PaneGrid<'a, Message, Renderer>
|
||||
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) => {
|
||||
if let Some((pane, _)) = self.state.picked_pane() {
|
||||
self.state.focus(&pane);
|
||||
|
||||
if let Some(on_drag) = &self.on_drag {
|
||||
let mut dropped_region = self
|
||||
.elements
|
||||
|
@ -527,8 +454,10 @@ where
|
|||
|
||||
messages.push(on_drag(event));
|
||||
}
|
||||
|
||||
self.state.idle();
|
||||
} else if self.state.picked_split().is_some() {
|
||||
self.state.drop_split();
|
||||
self.state.idle();
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
self.style = style.into();
|
||||
self
|
||||
|
|
|
@ -72,7 +72,7 @@ impl<T> State<T> {
|
|||
internal: Internal {
|
||||
layout,
|
||||
last_id,
|
||||
action: Action::Idle { focus: None },
|
||||
action: Action::Idle,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -122,45 +122,10 @@ impl<T> State<T> {
|
|||
&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
|
||||
/// 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
|
||||
|
@ -194,20 +159,6 @@ 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);
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// initializing the new [`Pane`] with the provided internal state.
|
||||
///
|
||||
|
@ -236,7 +187,6 @@ impl<T> State<T> {
|
|||
node.split(new_split, axis, new_pane);
|
||||
|
||||
let _ = self.panes.insert(new_pane, state);
|
||||
self.focus(&new_pane);
|
||||
|
||||
Some((new_pane, new_split))
|
||||
}
|
||||
|
@ -277,13 +227,13 @@ impl<T> State<T> {
|
|||
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
|
||||
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) {
|
||||
self.focus(&sibling);
|
||||
self.panes.remove(pane)
|
||||
self.panes.remove(pane).map(|state| (state, sibling))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -329,52 +279,12 @@ pub struct Internal {
|
|||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Action {
|
||||
Idle {
|
||||
focus: Option<Pane>,
|
||||
},
|
||||
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)),
|
||||
}
|
||||
}
|
||||
Idle,
|
||||
Dragging { pane: Pane, origin: Point },
|
||||
Resizing { split: Split, axis: Axis },
|
||||
}
|
||||
|
||||
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)> {
|
||||
match self.action {
|
||||
Action::Dragging { pane, origin, .. } => Some((pane, origin)),
|
||||
|
@ -405,17 +315,10 @@ impl Internal {
|
|||
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) {
|
||||
let focus = self.focused_pane();
|
||||
|
||||
self.action = Action::Dragging {
|
||||
pane: *pane,
|
||||
origin,
|
||||
focus,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -426,26 +329,14 @@ impl Internal {
|
|||
return;
|
||||
}
|
||||
|
||||
let focus = self.action.focus().map(|(pane, _)| pane);
|
||||
|
||||
self.action = Action::Resizing {
|
||||
split: *split,
|
||||
axis,
|
||||
focus,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn drop_split(&mut self) {
|
||||
match self.action {
|
||||
Action::Resizing { focus, .. } => {
|
||||
self.action = Action::Idle { focus };
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unfocus(&mut self) {
|
||||
self.action = Action::Idle { focus: None };
|
||||
pub fn idle(&mut self) {
|
||||
self.action = Action::Idle;
|
||||
}
|
||||
|
||||
pub fn hash_layout(&self, hasher: &mut Hasher) {
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
use crate::Renderer;
|
||||
|
||||
pub use iced_native::pane_grid::{
|
||||
Axis, Configuration, Direction, DragEvent, Focus, KeyPressEvent, Node,
|
||||
Pane, ResizeEvent, Split, State,
|
||||
Axis, Configuration, Direction, DragEvent, Focus, Node, Pane, ResizeEvent,
|
||||
Split, State,
|
||||
};
|
||||
|
||||
/// A collection of panes distributed using either vertical or horizontal splits
|
||||
|
|
Loading…
Reference in New Issue