use iced::{
button, keyboard, pane_grid, scrollable, Align, Button, Column, Container,
Element, HorizontalAlignment, Length, PaneGrid, Sandbox, Scrollable,
Settings, Text,
};
pub fn main() {
Example::run(Settings::default())
}
struct Example {
panes: pane_grid::State<Content>,
panes_created: usize,
}
#[derive(Debug, Clone, Copy)]
enum Message {
Split(pane_grid::Axis, pane_grid::Pane),
SplitFocused(pane_grid::Axis),
FocusAdjacent(pane_grid::Direction),
Dragged(pane_grid::DragEvent),
Resized(pane_grid::ResizeEvent),
Close(pane_grid::Pane),
CloseFocused,
}
impl Sandbox for Example {
type Message = Message;
fn new() -> Self {
let (panes, _) = pane_grid::State::new(Content::new(0));
Example {
panes,
panes_created: 1,
}
}
fn title(&self) -> String {
String::from("Pane grid - Iced")
}
fn update(&mut self, message: Message) {
match message {
Message::Split(axis, pane) => {
let _ = self.panes.split(
axis,
&pane,
Content::new(self.panes_created),
);
self.panes_created += 1;
}
Message::SplitFocused(axis) => {
if let Some(pane) = self.panes.active() {
let _ = self.panes.split(
axis,
&pane,
Content::new(self.panes_created),
);
self.panes_created += 1;
}
}
Message::FocusAdjacent(direction) => {
if let Some(pane) = self.panes.active() {
if let Some(adjacent) =
self.panes.adjacent(&pane, direction)
{
self.panes.focus(&adjacent);
}
}
}
Message::Resized(pane_grid::ResizeEvent { split, ratio }) => {
self.panes.resize(&split, ratio);
}
Message::Dragged(pane_grid::DragEvent::Dropped {
pane,
target,
}) => {
self.panes.swap(&pane, &target);
}
Message::Dragged(_) => {}
Message::Close(pane) => {
let _ = self.panes.close(&pane);
}
Message::CloseFocused => {
if let Some(pane) = self.panes.active() {
let _ = self.panes.close(&pane);
}
}
}
}
fn view(&mut self) -> Element<Message> {
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 });
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);
Container::new(pane_grid)
.width(Length::Fill)
.height(Length::Fill)
.padding(10)
.into()
}
}
fn handle_hotkey(event: pane_grid::KeyPressEvent) -> Option<Message> {
use keyboard::KeyCode;
use pane_grid::{Axis, Direction};
let direction = match event.key_code {
KeyCode::Up => Some(Direction::Up),
KeyCode::Down => Some(Direction::Down),
KeyCode::Left => Some(Direction::Left),
KeyCode::Right => Some(Direction::Right),
_ => None,
};
match event.key_code {
KeyCode::V => Some(Message::SplitFocused(Axis::Vertical)),
KeyCode::H => Some(Message::SplitFocused(Axis::Horizontal)),
KeyCode::W => Some(Message::CloseFocused),
_ => direction.map(Message::FocusAdjacent),
}
}
struct Content {
id: usize,
scroll: scrollable::State,
split_horizontally: button::State,
split_vertically: button::State,
close: button::State,
}
impl Content {
fn new(id: usize) -> Self {
Content {
id,
scroll: scrollable::State::new(),
split_horizontally: button::State::new(),
split_vertically: button::State::new(),
close: button::State::new(),
}
}
fn view(
&mut self,
pane: pane_grid::Pane,
total_panes: usize,
) -> Element<Message> {
let Content {
scroll,
split_horizontally,
split_vertically,
close,
..
} = self;
let button = |state, label, message, style| {
Button::new(
state,
Text::new(label)
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center)
.size(16),
)
.width(Length::Fill)
.padding(8)
.on_press(message)
.style(style)
};
let mut controls = Column::new()
.spacing(5)
.max_width(150)
.push(button(
split_horizontally,
"Split horizontally",
Message::Split(pane_grid::Axis::Horizontal, pane),
style::Button::Primary,
))
.push(button(
split_vertically,
"Split vertically",
Message::Split(pane_grid::Axis::Vertical, pane),
style::Button::Primary,
));
if total_panes > 1 {
controls = controls.push(button(
close,
"Close",
Message::Close(pane),
style::Button::Destructive,
));
}
let content = Scrollable::new(scroll)
.width(Length::Fill)
.spacing(10)
.align_items(Align::Center)
.push(controls);
Container::new(content)
.width(Length::Fill)
.height(Length::Fill)
.padding(5)
.center_y()
.into()
}
}
mod style {
use iced::{button, container, Background, Color, Vector};
const SURFACE: Color = Color::from_rgb(
0xF2 as f32 / 255.0,
0xF3 as f32 / 255.0,
0xF5 as f32 / 255.0,
);
const ACTIVE: Color = Color::from_rgb(
0x72 as f32 / 255.0,
0x89 as f32 / 255.0,
0xDA as f32 / 255.0,
);
const HOVERED: Color = Color::from_rgb(
0x67 as f32 / 255.0,
0x7B as f32 / 255.0,
0xC4 as f32 / 255.0,
);
pub struct TitleBar {
pub is_focused: bool,
}
impl container::StyleSheet for TitleBar {
fn style(&self) -> container::Style {
let pane = Pane {
is_focused: self.is_focused,
}
.style();
container::Style {
text_color: Some(Color::WHITE),
background: Some(pane.border_color.into()),
..Default::default()
}
}
}
pub struct Pane {
pub is_focused: bool,
}
impl container::StyleSheet for Pane {
fn style(&self) -> container::Style {
container::Style {
background: Some(Background::Color(SURFACE)),
border_width: 2,
border_color: if self.is_focused {
Color::BLACK
} else {
Color::from_rgb(0.7, 0.7, 0.7)
},
..Default::default()
}
}
}
pub enum Button {
Primary,
Destructive,
}
impl button::StyleSheet for Button {
fn active(&self) -> button::Style {
let (background, text_color) = match self {
Button::Primary => (Some(ACTIVE), Color::WHITE),
Button::Destructive => {
(None, Color::from_rgb8(0xFF, 0x47, 0x47))
}
};
button::Style {
text_color,
background: background.map(Background::Color),
border_radius: 5,
shadow_offset: Vector::new(0.0, 0.0),
..button::Style::default()
}
}
fn hovered(&self) -> button::Style {
let active = self.active();
let background = match self {
Button::Primary => Some(HOVERED),
Button::Destructive => Some(Color {
a: 0.2,
..active.text_color
}),
};
button::Style {
background: background.map(Background::Color),
..active
}
}
}
}