forked from 1b-agent/opencadstudio-upstream
Move grid and grid-snap toggles off the status bar (and its customization menu) into the top-left viewport chip, now a single container holding the horizontal/vertical split buttons, render-mode picker, and grid/snap toggles, sized to the render-mode picker. Add grid + magnet (snap) icons. Render the grid per model tile from each tile's own grid_on, in its own bounds and camera, instead of a single grid in the active tile. This stops the grid flickering on/off as the cursor crosses panes or leaves the viewport, and lets each pane show its own grid independently. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
178 lines
5.5 KiB
Rust
178 lines
5.5 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,
|
|
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::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::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::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"))
|
|
}
|