324 lines
13 KiB
Rust
324 lines
13 KiB
Rust
//! Theme and colour options example:
|
|
//! `cargo run --example 7_theme_options`
|
|
|
|
use eframe::egui;
|
|
use grimdock::{
|
|
ChildSide, PaneId, PaneStyleOverride, PanelContext, PanelStyle, PanelTree, SplitDir, Tab,
|
|
TabStyleOverride,
|
|
};
|
|
|
|
fn main() -> eframe::Result<()> {
|
|
let options = eframe::NativeOptions {
|
|
viewport: egui::ViewportBuilder::default()
|
|
.with_title("grimdock 7_theme_options")
|
|
.with_inner_size([1260.0, 760.0]),
|
|
..Default::default()
|
|
};
|
|
|
|
eframe::run_native(
|
|
"grimdock 7_theme_options",
|
|
options,
|
|
Box::new(|cc| Ok(Box::new(App::new(&cc.egui_ctx)))),
|
|
)
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
enum TabId {
|
|
Files,
|
|
Editor,
|
|
Search,
|
|
Preview,
|
|
Terminal,
|
|
}
|
|
|
|
struct App {
|
|
tree: PanelTree<TabId>,
|
|
style: PanelStyle,
|
|
terminal_pane: PaneId,
|
|
terminal_override_enabled: bool,
|
|
terminal_override: PaneStyleOverride,
|
|
search_override_enabled: bool,
|
|
search_override: TabStyleOverride,
|
|
}
|
|
|
|
impl App {
|
|
fn new(ctx: &egui::Context) -> Self {
|
|
let (tree, terminal_pane) = make_tree();
|
|
Self {
|
|
tree,
|
|
style: PanelStyle::from_visuals(&ctx.style().visuals),
|
|
terminal_pane,
|
|
terminal_override_enabled: true,
|
|
terminal_override: PaneStyleOverride {
|
|
header_bg: Some(egui::Color32::from_rgb(45, 28, 20)),
|
|
content_bg: Some(egui::Color32::from_rgb(34, 22, 16)),
|
|
border_color: Some(egui::Color32::from_rgb(168, 99, 56)),
|
|
accent_color: Some(egui::Color32::from_rgb(230, 142, 78)),
|
|
},
|
|
search_override_enabled: true,
|
|
search_override: TabStyleOverride {
|
|
active_bg: Some(egui::Color32::from_rgb(30, 74, 66)),
|
|
inactive_bg: Some(egui::Color32::from_rgb(24, 52, 48)),
|
|
hovered_bg: Some(egui::Color32::from_rgb(36, 90, 80)),
|
|
text_color: Some(egui::Color32::from_rgb(214, 245, 238)),
|
|
accent_color: Some(egui::Color32::from_rgb(52, 204, 168)),
|
|
icon_color: None,
|
|
},
|
|
}
|
|
}
|
|
|
|
fn sync_overrides(&mut self) {
|
|
if let Some(mut pane) = self.tree.pane_mut(self.terminal_pane) {
|
|
let mut options = pane.options();
|
|
options.style_override = self.terminal_override_enabled.then_some(self.terminal_override);
|
|
pane.set_options(options);
|
|
}
|
|
|
|
if let Some(search_pane_id) = self.tree.find_pane_containing(&TabId::Search) {
|
|
if let Some(mut pane) = self.tree.pane_mut(search_pane_id) {
|
|
if let Some(tab) = pane.tabs_mut().iter_mut().find(|tab| tab.id == TabId::Search) {
|
|
tab.style_override = self.search_override_enabled.then_some(self.search_override);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl eframe::App for App {
|
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
|
self.sync_overrides();
|
|
|
|
egui::Panel::left("theme_controls")
|
|
.resizable(true)
|
|
.default_width(360.0)
|
|
.show(ctx, |ui| {
|
|
ui.heading("Theme workbench");
|
|
ui.label("This example exposes the global PanelStyle plus pane and tab color overrides.");
|
|
|
|
ui.horizontal(|ui| {
|
|
if ui.button("Use egui visuals").clicked() {
|
|
self.style = PanelStyle::from_visuals(&ctx.style().visuals);
|
|
}
|
|
if ui.button("Use crate defaults").clicked() {
|
|
self.style = PanelStyle::default();
|
|
}
|
|
});
|
|
|
|
ui.separator();
|
|
|
|
ui.collapsing("Layout metrics", |ui| {
|
|
ui.add(egui::Slider::new(&mut self.style.header_height, 20.0..=40.0).text("Header height"));
|
|
ui.add(
|
|
egui::Slider::new(&mut self.style.collapsed_pane_thickness, 20.0..=60.0)
|
|
.text("Collapsed thickness"),
|
|
);
|
|
ui.add(egui::Slider::new(&mut self.style.handle_width, 2.0..=12.0).text("Handle width"));
|
|
ui.add(egui::Slider::new(&mut self.style.min_pane_size, 40.0..=160.0).text("Min pane size"));
|
|
ui.add(egui::Slider::new(&mut self.style.content_inset, 0.0..=20.0).text("Content inset"));
|
|
corner_radius_slider(ui, "Pane rounding", &mut self.style.pane_rounding);
|
|
corner_radius_slider(ui, "Tab rounding", &mut self.style.tabs.rounding);
|
|
corner_radius_slider(ui, "Header button rounding", &mut self.style.header.button.rounding);
|
|
});
|
|
|
|
ui.collapsing("Typography", |ui| {
|
|
ui.label("Dock chrome text is painted directly by grimdock.");
|
|
ui.label("Font weight comes from whichever font family your app has loaded.");
|
|
font_controls(ui, "Tab title", &mut self.style.typography.tab_title_font);
|
|
font_controls(
|
|
ui,
|
|
"Leading text icon",
|
|
&mut self.style.typography.tab_icon_text_font,
|
|
);
|
|
});
|
|
|
|
ui.collapsing("Header", |ui| {
|
|
color_row(ui, "Header bg", &mut self.style.header.bg);
|
|
color_row(ui, "Header border", &mut self.style.header.border_color);
|
|
color_row(ui, "Button bg", &mut self.style.header.button.bg);
|
|
color_row(ui, "Button hover bg", &mut self.style.header.button.hover_bg);
|
|
color_row(ui, "Button stroke", &mut self.style.header.button.stroke_color);
|
|
color_row(ui, "Button icon", &mut self.style.header.button.icon_color);
|
|
color_row(ui, "Button hover icon", &mut self.style.header.button.hover_icon_color);
|
|
});
|
|
|
|
ui.collapsing("Content", |ui| {
|
|
color_row(ui, "Content bg", &mut self.style.content.bg);
|
|
color_row(ui, "Content border", &mut self.style.content.border_color);
|
|
});
|
|
|
|
ui.collapsing("Tabs", |ui| {
|
|
ui.label("Active");
|
|
color_row(ui, "Active bg", &mut self.style.tabs.active.bg);
|
|
color_row(ui, "Active text", &mut self.style.tabs.active.text_color);
|
|
color_row(ui, "Active accent", &mut self.style.tabs.active.accent_color);
|
|
|
|
ui.separator();
|
|
ui.label("Inactive");
|
|
color_row(ui, "Inactive bg", &mut self.style.tabs.inactive.bg);
|
|
color_row(ui, "Inactive text", &mut self.style.tabs.inactive.text_color);
|
|
color_row(ui, "Inactive accent", &mut self.style.tabs.inactive.accent_color);
|
|
|
|
ui.separator();
|
|
ui.label("Hovered");
|
|
color_row(ui, "Hovered bg", &mut self.style.tabs.hovered.bg);
|
|
color_row(ui, "Hovered text", &mut self.style.tabs.hovered.text_color);
|
|
color_row(ui, "Hovered accent", &mut self.style.tabs.hovered.accent_color);
|
|
});
|
|
|
|
ui.collapsing("Handles and overlay", |ui| {
|
|
color_row(ui, "Handle", &mut self.style.handle.color);
|
|
color_row(ui, "Handle hover", &mut self.style.handle.hover_color);
|
|
color_row(ui, "Handle locked", &mut self.style.handle.locked_color);
|
|
color_row(ui, "Overlay fill", &mut self.style.overlay.fill);
|
|
color_row(ui, "Overlay stroke", &mut self.style.overlay.stroke.color);
|
|
ui.add(
|
|
egui::Slider::new(&mut self.style.overlay.stroke.width, 0.5..=4.0)
|
|
.text("Overlay stroke width"),
|
|
);
|
|
});
|
|
|
|
ui.collapsing("Pane override", |ui| {
|
|
ui.checkbox(&mut self.terminal_override_enabled, "Enable Terminal pane override");
|
|
color_option_row(ui, "Header bg", &mut self.terminal_override.header_bg);
|
|
color_option_row(ui, "Content bg", &mut self.terminal_override.content_bg);
|
|
color_option_row(ui, "Border", &mut self.terminal_override.border_color);
|
|
color_option_row(ui, "Accent", &mut self.terminal_override.accent_color);
|
|
});
|
|
|
|
ui.collapsing("Tab override", |ui| {
|
|
ui.checkbox(&mut self.search_override_enabled, "Enable Search tab override");
|
|
color_option_row(ui, "Active bg", &mut self.search_override.active_bg);
|
|
color_option_row(ui, "Inactive bg", &mut self.search_override.inactive_bg);
|
|
color_option_row(ui, "Hovered bg", &mut self.search_override.hovered_bg);
|
|
color_option_row(ui, "Text", &mut self.search_override.text_color);
|
|
color_option_row(ui, "Accent", &mut self.search_override.accent_color);
|
|
});
|
|
});
|
|
|
|
egui::CentralPanel::default()
|
|
.frame(egui::Frame::NONE)
|
|
.show(ctx, |ui| {
|
|
PanelContext::new(ui, &mut self.tree, &self.style).show(|ui, tab_id| match tab_id {
|
|
TabId::Files => {
|
|
ui.heading("Files");
|
|
ui.label("Use this workbench to tune every main color and size in PanelStyle.");
|
|
}
|
|
TabId::Editor => {
|
|
ui.heading("Editor");
|
|
ui.label("Drag tabs and resize panes while adjusting the theme controls.");
|
|
}
|
|
TabId::Search => {
|
|
ui.heading("Search");
|
|
ui.label("This tab can demonstrate per-tab color overrides.");
|
|
}
|
|
TabId::Preview => {
|
|
ui.heading("Preview");
|
|
ui.label("The dock updates live as you change colors, rounding, and handle styles.");
|
|
}
|
|
TabId::Terminal => {
|
|
ui.heading("Terminal");
|
|
ui.label("This pane can demonstrate per-pane header/content/accent overrides.");
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
fn color_row(ui: &mut egui::Ui, label: &str, color: &mut egui::Color32) {
|
|
ui.horizontal(|ui| {
|
|
ui.label(label);
|
|
ui.color_edit_button_srgba(color);
|
|
});
|
|
}
|
|
|
|
fn color_option_row(ui: &mut egui::Ui, label: &str, color: &mut Option<egui::Color32>) {
|
|
let mut enabled = color.is_some();
|
|
ui.horizontal(|ui| {
|
|
ui.checkbox(&mut enabled, label);
|
|
if !enabled {
|
|
*color = None;
|
|
return;
|
|
}
|
|
|
|
let value = color.get_or_insert(egui::Color32::WHITE);
|
|
ui.color_edit_button_srgba(value);
|
|
});
|
|
}
|
|
|
|
fn corner_radius_slider(ui: &mut egui::Ui, label: &str, radius: &mut egui::CornerRadius) {
|
|
let mut value = radius.nw;
|
|
ui.add(egui::Slider::new(&mut value, 0..=16).text(label));
|
|
*radius = egui::CornerRadius::same(value);
|
|
}
|
|
|
|
fn font_controls(ui: &mut egui::Ui, label: &str, font: &mut egui::FontId) {
|
|
ui.group(|ui| {
|
|
ui.label(label);
|
|
ui.add(egui::Slider::new(&mut font.size, 8.0..=24.0).text("Size"));
|
|
|
|
let mut family = match &font.family {
|
|
egui::FontFamily::Proportional => 0,
|
|
egui::FontFamily::Monospace => 1,
|
|
egui::FontFamily::Name(_) => 2,
|
|
};
|
|
egui::ComboBox::from_label("Family")
|
|
.selected_text(match family {
|
|
0 => "Proportional",
|
|
1 => "Monospace",
|
|
_ => "Custom",
|
|
})
|
|
.show_ui(ui, |ui| {
|
|
ui.selectable_value(&mut family, 0, "Proportional");
|
|
ui.selectable_value(&mut family, 1, "Monospace");
|
|
ui.selectable_value(&mut family, 2, "Custom");
|
|
});
|
|
|
|
if family == 2 {
|
|
let mut name = match &font.family {
|
|
egui::FontFamily::Name(name) => name.to_string(),
|
|
_ => String::from("bold"),
|
|
};
|
|
ui.text_edit_singleline(&mut name);
|
|
font.family = egui::FontFamily::Name(name.into());
|
|
} else {
|
|
font.family = if family == 0 {
|
|
egui::FontFamily::Proportional
|
|
} else {
|
|
egui::FontFamily::Monospace
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
fn make_tree() -> (PanelTree<TabId>, PaneId) {
|
|
let mut tree = PanelTree::new(vec![
|
|
Tab::new("editor", TabId::Editor).with_leading_visual(">"),
|
|
Tab::new("search", TabId::Search).with_leading_visual("?"),
|
|
]);
|
|
|
|
tree.split_leaf(
|
|
0,
|
|
SplitDir::Horizontal,
|
|
Tab::new("files", TabId::Files).with_leading_visual("#"),
|
|
ChildSide::First,
|
|
);
|
|
|
|
let editor_pane = tree
|
|
.find_pane_containing(&TabId::Editor)
|
|
.expect("editor pane should exist");
|
|
let terminal_pane = tree
|
|
.pane_mut(editor_pane)
|
|
.expect("editor pane should exist")
|
|
.split(
|
|
SplitDir::Vertical,
|
|
Tab::new("terminal", TabId::Terminal).with_leading_visual("$"),
|
|
ChildSide::Second,
|
|
);
|
|
|
|
if let Some(mut pane) = tree.pane_mut(editor_pane) {
|
|
pane.push_tab(Tab::new("preview", TabId::Preview).with_leading_visual("*"));
|
|
}
|
|
|
|
(tree, terminal_pane)
|
|
}
|