New Document: a tab shows its first preset; Saved starts with [Default], as in InDesign

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
moppeeer
2026-10-15 17:18:43 +02:00
co-authored by Claude Opus 5.5
parent 1062d39511
commit 3d91e64605
3 changed files with 62 additions and 41 deletions
-10
View File
@@ -3131,16 +3131,6 @@ const TABLE: &[(&str, [&str; 5])] = &[
"空白文档预设",
],
),
(
"No saved presets yet. Set the details, then click Save Document Preset.",
[
"Noch keine gespeicherten Vorgaben. Legen Sie die Details fest und klicken Sie auf Dokumentvorgabe speichern.",
"Aucun paramètre prédéfini enregistré. Définissez les détails, puis cliquez sur Enregistrer le paramètre prédéfini de document.",
"Aún no hay ajustes preestablecidos guardados. Defina los detalles y haga clic en Guardar ajuste preestablecido de documento.",
"保存済みのプリセットはありません。詳細を設定してから「ドキュメントプリセットを保存」をクリックしてください。",
"尚无已保存的预设。请设置详细信息,然后单击“存储文档预设”。",
],
),
("Delete Preset", ["Vorgabe löschen", "Supprimer le paramètre prédéfini", "Eliminar ajuste preestablecido", "プリセットを削除", "删除预设"]),
(
"Preset Details",
-4
View File
@@ -1609,10 +1609,6 @@ pub(super) const TABLE: &[(&str, &str)] = &[
("Your Recent Items", "Elementi recenti"),
("Saved Presets", "Predefiniti salvati"),
("Blank Document Presets", "Predefiniti documento vuoto"),
(
"No saved presets yet. Set the details, then click Save Document Preset.",
"Nessun predefinito salvato. Imposta i dettagli, poi fai clic su Salva predefinito documento.",
),
("Delete Preset", "Elimina predefinito"),
("Preset Details", "Dettagli predefinito"),
("Document Name", "Nome documento"),
+62 -27
View File
@@ -177,20 +177,19 @@ fn cards(app: &DesignApp, tab: &str) -> Vec<Card> {
};
Card { key, label, nd, custom }
};
// Recent and Saved both start with the factory settings, as in InDesign.
let default = Card { key: DEFAULT_CARD.into(), label: crate::i18n::tr(lang, DEFAULT_CARD).into(), nd: NewDocument::default(), custom: false };
match tab {
"recent" => {
let default =
Card { key: DEFAULT_CARD.into(), label: crate::i18n::tr(lang, DEFAULT_CARD).into(), nd: NewDocument::default(), custom: false };
std::iter::once(default)
.chain(app.ui.recent_new_documents.iter().enumerate().map(|(i, nd)| named(format!("recent:{i}"), nd.clone())))
.collect()
}
"saved" => app
.session
.prefs
.document_presets
.iter()
.map(|nd| Card { key: format!("saved:{}", nd.title), label: nd.title.clone(), nd: nd.clone(), custom: preset_name(nd).is_none() })
"recent" => std::iter::once(default)
.chain(app.ui.recent_new_documents.iter().enumerate().map(|(i, nd)| named(format!("recent:{i}"), nd.clone())))
.collect(),
"saved" => std::iter::once(default)
.chain(app.session.prefs.document_presets.iter().map(|nd| Card {
key: format!("saved:{}", nd.title),
label: nd.title.clone(),
nd: nd.clone(),
custom: preset_name(nd).is_none(),
}))
.collect(),
tab => {
let intent = match tab {
@@ -303,8 +302,12 @@ fn tabs(app: &DesignApp, ui: &mut Ui, d: &mut Dialog) {
t.text_strong,
);
}
if resp.clicked() {
if resp.clicked() && !on {
d.fields.insert("tab".into(), json!(key));
// As in InDesign, a tab shows its first preset's details (Web and Mobile in pixels).
if let Some(c) = cards(app, key).into_iter().next() {
select(d, &c);
}
}
resp.widget_info(|| egui::WidgetInfo::selected(egui::WidgetType::SelectableLabel, true, on, text));
}
@@ -314,6 +317,14 @@ fn tabs(app: &DesignApp, ui: &mut Ui, d: &mut Dialog) {
ui.painter().line_segment([pos2(ui.min_rect().left(), y), pos2(ui.max_rect().right(), y)], Stroke::new(1.0, t.divider));
}
/// Fill the details from card `c`, keeping the typed document name.
fn select(d: &mut Dialog, c: &Card) {
let name = d.s("name");
d.fields.extend(fields_for(&c.nd));
d.fields.insert("name".into(), json!(name));
d.fields.insert("preset".into(), json!(c.key));
}
/// The cards of the current tab; true when one was double-clicked (create at once).
fn presets(app: &mut DesignApp, ui: &mut Ui, d: &mut Dialog) -> bool {
let t = Tokens::get(ui.ctx());
@@ -335,13 +346,6 @@ fn presets(app: &mut DesignApp, ui: &mut Ui, d: &mut Dialog) -> bool {
);
});
ui.add_space(10.0);
if list.is_empty() {
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(&lang, "No saved presets yet. Set the details, then click Save Document Preset.")).color(t.text_dim),
);
return false;
}
let current = d.s("preset");
let mut create = false;
let mut delete = None;
@@ -351,15 +355,12 @@ fn presets(app: &mut DesignApp, ui: &mut Ui, d: &mut Dialog) -> bool {
for c in &list {
let resp = card(ui, c, current == c.key);
if resp.clicked() || resp.double_clicked() {
let name = d.s("name");
d.fields.extend(fields_for(&c.nd));
d.fields.insert("name".into(), json!(name));
d.fields.insert("preset".into(), json!(c.key));
select(d, c);
}
if resp.double_clicked() {
create = true;
}
if tab == "saved" {
if tab == "saved" && c.key != DEFAULT_CARD {
resp.context_menu(|ui| {
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&lang, "Delete Preset"))).clicked() {
delete = Some(c.nd.title.clone());
@@ -816,6 +817,38 @@ mod tests {
assert_eq!(recent[1].nd.start_page, 3);
}
#[test]
fn a_tab_shows_its_first_preset_as_indesign_does() {
let mut app = app();
let ctx = context();
// A tab's name, a little above its painted text's box (which reaches below the strip).
let tab = |texts: &[(String, Rect)], name: &str| texts.iter().find(|(t, _)| t == name).map(|(_, r)| r.center() - vec2(0.0, 12.0)).unwrap();
let texts = settle(&mut app, &ctx);
app.ui.dialog.as_mut().unwrap().fields.insert("name".into(), json!("Poster"));
click(&mut app, &ctx, tab(&texts, "Web"), 1);
let d = app.ui.dialog.as_ref().unwrap();
assert_eq!((d.s("tab"), d.s("preset")), ("web".into(), "Web 1920 \u{d7} 1080".into()));
assert_eq!((d.s("width"), d.s("height"), units(d)), ("1920 px".into(), "1080 px".into(), Unit::Pixels), "Web is in pixels");
assert!(!d.b("facingPages"));
assert_eq!(d.s("name"), "Poster", "the typed name stays");
let texts = settle(&mut app, &ctx);
click(&mut app, &ctx, tab(&texts, "Print"), 1);
let d = app.ui.dialog.as_ref().unwrap();
assert_eq!((d.s("preset"), units(d)), ("Letter".into(), Unit::Picas));
// Clicking the open tab again keeps what was picked on it.
let texts = settle(&mut app, &ctx);
click(&mut app, &ctx, card_at(&texts, "A4"), 1);
click(&mut app, &ctx, tab(&texts, "Print"), 1);
assert_eq!(app.ui.dialog.as_ref().unwrap().s("preset"), "A4");
// Saved starts with [Default], as in InDesign, and selects it.
let texts = settle(&mut app, &ctx);
click(&mut app, &ctx, tab(&texts, "Saved"), 1);
let d = app.ui.dialog.as_ref().unwrap();
assert_eq!((d.s("preset"), d.s("width")), (DEFAULT_CARD.into(), "51p0".into()));
let texts = settle(&mut app, &ctx);
assert!(texts.iter().any(|(t, _)| t == "SAVED PRESETS (1)"));
}
#[test]
fn double_clicking_a_card_creates_the_document() {
let mut app = app();
@@ -905,7 +938,9 @@ mod tests {
assert!(save_preset(&mut app, &mut d, "Letter").is_err(), "a built-in name is refused");
assert_eq!((d.s("tab"), d.s("preset")), ("saved".into(), "saved:Newsletter".into()));
let saved = cards(&app, "saved");
assert_eq!((saved.len(), saved[0].label.as_str(), saved[0].nd.pages), (1, "Newsletter", 8));
assert_eq!(saved.len(), 2);
assert_eq!((saved[0].key.as_str(), saved[0].nd.pages), (DEFAULT_CARD, 1), "[Default] comes first, as in InDesign");
assert_eq!((saved[1].label.as_str(), saved[1].nd.pages), ("Newsletter", 8));
let p = params(&d);
remember(&mut app, &p);
remember(&mut app, &p);