use super::SettingsUI;
use crate::search::Hit;
const COLOR_TAB_DIMMED: egui::Color32 = egui::Color32::from_rgb(110, 110, 110);
const MAX_RESULTS: usize = 60;
const COLOR_TAB_SELECTED: egui::Color32 = egui::Color32::from_rgb(255, 255, 255);
const COLOR_TAB_NORMAL: egui::Color32 = egui::Color32::from_rgb(180, 180, 180);
const COLOR_TAB_SELECTED_BG: egui::Color32 = egui::Color32::from_rgb(60, 60, 70);
const COLOR_TAB_SELECTED_BORDER: egui::Color32 = egui::Color32::from_rgb(100, 100, 120);
const TAB_BORDER_WIDTH: f32 = 1.0;
const TAB_BUTTON_WIDTH: f32 = 140.0;
const TAB_BUTTON_HEIGHT: f32 = 32.0;
const TAB_LIST_PADDING: f32 = 8.0;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum SettingsTab {
#[default]
General,
Appearance,
WindowsAndTabs,
Panes,
Sessions,
Profiles,
Keys,
Pointer,
Effects,
Automation,
Assistant,
Advanced,
}
impl SettingsTab {
pub fn display_name(&self) -> &'static str {
match self {
Self::General => "General",
Self::Appearance => "Appearance",
Self::WindowsAndTabs => "Windows & Tabs",
Self::Panes => "Panes",
Self::Sessions => "Sessions",
Self::Profiles => "Profiles",
Self::Keys => "Keys",
Self::Pointer => "Pointer",
Self::Effects => "Effects & Shaders",
Self::Automation => "Automation",
Self::Assistant => "Assistant & Agents",
Self::Advanced => "Advanced",
}
}
pub fn icon(&self) -> &'static str {
match self {
Self::General => "🏠",
Self::Appearance => "🎨",
Self::WindowsAndTabs => "🗗",
Self::Panes => "◫",
Self::Sessions => "🔗",
Self::Profiles => "👤",
Self::Keys => "⌨",
Self::Pointer => "🖱",
Self::Effects => "✨",
Self::Automation => "⚡",
Self::Assistant => "💬",
Self::Advanced => "⚙",
}
}
pub fn all() -> &'static [Self] {
&[
Self::General,
Self::Appearance,
Self::WindowsAndTabs,
Self::Panes,
Self::Sessions,
Self::Profiles,
Self::Keys,
Self::Pointer,
Self::Effects,
Self::Automation,
Self::Assistant,
Self::Advanced,
]
}
pub fn index(&self) -> usize {
Self::all()
.iter()
.position(|t| t == self)
.unwrap_or_default()
}
pub fn page_titles(&self) -> Vec<&'static str> {
crate::layout::pages(*self)
.iter()
.map(|p| p.title)
.collect()
}
}
pub fn show(ui: &mut egui::Ui, settings: &mut SettingsUI) -> bool {
let before = settings.selected_tab;
if !settings.search_query.trim().is_empty() {
show_results(ui, settings);
ui.separator();
}
show_tabs(ui, settings);
settings.selected_tab != before
}
fn show_results(ui: &mut egui::Ui, settings: &mut SettingsUI) {
let hits: Vec<Hit> = settings.search_hits().to_vec();
let registry = settings.search_registry();
ui.add_space(TAB_LIST_PADDING);
if hits.is_empty() {
ui.label(
egui::RichText::new(no_match_message(settings.search_query.trim()))
.color(COLOR_TAB_NORMAL),
);
ui.label(
egui::RichText::new("Try fewer words, or the command palette for actions.")
.small()
.color(COLOR_TAB_DIMMED),
);
return;
}
let mut chosen = None;
let mut last_page = None;
let mut last_section = None;
for hit in hits.iter().take(MAX_RESULTS) {
let section = ®istry.sections[hit.section];
if last_page != Some((section.tab, section.page)) {
last_page = Some((section.tab, section.page));
last_section = None;
ui.label(
egui::RichText::new(format!(
"{} {} \u{203a} {}",
section.tab.icon(),
section.tab.display_name(),
crate::layout::page_at(section.tab, section.page).title
))
.strong()
.color(COLOR_TAB_SELECTED),
);
}
let row = match registry.control(*hit) {
None => {
last_section = Some(hit.section);
section_row(ui, §ion.title)
}
Some(control) => {
if last_section != Some(hit.section) {
last_section = Some(hit.section);
section_row(ui, §ion.title);
}
ui.horizontal(|ui| {
ui.add_space(RESULT_INDENT * 2.0);
ui.add(
egui::Button::new(egui::RichText::new(&control.label).small())
.frame(false)
.truncate(),
)
})
.inner
}
};
if row.clicked() {
chosen = Some(*hit);
}
}
if hits.len() > MAX_RESULTS {
ui.label(
egui::RichText::new(format!("{} more", hits.len() - MAX_RESULTS))
.small()
.color(COLOR_TAB_DIMMED),
);
}
if let Some(hit) = chosen {
settings.go_to_result(ui.ctx(), hit);
}
}
const RESULT_INDENT: f32 = 8.0;
fn section_row(ui: &mut egui::Ui, title: &str) -> egui::Response {
ui.horizontal(|ui| {
ui.add_space(RESULT_INDENT);
ui.add(
egui::Button::new(egui::RichText::new(title).color(COLOR_TAB_NORMAL))
.frame(false)
.truncate(),
)
})
.inner
}
pub fn no_match_message(query: &str) -> String {
format!("No settings match \u{201c}{query}\u{201d}")
}
fn show_tabs(ui: &mut egui::Ui, settings: &mut SettingsUI) {
ui.add_space(TAB_LIST_PADDING);
for tab in SettingsTab::all() {
let is_selected = settings.selected_tab == *tab;
let has_matches = settings.tab_has_results(*tab);
let text_color = if !has_matches {
COLOR_TAB_DIMMED
} else if is_selected {
COLOR_TAB_SELECTED
} else {
COLOR_TAB_NORMAL
};
let bg_color = if is_selected {
COLOR_TAB_SELECTED_BG
} else {
egui::Color32::TRANSPARENT
};
let response = ui.add_sized(
[TAB_BUTTON_WIDTH, TAB_BUTTON_HEIGHT],
egui::Button::new(
egui::RichText::new(format!("{} {}", tab.icon(), tab.display_name()))
.color(text_color),
)
.fill(bg_color)
.stroke(if is_selected {
egui::Stroke::new(TAB_BORDER_WIDTH, COLOR_TAB_SELECTED_BORDER)
} else {
egui::Stroke::NONE
}),
);
if response.clicked() && settings.selected_tab != *tab {
settings.set_selected_tab(*tab);
}
response.on_hover_text(tab_contents_summary(*tab));
}
ui.add_space(TAB_LIST_PADDING);
}
fn tab_contents_summary(tab: SettingsTab) -> String {
tab.page_titles().join(" \u{b7} ")
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct SettingsNav {
tab: SettingsTab,
pages: [usize; 12],
search_query: String,
}
impl SettingsUI {
pub fn nav_state(&self) -> SettingsNav {
SettingsNav {
tab: self.selected_tab,
pages: self.selected_pages,
search_query: self.search_query.clone(),
}
}
pub fn restore_nav(&mut self, nav: SettingsNav) {
self.selected_tab = nav.tab;
for (tab, page) in SettingsTab::all().iter().zip(nav.pages) {
self.selected_pages[tab.index()] = page.min(crate::layout::pages(*tab).len() - 1);
}
self.search_query = nav.search_query;
}
pub fn selected_tab(&self) -> SettingsTab {
self.selected_tab
}
pub fn set_selected_tab(&mut self, tab: SettingsTab) {
self.selected_tab = tab;
}
pub fn selected_page(&self) -> usize {
self.selected_pages[self.selected_tab.index()]
}
pub fn select_page(&mut self, tab: SettingsTab, page: usize) {
self.selected_tab = tab;
let last = crate::layout::pages(tab).len() - 1;
self.selected_pages[tab.index()] = page.min(last);
}
}