use eframe::egui;
use grimdock::{
AddTabEntry, ChildSide, DropPolicy, Node, PaneBuilder, PaneStyleOverride, PanelContext,
PanelStyle, PanelTree, SplitDir, Tab, TabStyleOverride,
};
fn main() -> eframe::Result<()> {
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_title("grimdock demo")
.with_inner_size([1024.0, 680.0]),
..Default::default()
};
eframe::run_native("grimdock demo", options, Box::new(|_cc| Ok(Box::new(App::new()))))
}
#[derive(Clone, PartialEq, Eq, Debug)]
enum TabId {
Editor(u32),
FileTree,
Terminal,
Search,
Problems,
Output,
}
impl std::fmt::Display for TabId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TabId::Editor(n) => write!(f, "editor_{n}"),
TabId::FileTree => write!(f, "file_tree"),
TabId::Terminal => write!(f, "terminal"),
TabId::Search => write!(f, "search"),
TabId::Problems => write!(f, "problems"),
TabId::Output => write!(f, "output"),
}
}
}
struct App {
tree: PanelTree<TabId>,
tick: u64,
}
impl App {
fn new() -> Self {
let mut tree = PanelTree::new(vec![
Tab::new("editor_1", TabId::Editor(1))
.with_leading_visual(">")
.with_style_override(TabStyleOverride {
active_bg: Some(egui::Color32::from_rgb(62, 73, 50)),
inactive_bg: Some(egui::Color32::from_rgb(44, 52, 38)),
hovered_bg: Some(egui::Color32::from_rgb(55, 65, 45)),
text_color: Some(egui::Color32::from_rgb(221, 231, 200)),
accent_color: Some(egui::Color32::from_rgb(164, 196, 92)),
icon_color: None,
}),
Tab::new("editor_2", TabId::Editor(2)).with_leading_visual("+"),
]);
let _file_tree_pane = tree.split_leaf_with(
0,
SplitDir::Horizontal,
PaneBuilder::new(Tab::new("file_tree", TabId::FileTree).with_leading_visual("#")).with_options(
grimdock::PaneOptions {
style_override: Some(PaneStyleOverride {
header_bg: Some(egui::Color32::from_rgb(27, 47, 44)),
content_bg: Some(egui::Color32::from_rgb(21, 36, 34)),
border_color: Some(egui::Color32::from_rgb(52, 90, 84)),
accent_color: Some(egui::Color32::from_rgb(85, 188, 162)),
}),
drop_policy: DropPolicy::merge_only(),
..Default::default()
},
),
ChildSide::First,
);
let editors_pane = tree
.find_pane_containing(&TabId::Editor(1))
.expect("editor pane should exist after initial split");
let bottom_pane = {
let mut pane = tree.pane_mut(editors_pane).expect("editor pane should exist");
pane.split_with(
SplitDir::Vertical,
PaneBuilder::new(Tab::new("terminal", TabId::Terminal).with_leading_visual("$")),
ChildSide::Second,
)
};
let mut bottom = tree.pane_mut(bottom_pane).expect("bottom pane should exist");
bottom.push_tab(Tab::new("output", TabId::Output).with_leading_visual("!"));
bottom.push_tab(Tab::new("problems", TabId::Problems).with_leading_visual("x"));
bottom.push_tab(Tab::new("search", TabId::Search).with_leading_visual("?"));
if let Node::Split { ratio, .. } = tree.node_mut(0) {
*ratio = 0.22;
}
if let Node::Split { ratio, .. } = tree.node_mut(2) {
*ratio = 0.65;
}
Self {
tree,
tick: 0,
}
}
}
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
self.tick += 1;
egui::CentralPanel::default()
.frame(egui::Frame::NONE)
.show(ctx, |ui| {
let tree = &mut self.tree;
let mut style = PanelStyle::from_egui_style(ui.style().as_ref());
style.content_inset = 4.0;
style.tabs.rounding = egui::CornerRadius::same(5);
style.header.button.rounding = egui::CornerRadius::same(5);
let tick = self.tick;
let add_tab_entries = vec![
AddTabEntry::new(
"Editor 3",
Tab::new("editor_3", TabId::Editor(3))
.with_leading_visual("*")
.with_closable(true),
),
AddTabEntry::new(
"Terminal",
Tab::new("terminal", TabId::Terminal)
.with_leading_visual("$")
.with_closable(true),
),
AddTabEntry::new(
"Search",
Tab::new("search", TabId::Search)
.with_leading_visual("?")
.with_closable(true),
),
AddTabEntry::new(
"Problems",
Tab::new("problems", TabId::Problems)
.with_leading_visual("x")
.with_closable(true),
),
];
PanelContext::new(ui, tree, &style)
.with_add_tab_entries(&add_tab_entries)
.show(|ui, tab_id| {
render_tab(ui, tab_id, tick);
});
});
ctx.request_repaint();
}
}
fn render_tab(ui: &mut egui::Ui, tab_id: &TabId, tick: u64) {
egui::ScrollArea::vertical().show(ui, |ui| {
match tab_id {
TabId::Editor(n) => {
ui.add_space(8.0);
ui.label(
egui::RichText::new(format!("// Editor {n}\n\nfn main() {{\n println!(\"Hello, world!\");\n}}"))
.monospace()
.size(13.0),
);
ui.add_space(8.0);
ui.label("Drag a tab to a different pane to rearrange.");
ui.label("Drag the handle between panes to resize.");
}
TabId::FileTree => {
ui.add_space(6.0);
for item in &["src/", " lib.rs", " tree.rs", " layout.rs", " header.rs", " dnd.rs", "Cargo.toml"] {
ui.label(egui::RichText::new(*item).monospace().size(12.0));
}
}
TabId::Terminal => {
ui.add_space(4.0);
ui.label(egui::RichText::new(format!("$ frame {tick}")).monospace().size(12.0));
ui.label(egui::RichText::new("grimdock running ✓").monospace().color(egui::Color32::from_rgb(100, 200, 120)).size(12.0));
}
TabId::Search => {
ui.add_space(8.0);
ui.label("Search");
ui.text_edit_singleline(&mut String::new());
}
TabId::Problems => {
ui.add_space(8.0);
ui.label(egui::RichText::new("No problems detected.").color(egui::Color32::from_rgb(100, 200, 120)));
}
TabId::Output => {
ui.add_space(4.0);
ui.label(egui::RichText::new("[INFO] Build complete.").monospace().size(12.0));
}
}
});
}