use egui::{InnerResponse, Stroke, Ui};
use super::corner;
use crate::{RADIUS, palette_of};
type ActionsSlot<'a> = Box<dyn FnOnce(&mut Ui) + 'a>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SplitterSide {
Left,
Right,
Bottom,
}
pub struct Splitter<'a> {
id: egui::Id,
side: SplitterSide,
default_size: f32,
min_size: f32,
max_size: f32,
title: Option<&'a str>,
actions: Option<ActionsSlot<'a>>,
}
impl<'a> Splitter<'a> {
pub fn new(id: impl Into<egui::Id>, side: SplitterSide) -> Self {
Self {
id: id.into(),
side,
default_size: 240.0,
min_size: 160.0,
max_size: 800.0,
title: None,
actions: None,
}
}
pub fn title(mut self, title: &'a str) -> Self {
self.title = Some(title);
self
}
pub fn actions(mut self, actions: impl FnOnce(&mut Ui) + 'a) -> Self {
self.actions = Some(Box::new(actions));
self
}
pub fn default_size(mut self, size: f32) -> Self {
self.default_size = size;
self
}
pub fn min_size(mut self, size: f32) -> Self {
self.min_size = size;
self
}
pub fn max_size(mut self, size: f32) -> Self {
self.max_size = size;
self
}
pub fn show<R>(self, ui: &mut Ui, body: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {
let palette = palette_of(ui.ctx());
let frame = egui::Frame::default()
.fill(palette.bg_surface_alt)
.stroke(Stroke::new(1.0, palette.border_subtle))
.inner_margin(egui::Margin::ZERO);
let title = self.title;
let actions = self.actions;
let render = |ui: &mut Ui| {
if let Some(t) = title {
paint_title(ui, t, actions, &palette);
}
ui.separator();
body(ui)
};
match self.side {
SplitterSide::Left => egui::Panel::left(self.id)
.resizable(true)
.default_size(self.default_size)
.min_size(self.min_size)
.max_size(self.max_size)
.frame(frame)
.show_inside(ui, render),
SplitterSide::Right => egui::Panel::right(self.id)
.resizable(true)
.default_size(self.default_size)
.min_size(self.min_size)
.max_size(self.max_size)
.frame(frame)
.show_inside(ui, render),
SplitterSide::Bottom => egui::Panel::bottom(self.id)
.resizable(true)
.default_size(self.default_size)
.min_size(self.min_size)
.max_size(self.max_size)
.frame(frame)
.show_inside(ui, render),
}
}
}
fn paint_title(
ui: &mut Ui,
title: &str,
actions: Option<ActionsSlot<'_>>,
palette: &crate::Palette,
) {
let height = 28.0;
let frame = egui::Frame::default()
.fill(palette.bg_surface)
.corner_radius(corner(0.0))
.inner_margin(egui::Margin::symmetric(crate::SPACING.s3 as i8, 0));
frame.show(ui, |ui| {
ui.set_height(height);
ui.horizontal_centered(|ui| {
ui.label(
egui::RichText::new(title)
.font(egui::FontId::new(11.0, egui::FontFamily::Proportional))
.color(palette.text_secondary),
);
if let Some(actions) = actions {
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), actions);
}
});
});
ui.painter().line_segment(
[
egui::pos2(ui.min_rect().left(), ui.cursor().top()),
egui::pos2(ui.min_rect().right(), ui.cursor().top()),
],
Stroke::new(1.0, palette.border_subtle),
);
let _ = RADIUS;
}