mod drag;
mod layout;
mod show;
pub use layout::{DockLayout, DockNode, SplitDirection};
use std::hash::Hash;
use egui::{Response, Ui, Vec2b};
use crate::tab_paint::TabContent;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DockTab {
pub label: String,
pub icon: Option<String>,
pub dirty: bool,
pub closable: bool,
pub context_menu: bool,
pub scroll: Vec2b,
}
impl DockTab {
pub fn new(label: impl Into<String>) -> Self {
Self {
label: label.into(),
icon: None,
dirty: false,
closable: true,
context_menu: false,
scroll: Vec2b::FALSE,
}
}
#[inline]
pub fn icon(mut self, icon: impl Into<String>) -> Self {
self.icon = Some(icon.into());
self
}
#[inline]
pub fn dirty(mut self, dirty: bool) -> Self {
self.dirty = dirty;
self
}
#[inline]
pub fn closable(mut self, closable: bool) -> Self {
self.closable = closable;
self
}
#[inline]
pub fn context_menu(mut self, context_menu: bool) -> Self {
self.context_menu = context_menu;
self
}
#[inline]
pub fn scroll(mut self, scroll: impl Into<Vec2b>) -> Self {
self.scroll = scroll.into();
self
}
pub(crate) fn content(&self) -> TabContent<'_> {
TabContent {
label: &self.label,
icon: self.icon.as_deref(),
dirty: self.dirty,
closable: self.closable,
}
}
}
pub trait DockViewer {
type Tab: Clone + Eq + Hash;
fn tab(&mut self, tab: &Self::Tab) -> DockTab;
fn ui(&mut self, ui: &mut Ui, tab: &Self::Tab);
fn context_menu(&mut self, ui: &mut Ui, tab: &Self::Tab) {
let _ = (ui, tab);
}
fn empty_ui(&mut self, ui: &mut Ui) {
let theme = crate::Theme::current(ui.ctx());
ui.centered_and_justified(|ui| {
ui.label(
egui::RichText::new("No open tabs")
.color(theme.palette.text_faint)
.size(theme.typography.body),
);
});
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DockEvent<T> {
TabActivated(T),
TabClosed(T),
TabMoved(T),
LayoutChanged,
}
#[derive(Debug)]
pub struct DockResponse<T> {
pub response: Response,
pub events: Vec<DockEvent<T>>,
}
#[must_use = "Call `.show(ui, layout, viewer)` to render the dock."]
#[derive(Debug)]
pub struct Dock {
id_salt: egui::IdSalt,
min_pane_size: f32,
body_padding: f32,
min_tab_width: f32,
max_tab_width: f32,
}
impl Dock {
pub fn new(id_salt: impl crate::IdSalt) -> Self {
Self {
id_salt: egui::IdSalt::new(("elegance::dock", id_salt)),
min_pane_size: 96.0,
body_padding: 8.0,
min_tab_width: 120.0,
max_tab_width: 200.0,
}
}
#[inline]
pub fn min_pane_size(mut self, size: f32) -> Self {
self.min_pane_size = size.max(0.0);
self
}
#[inline]
pub fn body_padding(mut self, padding: f32) -> Self {
self.body_padding = padding.max(0.0);
self
}
#[inline]
pub fn min_tab_width(mut self, width: f32) -> Self {
self.min_tab_width = width.max(60.0);
self.max_tab_width = self.max_tab_width.max(self.min_tab_width);
self
}
#[inline]
pub fn max_tab_width(mut self, width: f32) -> Self {
self.max_tab_width = width.max(self.min_tab_width);
self
}
pub fn show<V: DockViewer>(
self,
ui: &mut Ui,
layout: &mut DockLayout<V::Tab>,
viewer: &mut V,
) -> DockResponse<V::Tab> {
let base_id = ui.scope_id().with_salt(self.id_salt);
let (response, events) = show::show(ui, base_id, &self, layout, viewer);
DockResponse { response, events }
}
}