forked from 1b-agent/opencadstudio-upstream
Phase 4.1. Handle / u64 keys dominate the hot maps (block_defn, hatch / image / mesh caches, draw-depth map, viewport wire caches); the default SipHash is wasted work on integer keys. Alias the std types to rustc_hash::FxHashMap / FxHashSet crate-wide so every map and set — including the cross-module draw-depth map shared between scene and the GPU pipelines — uses the faster hasher consistently. Mechanical: no behavioural change. Constructors moved from ::new() to ::default() (Fx variants carry no RandomState). Build + 13 tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
186 lines
5.7 KiB
Rust
186 lines
5.7 KiB
Rust
//! Status-bar customization — which toggle pills are shown on the bar.
|
|
//!
|
|
//! The customization menu (opened from the bar's far-right handle) lists every
|
|
//! pill with a check mark next to the ones currently shown. Toggling a row
|
|
//! adds or removes that pill from the bar. The choice is persisted so it
|
|
//! survives across sessions.
|
|
|
|
use rustc_hash::FxHashSet as HashSet;
|
|
use std::path::PathBuf;
|
|
|
|
/// Identifies a toggleable status-bar pill.
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
|
pub enum StatusPill {
|
|
Coords,
|
|
Snap,
|
|
Grid,
|
|
Ortho,
|
|
Lwt,
|
|
Polar,
|
|
Dyn,
|
|
Otrack,
|
|
Osnap,
|
|
Space,
|
|
Scale,
|
|
Units,
|
|
Transparency,
|
|
Isolate,
|
|
QuickProps,
|
|
SelFilter,
|
|
SelCycle,
|
|
Vp,
|
|
CleanScreen,
|
|
}
|
|
|
|
impl StatusPill {
|
|
/// Every pill, in status-bar display order. Drives both the bar layout and
|
|
/// the customization menu.
|
|
pub const ALL: &'static [StatusPill] = &[
|
|
StatusPill::Coords,
|
|
StatusPill::Snap,
|
|
StatusPill::Grid,
|
|
StatusPill::Ortho,
|
|
StatusPill::Lwt,
|
|
StatusPill::Polar,
|
|
StatusPill::Dyn,
|
|
StatusPill::Otrack,
|
|
StatusPill::Osnap,
|
|
StatusPill::Space,
|
|
StatusPill::Scale,
|
|
StatusPill::Units,
|
|
StatusPill::Transparency,
|
|
StatusPill::Isolate,
|
|
StatusPill::QuickProps,
|
|
StatusPill::SelFilter,
|
|
StatusPill::SelCycle,
|
|
StatusPill::Vp,
|
|
StatusPill::CleanScreen,
|
|
];
|
|
|
|
/// Stable identifier used for persistence.
|
|
pub fn id(self) -> &'static str {
|
|
match self {
|
|
StatusPill::Coords => "coords",
|
|
StatusPill::Snap => "snap",
|
|
StatusPill::Grid => "grid",
|
|
StatusPill::Ortho => "ortho",
|
|
StatusPill::Lwt => "lwt",
|
|
StatusPill::Polar => "polar",
|
|
StatusPill::Dyn => "dyn",
|
|
StatusPill::Otrack => "otrack",
|
|
StatusPill::Osnap => "osnap",
|
|
StatusPill::Space => "space",
|
|
StatusPill::Scale => "scale",
|
|
StatusPill::Units => "units",
|
|
StatusPill::Transparency => "transparency",
|
|
StatusPill::Isolate => "isolate",
|
|
StatusPill::QuickProps => "quickprops",
|
|
StatusPill::SelFilter => "selfilter",
|
|
StatusPill::SelCycle => "selcycle",
|
|
StatusPill::Vp => "vp",
|
|
StatusPill::CleanScreen => "cleanscreen",
|
|
}
|
|
}
|
|
|
|
/// Label shown in the customization menu.
|
|
pub fn label(self) -> &'static str {
|
|
match self {
|
|
StatusPill::Coords => "Coordinates",
|
|
StatusPill::Snap => "Snap Mode",
|
|
StatusPill::Grid => "Grid",
|
|
StatusPill::Ortho => "Ortho Mode",
|
|
StatusPill::Lwt => "Show Lineweight",
|
|
StatusPill::Polar => "Polar Tracking",
|
|
StatusPill::Dyn => "Dynamic Input",
|
|
StatusPill::Otrack => "Object Snap Tracking",
|
|
StatusPill::Osnap => "Object Snap",
|
|
StatusPill::Space => "Model/Paper Space",
|
|
StatusPill::Scale => "Annotation Scale",
|
|
StatusPill::Units => "Drawing Units",
|
|
StatusPill::Transparency => "Show Transparency",
|
|
StatusPill::Isolate => "Isolate Objects",
|
|
StatusPill::QuickProps => "Quick Properties",
|
|
StatusPill::SelFilter => "Selection Filtering",
|
|
StatusPill::SelCycle => "Selection Cycling",
|
|
StatusPill::Vp => "Viewport Count",
|
|
StatusPill::CleanScreen => "Clean Screen",
|
|
}
|
|
}
|
|
|
|
fn from_id(s: &str) -> Option<StatusPill> {
|
|
StatusPill::ALL.iter().copied().find(|p| p.id() == s)
|
|
}
|
|
}
|
|
|
|
/// Tracks which pills the user has hidden. Empty = every pill visible.
|
|
#[derive(Clone, Default)]
|
|
pub struct StatusBarConfig {
|
|
hidden: HashSet<StatusPill>,
|
|
}
|
|
|
|
impl StatusBarConfig {
|
|
/// Load the saved customization, or all-visible when none exists.
|
|
pub fn load() -> Self {
|
|
let mut hidden = HashSet::default();
|
|
if let Some(path) = config_path() {
|
|
if let Ok(body) = std::fs::read_to_string(path) {
|
|
for line in body.lines() {
|
|
if let Some(p) = StatusPill::from_id(line.trim()) {
|
|
hidden.insert(p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Self { hidden }
|
|
}
|
|
|
|
pub fn is_visible(&self, pill: StatusPill) -> bool {
|
|
!self.hidden.contains(&pill)
|
|
}
|
|
|
|
/// Flip a pill's visibility and persist the change.
|
|
pub fn toggle(&mut self, pill: StatusPill) {
|
|
if !self.hidden.remove(&pill) {
|
|
self.hidden.insert(pill);
|
|
}
|
|
self.save();
|
|
}
|
|
|
|
fn save(&self) {
|
|
let Some(path) = config_path() else { return };
|
|
if let Some(dir) = path.parent() {
|
|
let _ = std::fs::create_dir_all(dir);
|
|
}
|
|
let body: String = StatusPill::ALL
|
|
.iter()
|
|
.filter(|p| self.hidden.contains(p))
|
|
.map(|p| p.id())
|
|
.collect::<Vec<_>>()
|
|
.join("\n");
|
|
let _ = std::fs::write(path, body);
|
|
}
|
|
}
|
|
|
|
/// `<config-dir>/OpenCADStudio/statusbar.txt`, matching the recent-files store.
|
|
fn config_path() -> Option<PathBuf> {
|
|
let base: PathBuf = if cfg!(target_os = "windows") {
|
|
std::env::var_os("APPDATA").map(PathBuf::from)?
|
|
} else if cfg!(target_os = "macos") {
|
|
let home = std::env::var_os("HOME")?;
|
|
let mut p = PathBuf::from(home);
|
|
p.push("Library");
|
|
p.push("Application Support");
|
|
p
|
|
} else if let Some(d) = std::env::var_os("XDG_CONFIG_HOME") {
|
|
PathBuf::from(d)
|
|
} else {
|
|
let home = std::env::var_os("HOME")?;
|
|
let mut p = PathBuf::from(home);
|
|
p.push(".config");
|
|
p
|
|
};
|
|
let mut p = base;
|
|
p.push("OpenCADStudio");
|
|
Some(p.join("statusbar.txt"))
|
|
}
|