mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-27 03:36:51 -06:00
New Workspace: Capture Panel Locations and Menu Customization, as InDesign's
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a930276fd7
commit
45a6cc54b9
@@ -347,6 +347,7 @@ list) · [roadmap.md](docs/roadmap.md) (milestones, plan) · [parity-checklist.m
|
||||
|
||||
Newest first.
|
||||
|
||||
- **2026-10-15** · New Workspace is InDesign's: Name, and Capture › Panel Locations and Menu Customization (the items hidden with Edit › Menus), both on by default; a workspace saved without panel locations leaves the panels where they are. No readiness percentage change.
|
||||
- **2026-10-14** · The Type tool draws a new text frame over a locked empty frame, as InDesign does, instead of turning the locked frame into a text frame. No readiness percentage change.
|
||||
- **2026-10-14** · 100% black shows as pure black on screen by default, as InDesign's Appearance of Black does out of the box (Display All Blacks as Rich Black); Preferences › Appearance of Black › Display All Blacks Accurately still shows it as the dark grey it prints, and a saved choice is kept. No readiness percentage change.
|
||||
- **2026-10-14** · Pathfinder keeps the frontmost object, as InDesign does: a photo placed over shapes and intersected with them shows through the shapes (it was dropped, and the shapes kept their black fill); Add, Exclude Overlap and Minus Back take the front object's fill too, Subtract still keeps the back one. No readiness percentage change.
|
||||
|
||||
@@ -1537,7 +1537,7 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
|
||||
let available = (ctx.content_rect().size() - frame_margin - egui::vec2(16.0, 16.0)).max(egui::Vec2::splat(1.0));
|
||||
let preferred_width: f32 = match d.id.as_str() {
|
||||
"alert" => 440.0,
|
||||
"closeDocument" | "updateLink" => 380.0,
|
||||
"closeDocument" | "updateLink" | "newWorkspace" => 380.0,
|
||||
"newDocument" if crate::i18n::is_rtl(&app.ui.language) => 380.0,
|
||||
_ => 640.0,
|
||||
};
|
||||
@@ -2036,10 +2036,10 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
|
||||
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Name:"));
|
||||
text_field(ui, &mut d, "name", 220.0);
|
||||
});
|
||||
crate::rtl::label(
|
||||
ui,
|
||||
egui::RichText::new(crate::i18n::tr(&app.ui.language, "Saves which bars show and where the panels are.")).size(10.5),
|
||||
);
|
||||
ui.add_space(6.0);
|
||||
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Capture")).font(semibold(12.0)));
|
||||
check(ui, &mut d, "panelLocations", crate::i18n::tr(&app.ui.language, "Panel Locations"));
|
||||
check(ui, &mut d, "menuCustomization", crate::i18n::tr(&app.ui.language, "Menu Customization"));
|
||||
}
|
||||
"fittingOptions" => {
|
||||
check(ui, &mut d, "autoFit", crate::i18n::tr(&app.ui.language, "Auto-Fit"));
|
||||
@@ -2635,7 +2635,10 @@ pub fn confirm(app: &mut DesignApp) -> Result<Value, String> {
|
||||
doc["glyphFallback"] = json!(d.b("glyphFallback"));
|
||||
app.run("document.preferences", doc)
|
||||
}
|
||||
"newWorkspace" => app.run("window.newWorkspace", json!({"name": d.s("name")})),
|
||||
"newWorkspace" => app.run(
|
||||
"window.newWorkspace",
|
||||
json!({"name": d.s("name"), "panelLocations": d.b("panelLocations"), "menuCustomization": d.b("menuCustomization")}),
|
||||
),
|
||||
"updateLink" => crate::panels::properties::run_update_links(app, d.fields.get("target").cloned().unwrap_or_else(|| json!({}))),
|
||||
"closeDocument" => {
|
||||
// The document may have moved in the tab row (or gone) while the dialog was open.
|
||||
|
||||
@@ -58,6 +58,8 @@ const TABLE: &[(&str, [&str; 5])] = &[
|
||||
("Cut", ["Ausschneiden", "Couper", "Cortar", "カット", "剪切"]),
|
||||
("Copy", ["Kopieren", "Copier", "Copiar", "コピー", "复制"]),
|
||||
("Paste", ["Einfügen", "Coller", "Pegar", "ペースト", "粘贴"]),
|
||||
("Capture", ["Erfassen", "Capturer", "Capturar", "取り込み", "捕捉"]),
|
||||
("Panel Locations", ["Bedienfeldpositionen", "Emplacements des panneaux", "Ubicaciones de paneles", "パネルの位置", "面板位置"]),
|
||||
(
|
||||
"Bulleted & Numbered Lists",
|
||||
[
|
||||
|
||||
@@ -30,6 +30,8 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "قص"),
|
||||
("Copy", "نسخ"),
|
||||
("Paste", "لصق"),
|
||||
("Capture", "التقاط"),
|
||||
("Panel Locations", "مواضع اللوحات"),
|
||||
("Bulleted & Numbered Lists", "القوائم ذات التعداد النقطي والرقمي"),
|
||||
("Current Page Number", "رقم الصفحة الحالية"),
|
||||
("Apply Color (,)", "تطبيق اللون (,)"),
|
||||
|
||||
@@ -30,6 +30,8 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "Ausschneiden"),
|
||||
("Copy", "Kopieren"),
|
||||
("Paste", "Einfügen"),
|
||||
("Capture", "Erfassen"),
|
||||
("Panel Locations", "Bedienfeldpositionen"),
|
||||
("Clear", "Löschen"),
|
||||
("Duplicate", "Duplizieren"),
|
||||
("Select All", "Alles auswählen"),
|
||||
|
||||
@@ -28,6 +28,8 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "Taglia"),
|
||||
("Copy", "Copia"),
|
||||
("Paste", "Incolla"),
|
||||
("Capture", "Acquisisci"),
|
||||
("Panel Locations", "Posizioni pannelli"),
|
||||
("Bulleted & Numbered Lists", "Elenchi puntati e numerati"),
|
||||
("Current Page Number", "Numero di pagina corrente"),
|
||||
("Apply Color (,)", "Applica colore (,)"),
|
||||
|
||||
@@ -30,6 +30,8 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "カット"),
|
||||
("Copy", "コピー"),
|
||||
("Paste", "ペースト"),
|
||||
("Capture", "取り込み"),
|
||||
("Panel Locations", "パネルの位置"),
|
||||
("Bulleted & Numbered Lists", "箇条書き"),
|
||||
("Apply Color (,)", "カラーを適用 (,)"),
|
||||
("Apply Gradient (.)", "グラデーションを適用 (.)"),
|
||||
|
||||
@@ -31,6 +31,8 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "ამოჭრა"),
|
||||
("Copy", "კოპირება"),
|
||||
("Paste", "ჩასმა"),
|
||||
("Capture", "შენახვა"),
|
||||
("Panel Locations", "პანელების მდებარეობა"),
|
||||
("Reverse Path", "ტრაექტორიის შებრუნება"),
|
||||
("Set Anchor", "საყრდენი წერტილის დაყენება"),
|
||||
("Clear", "გასუფთავება"),
|
||||
|
||||
@@ -31,6 +31,8 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "Recortar"),
|
||||
("Copy", "Copiar"),
|
||||
("Paste", "Colar"),
|
||||
("Capture", "Capturar"),
|
||||
("Panel Locations", "Locais dos painéis"),
|
||||
("Bulleted & Numbered Lists", "Listas com marcadores e numeradas"),
|
||||
("Show Hidden Characters", "Mostrar caracteres ocultos"),
|
||||
("Current Page Number", "Número da página atual"),
|
||||
|
||||
@@ -31,6 +31,8 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "Вирізати"),
|
||||
("Copy", "Копіювати"),
|
||||
("Paste", "Вставити"),
|
||||
("Capture", "Зберегти"),
|
||||
("Panel Locations", "Розташування панелей"),
|
||||
("Bulleted & Numbered Lists", "Марковані й нумеровані списки"),
|
||||
("Current Page Number", "Номер поточної сторінки"),
|
||||
("Apply Color (,)", "Застосувати колір (,)"),
|
||||
|
||||
@@ -113,6 +113,13 @@ pub struct SavedWorkspace {
|
||||
/// The workspace's own icon column (see [`UiState::icon_groups`]).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub icon_groups: Option<Vec<Vec<String>>>,
|
||||
/// Saved with New Workspace › Capture › Panel Locations off: choosing the workspace leaves the
|
||||
/// bars and panels where they are.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub keep_panels: bool,
|
||||
/// New Workspace › Capture › Menu Customization: the menu items hidden with Edit › Menus.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub hidden_menu_items: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// A missing on-by-default bool stays on. `bool::default` is false.
|
||||
|
||||
@@ -218,7 +218,12 @@ pub const UI_COMMANDS: &[(&str, &str, Option<&str>, &str)] = &[
|
||||
None,
|
||||
"{name: Advanced|Book|Digital Publishing|Essentials|Essentials Classic|Interactive for PDF|Printing and Proofing|Typography or a saved workspace}",
|
||||
),
|
||||
("window.newWorkspace", "New Workspace…", None, "{name} — saves the current bars and panel arrangement"),
|
||||
(
|
||||
"window.newWorkspace",
|
||||
"New Workspace…",
|
||||
None,
|
||||
"{name, panelLocations?: bool (default true: the bars and panels), menuCustomization?: bool (default true: the menu items hidden with Edit › Menus)} — saves the workspace",
|
||||
),
|
||||
("window.deleteWorkspace", "Delete Workspace…", None, "{name}"),
|
||||
("window.resetWorkspace", "Reset Workspace", None, "{} — back to the current workspace as saved (or its defaults)"),
|
||||
("window.brightness", "Interface Color Theme", None, "{brightness: dark|mediumDark|mediumLight|light|highContrast}"),
|
||||
@@ -1559,7 +1564,8 @@ fn run_ui_inner(app: &mut DesignApp, id: &str, p: &Value) -> Option<Result<Value
|
||||
"window.toolsDoubleColumn" => flag(&mut app.ui.tools_double_column),
|
||||
"window.newWorkspace" => {
|
||||
let Some(name) = p.get("name").and_then(Value::as_str).map(str::trim).filter(|n| !n.is_empty()) else {
|
||||
app.ui.dialog = Some(crate::dialogs::Dialog::new("newWorkspace", json!({"name": ""})));
|
||||
app.ui.dialog =
|
||||
Some(crate::dialogs::Dialog::new("newWorkspace", json!({"name": "", "panelLocations": true, "menuCustomization": true})));
|
||||
return Some(Ok(Value::Null));
|
||||
};
|
||||
let u = &app.ui;
|
||||
@@ -1576,6 +1582,8 @@ fn run_ui_inner(app: &mut DesignApp, id: &str, p: &Value) -> Option<Result<Value
|
||||
docking: u.docking.clone(),
|
||||
docking_hidden: u.docking_hidden.clone(),
|
||||
icon_groups: u.icon_groups.clone(),
|
||||
keep_panels: !p.get("panelLocations").and_then(Value::as_bool).unwrap_or(true),
|
||||
hidden_menu_items: p.get("menuCustomization").and_then(Value::as_bool).unwrap_or(true).then(|| u.hidden_menu_items.clone()),
|
||||
};
|
||||
app.ui.custom_workspaces.retain(|x| x.name != name);
|
||||
app.ui.custom_workspaces.push(w);
|
||||
@@ -1604,6 +1612,13 @@ fn run_ui_inner(app: &mut DesignApp, id: &str, p: &Value) -> Option<Result<Value
|
||||
app.ui.docking_opened = None;
|
||||
let name = p.get("name").and_then(Value::as_str).unwrap_or("Essentials");
|
||||
if let Some(w) = app.ui.custom_workspaces.iter().find(|w| w.name == name).cloned() {
|
||||
if let Some(hidden) = w.hidden_menu_items {
|
||||
app.ui.hidden_menu_items = hidden;
|
||||
}
|
||||
if w.keep_panels {
|
||||
app.ui.workspace = w.name;
|
||||
return Some(Ok(Value::Null));
|
||||
}
|
||||
app.ui.control_bar = w.control_bar;
|
||||
app.ui.task_bar = w.task_bar;
|
||||
app.ui.task_bar_pin = w.task_bar_pin;
|
||||
@@ -3935,6 +3950,41 @@ mod tests {
|
||||
assert_eq!(app.ui.workspace, "Essentials");
|
||||
}
|
||||
|
||||
/// New Workspace › Capture (Learn Skills Daily's InDesign course, 11:40): Panel Locations and
|
||||
/// Menu Customization, both on by default.
|
||||
#[test]
|
||||
fn new_workspace_captures_panel_locations_and_menu_customization() {
|
||||
let mut app = crate::DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
|
||||
// The dialog offers both, checked.
|
||||
run_ui(&mut app, "window.newWorkspace", &json!({})).unwrap().unwrap();
|
||||
let d = app.ui.dialog.take().unwrap();
|
||||
assert_eq!(
|
||||
(d.id.as_str(), d.fields["panelLocations"].clone(), d.fields["menuCustomization"].clone()),
|
||||
("newWorkspace", json!(true), json!(true))
|
||||
);
|
||||
// Both captured.
|
||||
app.ui.dock_tab = "swatches".into();
|
||||
app.ui.hidden_menu_items = vec!["Type/Glyphs".into()];
|
||||
run_ui(&mut app, "window.newWorkspace", &json!({"name": "All"})).unwrap().unwrap();
|
||||
// Menus only: choosing it leaves the panels where they are.
|
||||
app.ui.hidden_menu_items = vec!["Layout/Pages".into()];
|
||||
run_ui(&mut app, "window.newWorkspace", &json!({"name": "Menus", "panelLocations": false})).unwrap().unwrap();
|
||||
// Panels only: the menus stay as they are.
|
||||
app.ui.dock_tab = "layers".into();
|
||||
run_ui(&mut app, "window.newWorkspace", &json!({"name": "Panels", "menuCustomization": false})).unwrap().unwrap();
|
||||
app.ui.dock_tab = "properties".into();
|
||||
app.ui.hidden_menu_items.clear();
|
||||
run_ui(&mut app, "window.workspace", &json!({"name": "Menus"})).unwrap().unwrap();
|
||||
assert_eq!((app.ui.dock_tab.as_str(), app.ui.hidden_menu_items.clone()), ("properties", vec!["Layout/Pages".to_string()]));
|
||||
run_ui(&mut app, "window.workspace", &json!({"name": "Panels"})).unwrap().unwrap();
|
||||
assert_eq!((app.ui.dock_tab.as_str(), app.ui.hidden_menu_items.clone()), ("layers", vec!["Layout/Pages".to_string()]));
|
||||
run_ui(&mut app, "window.workspace", &json!({"name": "All"})).unwrap().unwrap();
|
||||
assert_eq!((app.ui.dock_tab.as_str(), app.ui.hidden_menu_items.clone()), ("swatches", vec!["Type/Glyphs".to_string()]));
|
||||
// A workspace saved before the options existed restores its panels.
|
||||
let old: crate::SavedWorkspace = serde_json::from_value(json!({"name": "Old", "dockTab": "pages"})).unwrap();
|
||||
assert!(!old.keep_panels && old.hidden_menu_items.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn essentials_classic_is_the_control_panel_and_iconic_groups() {
|
||||
let mut app = crate::DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
|
||||
|
||||
Reference in New Issue
Block a user