use crate::automation::hit_keys::HitKeyDoc;
use brep_render::engine_state::{EngineState, SelectionFilter};
use crate::icon_text::IconTextUi as _;
use eframe::egui;
use std::collections::HashMap;
const KINDS: [(&str, &str); 7] = [
("COMPONENT", "Component"),
("SOLID", "Solid"),
("SKETCH", "Sketch"),
("FACE", "Face"),
("EDGE", "Edge"),
("VERTEX", "Vertex"),
("PLANE", "Plane"),
];
#[derive(Default)]
pub struct SelectionPanel {
hits: HashMap<String, egui::Rect>,
inspection: super::selection_inspection::Cache,
button: Option<String>,
}
impl SelectionPanel {
pub fn new() -> Self {
Self::default()
}
pub fn show_status_bar(&mut self, ui: &mut egui::Ui, state: &mut EngineState, document: u64) {
let inspection = self.inspection.read(state, document);
ui.horizontal_wrapped(|ui| {
if let Some(text) = inspection["text"].as_str().filter(|s| !s.is_empty()) {
ui.label(text);
ui.separator();
}
self.show_filter_bar(ui, state);
});
}
pub fn show_filter_bar(&mut self, ui: &mut egui::Ui, state: &mut EngineState) {
self.hits.clear();
self.button = None;
if crate::viewport::touch::enabled(ui.ctx()) || ui.available_width() < crate::viewport::touch::NARROW_WIDTH {
let caption = button_caption(&state.selection_filter(), state.ref_select_active());
self.button = Some(caption.clone());
let menu = egui::containers::menu::MenuButton::new(caption)
.config(egui::containers::menu::MenuConfig::new()
.close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside))
.ui(ui, |ui| {
ui.set_width(240.0_f32.min(ui.ctx().content_rect().width() - 24.0));
egui::ScrollArea::vertical()
.max_height((ui.ctx().content_rect().height() - 100.0).max(88.0))
.show(ui, |ui| self.show_filters(ui, state));
});
self.hits.insert("filters:toggle".into(), menu.0.rect);
} else {
ui.horizontal_wrapped(|ui| self.show_filters(ui, state));
}
}
pub fn inspection_json(&mut self, state: &mut EngineState, document: u64) -> String {
self.inspection.read(state, document).to_string()
}
fn show_filters(&mut self, ui: &mut egui::Ui, state: &mut EngineState) {
let locked = state.ref_select_active();
if locked {
ui.icon_label(egui::RichText::new("\u{1f512} Reference selection — filter locked").weak())
.on_hover_text(
"The pick filter is set by the field being referenced. \
Finish the pick (Done on its field, or Escape) to change it.",
);
} else {
ui.label("Pickable:");
}
ui.add_enabled_ui(!locked, |ui| {
let mut filter = state.selection_filter();
let before = filter;
let all_on = KINDS.iter().all(|(k, _)| filter.get(k));
let any_on = KINDS.iter().any(|(k, _)| filter.get(k));
let mut all_checked = all_on;
let resp = ui.add(
egui::Checkbox::new(&mut all_checked, "All").indeterminate(any_on && !all_on),
);
self.hits.insert("filter:ALL".into(), resp.rect);
if resp.changed() {
let target = toggle_all_target(&filter);
for (kind, _) in KINDS {
filter.set(kind, target);
}
}
for (kind, label) in KINDS {
let mut on = filter.get(kind);
let resp = ui.checkbox(&mut on, label);
self.hits.insert(format!("filter:{kind}"), resp.rect);
if resp.changed() {
filter.set(kind, on);
}
}
if filter != before {
state.set_selection_filter(filter);
}
});
}
pub fn clear_hits(&mut self) {
self.hits.clear();
self.button = None;
}
pub fn button_json(&self) -> String {
serde_json::json!({ "shown": self.button.is_some(), "label": self.button }).to_string()
}
pub fn hits_json(&self) -> String {
crate::automation::hit_rects::hits_json(&self.hits)
}
}
fn button_caption(filter: &SelectionFilter, locked: bool) -> String {
let on: Vec<&str> = KINDS.iter().filter(|(k, _)| filter.get(k)).map(|(_, label)| *label).collect();
let what = match on.len() {
0 => "None".to_string(),
n if n == KINDS.len() => "All".to_string(),
1 | 2 => on.join(", "),
n => format!("{n} of {}", KINDS.len()),
};
if locked { format!("Filter (locked): {what}") } else { format!("Filter: {what}") }
}
fn toggle_all_target(filter: &SelectionFilter) -> bool {
!KINDS.iter().all(|(k, _)| filter.get(k))
}
pub static HIT_KEYS: &[HitKeyDoc] = &[
HitKeyDoc { panel: "selection", prefix: "filters:toggle", meaning: "show or hide pickable filters when the status bar is compact", command: None },
HitKeyDoc { panel: "selection", prefix: "filter:", meaning: "toggle which entity kinds a click may pick (filter:ALL, filter:kind)", command: None },
];