mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-27 11:56:50 -06:00
#385: outline modes and soft-proof stay with their file; one toggle table for the menu's check marks
This commit is contained in:
@@ -22,31 +22,33 @@ fn flag(s: &mut Session, p: &Value, f: fn(&mut ViewState) -> &mut bool) -> Resul
|
||||
/// One of a view's on/off settings.
|
||||
type Slot = fn(&mut ViewState) -> &mut bool;
|
||||
|
||||
/// The View menu's toggles the preferences keep: files opened or made later start with them.
|
||||
const KEPT: &[(&str, Slot)] = &[
|
||||
("view.pixelGrid", |v| &mut v.pixel_grid),
|
||||
("view.layoutGrids", |v| &mut v.layout_grids),
|
||||
("view.rulers", |v| &mut v.rulers),
|
||||
("view.slices", |v| &mut v.show_slices),
|
||||
("view.comments", |v| &mut v.comments),
|
||||
("view.multiplayerCursors", |v| &mut v.multiplayer_cursors),
|
||||
("view.outlines", |v| &mut v.outlines),
|
||||
("view.outlinesSelection", |v| &mut v.outlines_selection),
|
||||
("view.outlinesHiddenLayers", |v| &mut v.outline_hidden),
|
||||
("view.outlinesObjectBounds", |v| &mut v.outline_bounds),
|
||||
("view.maskOutlines", |v| &mut v.mask_outlines),
|
||||
("view.frameOutlines", |v| &mut v.frame_outlines),
|
||||
("view.proofSrgb", |v| &mut v.proof_srgb),
|
||||
("view.snapGeometry", |v| &mut v.snap_geometry),
|
||||
("view.snapObjects", |v| &mut v.snap_objects),
|
||||
("view.snapPixel", |v| &mut v.snap_pixel),
|
||||
/// The View menu's on/off toggles (Pixel preview, with its scale, has its own command), and
|
||||
/// whether the preferences keep each: files opened or made later start with the kept ones. Outline
|
||||
/// modes and the sRGB soft-proof stay with their file, so a new file never opens in one.
|
||||
const TOGGLES: &[(&str, Slot, bool)] = &[
|
||||
("view.pixelGrid", |v| &mut v.pixel_grid, true),
|
||||
("view.layoutGrids", |v| &mut v.layout_grids, true),
|
||||
("view.rulers", |v| &mut v.rulers, true),
|
||||
("view.slices", |v| &mut v.show_slices, true),
|
||||
("view.comments", |v| &mut v.comments, true),
|
||||
("view.multiplayerCursors", |v| &mut v.multiplayer_cursors, true),
|
||||
("view.outlines", |v| &mut v.outlines, false),
|
||||
("view.outlinesSelection", |v| &mut v.outlines_selection, false),
|
||||
("view.outlinesHiddenLayers", |v| &mut v.outline_hidden, true),
|
||||
("view.outlinesObjectBounds", |v| &mut v.outline_bounds, true),
|
||||
("view.maskOutlines", |v| &mut v.mask_outlines, true),
|
||||
("view.frameOutlines", |v| &mut v.frame_outlines, true),
|
||||
("view.proofSrgb", |v| &mut v.proof_srgb, false),
|
||||
("view.snapGeometry", |v| &mut v.snap_geometry, true),
|
||||
("view.snapObjects", |v| &mut v.snap_objects, true),
|
||||
("view.snapPixel", |v| &mut v.snap_pixel, true),
|
||||
];
|
||||
|
||||
/// A View toggle the preferences keep: the file's view changes and the preferences remember it.
|
||||
fn kept(s: &mut Session, p: &Value, id: &str) -> Result<Value> {
|
||||
let Some((_, f)) = KEPT.iter().find(|(k, _)| *k == id) else { return Err(bad(id, "not a View toggle")) };
|
||||
let r = flag(s, p, *f)?;
|
||||
if let Some(on) = r.get("on").and_then(Value::as_bool) {
|
||||
/// View toggle `id`: the file's view changes, and the preferences remember a kept one.
|
||||
fn toggle(s: &mut Session, p: &Value, id: &str) -> Result<Value> {
|
||||
let Some(&(_, f, keep)) = TOGGLES.iter().find(|(k, ..)| *k == id) else { return Err(bad(id, "not a View toggle")) };
|
||||
let r = flag(s, p, f)?;
|
||||
if keep && let Some(on) = r.get("on").and_then(Value::as_bool) {
|
||||
s.prefs.view.insert(id.to_string(), on);
|
||||
}
|
||||
Ok(r)
|
||||
@@ -54,7 +56,7 @@ fn kept(s: &mut Session, p: &Value, id: &str) -> Result<Value> {
|
||||
|
||||
/// Set `v`'s View toggles to the ones the preferences keep (the others stay as they are).
|
||||
pub(crate) fn apply_kept(prefs: &crate::Prefs, v: &mut ViewState) {
|
||||
for (id, f) in KEPT {
|
||||
for (id, f, _) in TOGGLES.iter().filter(|t| t.2) {
|
||||
if let Some(on) = prefs.view.get(*id) {
|
||||
*f(v) = *on;
|
||||
}
|
||||
@@ -64,27 +66,11 @@ pub(crate) fn apply_kept(prefs: &crate::Prefs, v: &mut ViewState) {
|
||||
/// Whether the View menu's toggle `id` is on (the menus show a check mark); `None` for a command
|
||||
/// that isn't one.
|
||||
pub fn checked(s: &Session, id: &str) -> Option<bool> {
|
||||
let v = &s.ds().ok()?.view;
|
||||
Some(match id {
|
||||
"view.pixelGrid" => v.pixel_grid,
|
||||
"view.layoutGrids" => v.layout_grids,
|
||||
"view.rulers" => v.rulers,
|
||||
"view.slices" => v.show_slices,
|
||||
"view.comments" => v.comments,
|
||||
"view.multiplayerCursors" => v.multiplayer_cursors,
|
||||
"view.outlines" => v.outlines,
|
||||
"view.outlinesSelection" => v.outlines_selection,
|
||||
"view.outlinesHiddenLayers" => v.outline_hidden,
|
||||
"view.outlinesObjectBounds" => v.outline_bounds,
|
||||
"view.maskOutlines" => v.mask_outlines,
|
||||
"view.pixelPreview" => v.pixel_preview,
|
||||
"view.frameOutlines" => v.frame_outlines,
|
||||
"view.proofSrgb" => v.proof_srgb,
|
||||
"view.snapGeometry" => v.snap_geometry,
|
||||
"view.snapObjects" => v.snap_objects,
|
||||
"view.snapPixel" => v.snap_pixel,
|
||||
_ => return None,
|
||||
})
|
||||
let mut v = s.ds().ok()?.view.clone();
|
||||
if id == "view.pixelPreview" {
|
||||
return Some(v.pixel_preview);
|
||||
}
|
||||
TOGGLES.iter().find(|(k, ..)| *k == id).map(|(_, f, _)| *f(&mut v))
|
||||
}
|
||||
|
||||
/// Pixel preview: toggles, or turns on at a resolution of 1x or 2x.
|
||||
@@ -277,46 +263,46 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
spec!("view.nextFrame", "Zoom to next frame", "{}", false, |s, _| zoom_frame(s, 1)),
|
||||
spec!("view.prevPage", "Previous page", "{}", false, |s, _| page_step(s, -1)),
|
||||
spec!("view.nextPage", "Next page", "{}", false, |s, _| page_step(s, 1)),
|
||||
spec!("view.pixelGrid", "Pixel grid", "{on?}", false, |s, p| kept(s, p, "view.pixelGrid")),
|
||||
spec!("view.layoutGrids", "Layout grids", "{on?}", false, |s, p| kept(s, p, "view.layoutGrids")),
|
||||
spec!("view.rulers", "Rulers", "{on?}", false, |s, p| kept(s, p, "view.rulers")),
|
||||
spec!("view.slices", "Slices", "{on?}", false, |s, p| kept(s, p, "view.slices")),
|
||||
spec!("view.comments", "Comments", "{on?}", false, |s, p| kept(s, p, "view.comments")),
|
||||
spec!("view.pixelGrid", "Pixel grid", "{on?}", false, |s, p| toggle(s, p, "view.pixelGrid")),
|
||||
spec!("view.layoutGrids", "Layout grids", "{on?}", false, |s, p| toggle(s, p, "view.layoutGrids")),
|
||||
spec!("view.rulers", "Rulers", "{on?}", false, |s, p| toggle(s, p, "view.rulers")),
|
||||
spec!("view.slices", "Slices", "{on?}", false, |s, p| toggle(s, p, "view.slices")),
|
||||
spec!("view.comments", "Comments", "{on?}", false, |s, p| toggle(s, p, "view.comments")),
|
||||
spec!("view.multiplayerCursors", "Multiplayer cursors", "{on?} others' cursors in a live session", false, |s, p| {
|
||||
kept(s, p, "view.multiplayerCursors")
|
||||
toggle(s, p, "view.multiplayerCursors")
|
||||
}),
|
||||
spec!("view.pan", "Pan the view", "{dx, dy (arrow-key steps, e.g. 1 or -1), big? (ten times as far, like Shift)}", false, pan),
|
||||
spec!("view.outlines", "Outlines", "{on?}", false, |s, p| kept(s, p, "view.outlines")),
|
||||
spec!("view.outlines", "Outlines", "{on?}", false, |s, p| toggle(s, p, "view.outlines")),
|
||||
spec!("view.outlinesSelection", "Outlines for selection", "{on?} outline mode for the selected layers only", false, |s, p| {
|
||||
kept(s, p, "view.outlinesSelection")
|
||||
toggle(s, p, "view.outlinesSelection")
|
||||
}),
|
||||
spec!(
|
||||
"view.outlinesHiddenLayers",
|
||||
"Include hidden layers in outlines",
|
||||
"{on?} outline mode (and outlines for the selection) outlines hidden layers too; they can be clicked there",
|
||||
false,
|
||||
|s, p| { kept(s, p, "view.outlinesHiddenLayers") }
|
||||
|s, p| { toggle(s, p, "view.outlinesHiddenLayers") }
|
||||
),
|
||||
spec!(
|
||||
"view.outlinesObjectBounds",
|
||||
"Include object bounds in outlines",
|
||||
"{on?} outline mode also draws each layer's box (groups, text, vectors); a click on a box's edge picks its layer",
|
||||
false,
|
||||
|s, p| { kept(s, p, "view.outlinesObjectBounds") }
|
||||
|s, p| { toggle(s, p, "view.outlinesObjectBounds") }
|
||||
),
|
||||
spec!("view.maskOutlines", "Mask outlines", "{on?}", false, |s, p| kept(s, p, "view.maskOutlines")),
|
||||
spec!("view.maskOutlines", "Mask outlines", "{on?}", false, |s, p| toggle(s, p, "view.maskOutlines")),
|
||||
spec!("view.pixelPreview", "Pixel preview", "{on?, scale? (1|2: rendered pixels per page pixel; turns it on)}", false, pixel_preview),
|
||||
spec!("view.frameOutlines", "Frame outlines", "{on?}", false, |s, p| kept(s, p, "view.frameOutlines")),
|
||||
spec!("view.frameOutlines", "Frame outlines", "{on?}", false, |s, p| toggle(s, p, "view.frameOutlines")),
|
||||
spec!(
|
||||
"view.proofSrgb",
|
||||
"Soft-proof sRGB",
|
||||
"{on?} mark the colours of a Display P3 file that sRGB screens and files can't show",
|
||||
false,
|
||||
|s, p| { kept(s, p, "view.proofSrgb") }
|
||||
|s, p| { toggle(s, p, "view.proofSrgb") }
|
||||
),
|
||||
spec!("view.snapGeometry", "Snap to geometry", "{on?}", false, |s, p| kept(s, p, "view.snapGeometry")),
|
||||
spec!("view.snapObjects", "Snap to objects", "{on?}", false, |s, p| kept(s, p, "view.snapObjects")),
|
||||
spec!("view.snapPixel", "Snap to pixel grid", "{on?}", false, |s, p| kept(s, p, "view.snapPixel")),
|
||||
spec!("view.snapGeometry", "Snap to geometry", "{on?}", false, |s, p| toggle(s, p, "view.snapGeometry")),
|
||||
spec!("view.snapObjects", "Snap to objects", "{on?}", false, |s, p| toggle(s, p, "view.snapObjects")),
|
||||
spec!("view.snapPixel", "Snap to pixel grid", "{on?}", false, |s, p| toggle(s, p, "view.snapPixel")),
|
||||
spec!("view.toggleUi", "Show/Hide UI", "{on?}", false, |s, p| flag(s, p, |v| &mut v.ui_hidden)),
|
||||
spec!("view.fullscreen", "Full screen", "{on?} the window fills the screen (the desktop app; a no-op headless)", false, |s, p| {
|
||||
s.request_ui("window.fullscreen", json!({"on": boolean(p, "on")}));
|
||||
|
||||
@@ -756,6 +756,10 @@ fn view_toggles_are_kept_for_the_next_launch() {
|
||||
for (cmd, on, _, _) in &rows {
|
||||
ex(&mut s, cmd, json!({"on": on}));
|
||||
}
|
||||
// Modes stay with their file: a new one never opens in outline mode or soft-proofing.
|
||||
for cmd in ["view.outlines", "view.outlinesSelection", "view.proofSrgb"] {
|
||||
ex(&mut s, cmd, json!({"on": true}));
|
||||
}
|
||||
// The app writes its preferences as JSON and reads them back at the next launch.
|
||||
let saved = serde_json::to_value(&s.prefs).unwrap();
|
||||
let mut next = Session::empty();
|
||||
@@ -765,4 +769,5 @@ fn view_toggles_are_kept_for_the_next_launch() {
|
||||
for (cmd, on, get, why) in rows {
|
||||
assert_eq!(get(view), on, "{cmd}: {why}");
|
||||
}
|
||||
assert!(!view.outlines && !view.outlines_selection && !view.proof_srgb, "{view:?}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user