forked from 1b-agent/opencadstudio-upstream
App preferences were spread across six flat plain-text files (settings.txt,
recent.txt, recent_limit.txt, statusbar.txt, ribbon.txt, plot.txt), each with a
bespoke line parser and no grouping. Replace them with a single grouped JSON,
serialized via serde (enabling the derive feature; serde_json was already a
dependency).
- New app/config.rs: AppConfig { settings, recent, statusbar, ribbon, plot } →
<config>/settings.json (pretty JSON), load()/save().
- Make UserSettings, StatusBarConfig/StatusPill, CollapseMode and
PlotDialogState serde-derived; #[serde(skip)] marks the plot dialog's runtime
fields so only print preferences persist. Their bespoke .txt load/save is
removed.
- App gains current_config/apply_config/save_config: one AppConfig::load at
startup distributes into live state; every settings-change site
(persist_settings_if_changed, recent add/remove/limit, status-bar pill
toggle, ribbon density, plot commit) routes through save_config, which diffs
against the last write to avoid thrashing.
- Clean start: the old flat files are ignored (no migration). ocad.pgp stays a
separate hand-editable file.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
147 lines
4.6 KiB
Rust
147 lines
4.6 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 serde::{Deserialize, Serialize};
|
|
|
|
/// Identifies a toggleable status-bar pill.
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
|
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",
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/// Tracks which pills the user has hidden. Serialized as the "statusbar" section
|
|
/// of the app config ([`crate::app::config`]).
|
|
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct StatusBarConfig {
|
|
hidden: HashSet<StatusPill>,
|
|
}
|
|
|
|
impl Default for StatusBarConfig {
|
|
/// The out-of-the-box bar: a few informational / niche pills are hidden by
|
|
/// default to keep it uncluttered, and the user turns them on from the
|
|
/// customization (⚙) menu if wanted. The mode toggles that matter most
|
|
/// (Ortho, Polar, Otrack, Osnap, …) stay visible.
|
|
fn default() -> Self {
|
|
let hidden = [
|
|
StatusPill::Coords,
|
|
StatusPill::Lwt,
|
|
StatusPill::Dyn,
|
|
StatusPill::Space,
|
|
StatusPill::Units,
|
|
StatusPill::Transparency,
|
|
StatusPill::SelCycle,
|
|
StatusPill::Vp,
|
|
]
|
|
.into_iter()
|
|
.collect();
|
|
Self { hidden }
|
|
}
|
|
}
|
|
|
|
impl StatusBarConfig {
|
|
pub fn is_visible(&self, pill: StatusPill) -> bool {
|
|
!self.hidden.contains(&pill)
|
|
}
|
|
|
|
/// Flip a pill's visibility. Persistence is handled by the caller via the
|
|
/// consolidated app config (`save_config`).
|
|
pub fn toggle(&mut self, pill: StatusPill) {
|
|
if !self.hidden.remove(&pill) {
|
|
self.hidden.insert(pill);
|
|
}
|
|
}
|
|
}
|