use std::sync::Arc;
use crate::search::{Hit, Jump, LiveView, Query, Registry};
use crate::sidebar::SettingsTab;
use super::SettingsUI;
#[derive(Default)]
pub struct SearchState {
registry: Option<Arc<Registry>>,
last_query: String,
view: Arc<LiveView>,
hits: Vec<Hit>,
}
impl SettingsUI {
pub fn search_registry(&mut self) -> Arc<Registry> {
if let Some(registry) = &self.search.registry {
return Arc::clone(registry);
}
let registry = Arc::new(self.build_search_registry());
self.search.registry = Some(Arc::clone(®istry));
registry
}
fn build_search_registry(&self) -> Registry {
let mut copy =
SettingsUI::new_with_assistant_prompts(self.config.clone(), Vec::new(), None);
copy.available_agent_ids = self.available_agent_ids.clone();
copy.supported_vsync_modes = self.supported_vsync_modes.clone();
copy.installation_type = self.installation_type;
copy.app_version = self.app_version;
copy.profile_modal_ui
.load_profiles(self.profile_modal_ui.get_working_profiles().to_vec());
copy.collapsed_sections.clear();
let mut registry = Registry::default();
for tab in SettingsTab::all() {
let base = registry.sections.len();
for mut section in crate::search::harvest_tab(&mut copy, *tab) {
section.parent = section.parent.map(|p| p + base);
registry.sections.push(section);
}
}
registry
}
pub fn search_hits(&self) -> &[Hit] {
&self.search.hits
}
pub(super) fn publish_search_view(&mut self, ctx: &egui::Context) {
let query_text = self.search_query.trim().to_string();
let changed = query_text != self.search.last_query;
if changed {
self.search.last_query = query_text.clone();
let view = if query_text.is_empty() {
self.search.hits.clear();
LiveView::default()
} else {
let registry = self.search_registry();
let query = Query::parse(&query_text);
self.search.hits = registry.search(&query);
LiveView {
active: true,
query_changed: true,
known: registry.known_sections(),
visible: registry.visible_sections(&query, &self.search.hits),
}
};
self.search.view = Arc::new(view);
} else if self.search.view.query_changed {
let mut view = (*self.search.view).clone();
view.query_changed = false;
self.search.view = Arc::new(view);
}
crate::search::set_live_view(ctx, Arc::clone(&self.search.view));
}
pub fn tab_has_results(&mut self, tab: SettingsTab) -> bool {
let query = Query::parse(&self.search.last_query);
if query.is_empty() {
return true;
}
let registry = self.search_registry();
registry.tab_matches(tab, &query, &self.search.hits)
}
pub fn go_to_result(&mut self, ctx: &egui::Context, hit: Hit) {
let registry = self.search_registry();
let Some(section) = registry.sections.get(hit.section) else {
return;
};
self.select_page(section.tab, section.page);
let path = registry
.path(hit.section)
.into_iter()
.map(|i| registry.sections[i].id.clone())
.collect();
let label = registry.control(hit).map(|c| c.label.clone());
crate::search::start_jump(ctx, Jump::new(path, label));
}
pub(super) fn show_search_field(&mut self, ui: &mut egui::Ui) {
if ui.input_mut(|i| i.consume_key(egui::Modifiers::COMMAND, egui::Key::F)) {
self.focus_search = true;
}
ui.horizontal(|ui| {
ui.label("Search:");
let response = ui.add(
egui::TextEdit::singleline(&mut self.search_query)
.id(search_field_id())
.hint_text("Setting name, description, or YAML key"),
);
if self.focus_search {
self.focus_search = false;
response.request_focus();
}
if !self.search_query.is_empty()
&& ui
.small_button("\u{2715}")
.on_hover_text("Clear search")
.clicked()
{
self.search_query.clear();
}
});
}
}
pub(crate) fn search_field_id() -> egui::Id {
egui::Id::new("settings_search_field")
}