use std::collections::HashSet;
pub const INPUT_WIDTH: f32 = 300.0;
pub const SLIDER_WIDTH: f32 = 250.0;
pub const SLIDER_HEIGHT: f32 = 18.0;
pub const COMBO_WIDTH: f32 = 200.0;
pub fn collapsing_section<R>(
ui: &mut egui::Ui,
title: &str,
id: &str,
default_open: bool,
collapsed_sections: &mut HashSet<String>,
add_contents: impl FnOnce(&mut egui::Ui) -> R,
) -> Option<egui::CollapsingResponse<R>> {
section(
ui,
title,
id,
&[],
default_open,
collapsed_sections,
|ui, _| add_contents(ui),
)
}
pub fn collapsing_section_with_state<R>(
ui: &mut egui::Ui,
title: &str,
id: &str,
default_open: bool,
collapsed_sections: &mut HashSet<String>,
add_contents: impl FnOnce(&mut egui::Ui, &mut HashSet<String>) -> R,
) -> Option<egui::CollapsingResponse<R>> {
section(
ui,
title,
id,
&[],
default_open,
collapsed_sections,
add_contents,
)
}
pub fn keyword_section<R>(
ui: &mut egui::Ui,
title: &str,
id: &str,
keywords: &[&str],
default_open: bool,
collapsed_sections: &mut HashSet<String>,
add_contents: impl FnOnce(&mut egui::Ui) -> R,
) -> Option<egui::CollapsingResponse<R>> {
section(
ui,
title,
id,
keywords,
default_open,
collapsed_sections,
|ui, _| add_contents(ui),
)
}
pub fn keyword_section_with_state<R>(
ui: &mut egui::Ui,
title: &str,
id: &str,
keywords: &[&str],
default_open: bool,
collapsed_sections: &mut HashSet<String>,
add_contents: impl FnOnce(&mut egui::Ui, &mut HashSet<String>) -> R,
) -> Option<egui::CollapsingResponse<R>> {
section(
ui,
title,
id,
keywords,
default_open,
collapsed_sections,
add_contents,
)
}
fn section<R>(
ui: &mut egui::Ui,
title: &str,
id: &str,
keywords: &[&str],
default_open: bool,
collapsed_sections: &mut HashSet<String>,
add_contents: impl FnOnce(&mut egui::Ui, &mut HashSet<String>) -> R,
) -> Option<egui::CollapsingResponse<R>> {
let view = crate::search::live_view(ui.ctx());
if view.as_ref().is_some_and(|v| v.hides(id)) {
return None;
}
let searching = view.as_ref().is_some_and(|v| v.active());
let force_open = crate::search::forces_open(ui.ctx(), view.as_deref(), id);
let header = if searching {
egui::CollapsingHeader::new(title)
.id_salt((id, "search"))
.default_open(true)
} else {
let should_be_open = collapsed_sections.contains(id) != default_open;
egui::CollapsingHeader::new(title)
.id_salt(id)
.default_open(should_be_open)
}
.open(force_open.then_some(true));
let mut body = None;
let response = header.show(ui, |ui| {
let r = add_contents(ui, collapsed_sections);
body = Some((ui.unique_id(), ui.min_rect()));
r
});
if !searching {
let was_open = collapsed_sections.contains(id) != default_open;
let toggled = response.header_response.clicked() || (force_open && !was_open);
if toggled && !collapsed_sections.remove(id) {
collapsed_sections.insert(id.to_string());
}
}
crate::search::note_section(
ui,
crate::search::DrawnSection {
id,
title,
keywords,
header: response.header_response.clone(),
body,
fully_open: response.fully_open(),
},
);
Some(response)
}
pub fn section_heading(ui: &mut egui::Ui, title: &str) {
ui.add_space(8.0);
ui.heading(title);
ui.add_space(4.0);
}
pub fn subsection_label(ui: &mut egui::Ui, title: &str) {
ui.add_space(8.0);
ui.label(egui::RichText::new(title).strong());
ui.add_space(4.0);
}
pub fn section_spacing(ui: &mut egui::Ui) {
ui.add_space(12.0);
}
pub fn indented<R>(
ui: &mut egui::Ui,
id: impl std::hash::Hash + std::fmt::Debug,
add_contents: impl FnOnce(&mut egui::Ui) -> R,
) -> egui::InnerResponse<R> {
ui.indent(id, add_contents)
}