From be0fbb75c047bb3579a90625456aa29919df314b Mon Sep 17 00:00:00 2001 From: moppeeer Date: Wed, 14 Oct 2026 12:32:25 +0200 Subject: [PATCH 1/4] Numbering & Section Options opens as New Section on a page without one, as InDesign's Chris Nielsen's "Numbering Page Sections" (4:43): with page 3 selected, the Pages panel menu's Numbering & Section Options opens InDesign's New Section window: Start Section checked, Automatic Page Numbering chosen and 1 offered in Start Page Numbering at, which stays editable. The window here was titled Numbering & Section Options, Start Section was off and the field showed the page's current number, greyed out. - Opened on a page that doesn't start a section, the window is New Section: Start Section on and 1 offered; on a section's first page it is Numbering & Section Options, as before. - Start Page Numbering at and Start Chapter Numbering at stay editable; typing a number chooses that option. - "New Section" translated. Co-Authored-By: Claude Opus 5.5 --- ROADMAP.md | 1 + crates/ui-egui/src/dialogs.rs | 50 +++++++++++++++++++++++++++ crates/ui-egui/src/i18n.rs | 1 + crates/ui-egui/src/i18n/ar.rs | 1 + crates/ui-egui/src/i18n/it.rs | 1 + crates/ui-egui/src/i18n/ja.rs | 1 + crates/ui-egui/src/i18n/pt_br.rs | 1 + crates/ui-egui/src/i18n/uk.rs | 1 + crates/ui-egui/src/section_options.rs | 24 ++++++++++--- 9 files changed, 76 insertions(+), 5 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 805dfeb6c..f9dfe361a 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -129,6 +129,7 @@ list) · [roadmap.md](docs/roadmap.md) (milestones, plan) · [parity-checklist.m Newest first. +- **2026-10-14** · Numbering & Section Options opened on a page that doesn't start a section is New Section, as in InDesign: Start Section on, 1 offered, and the start numbers stay editable (typing one chooses it). No readiness percentage change. - **2026-10-12** · macOS Dock Quit uses the same unsaved-document prompts as window and menu Quit. Repeated requests keep the current answers; Cancel retains every tab and unsaved recovery checkpoint, and completed Save/Don't Save answers exit through the ordinary event loop. No readiness percentage change. - **2026-10-12** · Orderly native exit writes the latest recovery state after finalizing current quit approvals; incomplete or stale plans keep their documents, and Undo to the saved state clears stale recovery. Unavailable or failed recovery is reported. Cancelable macOS Dock Quit remains open (#544). No readiness percentage change. - **2026-10-12** · Recovered documents keep a checkpoint until saved, closed or reverted, including a second crash before autosave. Restarted document IDs and explicit recovery folders preserve other snapshots; failed republishing retains the original. No readiness percentage change. diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index c56bf267e..26c339e72 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -1130,6 +1130,8 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) { "insertTable" => crate::i18n::tr(&app.ui.language, "Create Table"), "textFrameOptions" => crate::i18n::tr(&app.ui.language, "Text Frame Options"), "documentSetup" => crate::i18n::tr(&app.ui.language, "Document Setup"), + // On a page that doesn't start a section yet, InDesign titles it New Section. + "numberingSection" if d.b("newSection") => crate::i18n::tr(&app.ui.language, "New Section"), "numberingSection" => crate::i18n::tr(&app.ui.language, "Numbering & Section Options…").trim_end_matches('…'), "findChange" => crate::i18n::tr(&app.ui.language, "Find/Change"), "paragraphStyleOptions" => crate::i18n::tr(&app.ui.language, "Paragraph Style Options"), @@ -4156,6 +4158,54 @@ mod tests { } } + #[test] + fn on_a_page_without_a_section_the_window_is_new_section() { + // Chris Nielsen's "Numbering Page Sections" (4:43): page 3 selected, the Pages panel menu's + // Numbering & Section Options opens New Section with Start Section on and 1 offered. + let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default()); + app.run("file.new", json!({"pages": 6, "facingPages": true})).unwrap(); + app.canvas_rect = Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1000.0, 800.0))); + crate::canvas::go_to_page(&mut app, 2); + crate::menus::activate(&mut app, "layout.section", &Value::Null); + let d = app.ui.dialog.as_ref().unwrap(); + assert_eq!( + (d.n("page"), d.b("newSection"), d.b("startSection"), d.b("automatic"), d.s("startNumber")), + (Some(3.0), true, true, true, "1".into()) + ); + let ctx = dialog_context("en"); + let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1178.0, 814.0)); + for _ in 0..3 { + dialog_frame(&mut app, &ctx, screen, vec![]); + } + let labels = dialog_frame(&mut app, &ctx, screen, vec![]); + visible_label(&labels, screen, "New Section"); + // Typing a number in the field chooses Start Page Numbering at; OK: page 3 is page 2. + let field = visible_label(&labels, screen, "1"); + click_dialog(&mut app, &ctx, screen, field.center()); + dialog_frame( + &mut app, + &ctx, + screen, + vec![ + egui::Event::Key { key: egui::Key::End, physical_key: None, pressed: true, repeat: false, modifiers: egui::Modifiers::NONE }, + egui::Event::Key { key: egui::Key::Backspace, physical_key: None, pressed: true, repeat: false, modifiers: egui::Modifiers::NONE }, + egui::Event::Text("2".into()), + ], + ); + assert!(!app.ui.dialog.as_ref().unwrap().b("automatic"), "typing chose Start Page Numbering at"); + let labels = dialog_frame(&mut app, &ctx, screen, vec![]); + click_dialog(&mut app, &ctx, screen, visible_label(&labels, screen, "OK").center()); + assert!(app.ui.dialog.is_none()); + assert_eq!(app.session.active().unwrap().doc.page_name(2), "2"); + // Opened again on that page, it is Numbering & Section Options, showing the section. + crate::menus::activate(&mut app, "layout.section", &Value::Null); + let d = app.ui.dialog.as_ref().unwrap(); + assert_eq!((d.b("newSection"), d.b("startSection"), d.b("automatic"), d.s("startNumber")), (false, true, false, "2".into())); + let labels = (0..4).map(|_| dialog_frame(&mut app, &ctx, screen, vec![])).last().unwrap(); + visible_label(&labels, screen, "Numbering & Section Options"); + assert!(!labels.iter().any(|(t, _, _)| t == "New Section")); + } + #[test] fn preferences_sidebar_scroll_keeps_selected_options_visible() { let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default()); diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index bff4e0bd8..2d715ffbe 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -55,6 +55,7 @@ const TABLE: &[(&str, [&str; 5])] = &[ ("Cut", ["Ausschneiden", "Couper", "Cortar", "カット", "剪切"]), ("Copy", ["Kopieren", "Copier", "Copiar", "コピー", "复制"]), ("Paste", ["Einfügen", "Coller", "Pegar", "ペースト", "粘贴"]), + ("New Section", ["Neuer Abschnitt", "Nouvelle section", "Nueva sección", "新規セクション", "新建章节"]), ("Reverse Path", ["Pfadrichtung umkehren", "Inverser le tracé", "Invertir trazado", "パスの方向反転", "反转路径"]), ("Set Anchor", ["Ankerpunkt festlegen", "Définir le point d’ancrage", "Definir punto de ancla", "アンカーポイントを設定", "设置锚点"]), ("Clear", ["Löschen", "Effacer", "Borrar", "消去", "清除"]), diff --git a/crates/ui-egui/src/i18n/ar.rs b/crates/ui-egui/src/i18n/ar.rs index a182d09ec..f40ed5518 100644 --- a/crates/ui-egui/src/i18n/ar.rs +++ b/crates/ui-egui/src/i18n/ar.rs @@ -30,6 +30,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "قص"), ("Copy", "نسخ"), ("Paste", "لصق"), + ("New Section", "قسم جديد"), ("Reverse Path", "عكس المسار"), ("Set Anchor", "تعيين نقطة الارتساء"), ("Clear", "مسح"), diff --git a/crates/ui-egui/src/i18n/it.rs b/crates/ui-egui/src/i18n/it.rs index aa8963f2c..f96181b3b 100644 --- a/crates/ui-egui/src/i18n/it.rs +++ b/crates/ui-egui/src/i18n/it.rs @@ -28,6 +28,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "Taglia"), ("Copy", "Copia"), ("Paste", "Incolla"), + ("New Section", "Nuova sezione"), ("Reverse Path", "Inverti tracciato"), ("Set Anchor", "Imposta ancoraggio"), ("Clear", "Cancella"), diff --git a/crates/ui-egui/src/i18n/ja.rs b/crates/ui-egui/src/i18n/ja.rs index 642b4a4c0..fdfcf0534 100644 --- a/crates/ui-egui/src/i18n/ja.rs +++ b/crates/ui-egui/src/i18n/ja.rs @@ -30,6 +30,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "カット"), ("Copy", "コピー"), ("Paste", "ペースト"), + ("New Section", "新規セクション"), ("Reverse Path", "パスの方向反転"), ("Set Anchor", "アンカーポイントを設定"), ("Clear", "消去"), diff --git a/crates/ui-egui/src/i18n/pt_br.rs b/crates/ui-egui/src/i18n/pt_br.rs index 982dfb20d..f144bbf2a 100644 --- a/crates/ui-egui/src/i18n/pt_br.rs +++ b/crates/ui-egui/src/i18n/pt_br.rs @@ -31,6 +31,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "Recortar"), ("Copy", "Copiar"), ("Paste", "Colar"), + ("New Section", "Nova seção"), ("Reverse Path", "Inverter demarcador"), ("Set Anchor", "Definir ponto de ancoragem"), ("Clear", "Limpar"), diff --git a/crates/ui-egui/src/i18n/uk.rs b/crates/ui-egui/src/i18n/uk.rs index f3bc8fb35..a133bd753 100644 --- a/crates/ui-egui/src/i18n/uk.rs +++ b/crates/ui-egui/src/i18n/uk.rs @@ -31,6 +31,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "Вирізати"), ("Copy", "Копіювати"), ("Paste", "Вставити"), + ("New Section", "Новий розділ"), ("Reverse Path", "Розвернути контур"), ("Set Anchor", "Задати прив’язку"), ("Clear", "Очистити"), diff --git a/crates/ui-egui/src/section_options.rs b/crates/ui-egui/src/section_options.rs index 899b9fbed..428f60828 100644 --- a/crates/ui-egui/src/section_options.rs +++ b/crates/ui-egui/src/section_options.rs @@ -36,13 +36,17 @@ pub(crate) fn open(app: &mut DesignApp) { let doc = &st.doc; let page = current.min(doc.page_count().saturating_sub(1)); let own = doc.sections.iter().find(|s| s.start == page); - // A new section starts from the one the page is in. + // A new section starts from the one the page is in. As in InDesign, the window opened on a + // page that doesn't start a section is New Section: Start Section checked, Start Page + // Numbering at offering 1, Automatic Page Numbering chosen. let base = own.or_else(|| doc.section_of(page)); + let new = page != 0 && own.is_none(); let fields = json!({ "page": page + 1, - "startSection": page == 0 || own.is_some(), + "newSection": new, + "startSection": true, "automatic": own.is_none_or(|s| s.start_number.is_none()), - "startNumber": doc.page_number(page).to_string(), + "startNumber": if new { "1".to_string() } else { doc.page_number(page).to_string() }, "prefix": base.map(|s| s.prefix.clone()).unwrap_or_default(), "style": base.map(|s| s.style).unwrap_or_default(), "marker": base.map(|s| s.marker.clone()).unwrap_or_default(), @@ -160,7 +164,12 @@ pub(crate) fn body(app: &DesignApp, ui: &mut Ui, d: &mut Dialog) { if ui.radio(!automatic, crate::rtl::widget(ui, tr("Start Page Numbering at:"))).clicked() { d.fields.insert("automatic".into(), json!(false)); } - ui.add_enabled_ui(!automatic, |ui| text(ui, d, "startNumber", FIELD_W)); + // As in InDesign, the number stays editable; typing one chooses Start Page Numbering at. + let mut n = d.s("startNumber"); + if ui.add(egui::TextEdit::singleline(&mut n).desired_width(FIELD_W)).changed() { + d.fields.insert("startNumber".into(), Value::String(n)); + d.fields.insert("automatic".into(), json!(false)); + } }); ui.add_space(4.0); group(ui, rtl, tr("Page Numbering"), |ui| { @@ -200,7 +209,12 @@ pub(crate) fn body(app: &DesignApp, ui: &mut Ui, d: &mut Dialog) { } }); if value == ChapterSource::UserDefined { - ui.add_enabled_ui(source == value, |ui| text(ui, d, "chapterStart", FIELD_W)); + // Editable whichever is chosen, as in InDesign; typing a number chooses it. + let mut n = d.s("chapterStart"); + if ui.add(egui::TextEdit::singleline(&mut n).desired_width(FIELD_W)).changed() { + d.fields.insert("chapterStart".into(), Value::String(n)); + d.fields.insert("chapterSource".into(), json!(value)); + } } }); } From d70093244692d55e57cecbb46dddf9835dfb5162 Mon Sep 17 00:00:00 2001 From: moppeeer Date: Wed, 14 Oct 2026 14:07:17 +0200 Subject: [PATCH 2/4] New Section test: start page 3 at 5, an odd number, so it holds with #889's facing-page sides Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/dialogs.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index 26c339e72..a289d4005 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -4179,7 +4179,8 @@ mod tests { } let labels = dialog_frame(&mut app, &ctx, screen, vec![]); visible_label(&labels, screen, "New Section"); - // Typing a number in the field chooses Start Page Numbering at; OK: page 3 is page 2. + // Typing a number in the field chooses Start Page Numbering at; OK: page 3 is page 5 (odd, + // so a facing page stays on the right). let field = visible_label(&labels, screen, "1"); click_dialog(&mut app, &ctx, screen, field.center()); dialog_frame( @@ -4189,18 +4190,18 @@ mod tests { vec![ egui::Event::Key { key: egui::Key::End, physical_key: None, pressed: true, repeat: false, modifiers: egui::Modifiers::NONE }, egui::Event::Key { key: egui::Key::Backspace, physical_key: None, pressed: true, repeat: false, modifiers: egui::Modifiers::NONE }, - egui::Event::Text("2".into()), + egui::Event::Text("5".into()), ], ); assert!(!app.ui.dialog.as_ref().unwrap().b("automatic"), "typing chose Start Page Numbering at"); let labels = dialog_frame(&mut app, &ctx, screen, vec![]); click_dialog(&mut app, &ctx, screen, visible_label(&labels, screen, "OK").center()); assert!(app.ui.dialog.is_none()); - assert_eq!(app.session.active().unwrap().doc.page_name(2), "2"); + assert_eq!(app.session.active().unwrap().doc.page_name(2), "5"); // Opened again on that page, it is Numbering & Section Options, showing the section. crate::menus::activate(&mut app, "layout.section", &Value::Null); let d = app.ui.dialog.as_ref().unwrap(); - assert_eq!((d.b("newSection"), d.b("startSection"), d.b("automatic"), d.s("startNumber")), (false, true, false, "2".into())); + assert_eq!((d.b("newSection"), d.b("startSection"), d.b("automatic"), d.s("startNumber")), (false, true, false, "5".into())); let labels = (0..4).map(|_| dialog_frame(&mut app, &ctx, screen, vec![])).last().unwrap(); visible_label(&labels, screen, "Numbering & Section Options"); assert!(!labels.iter().any(|(t, _, _)| t == "New Section")); From 976f0d63ba609f33c6e1392d12bb427bbbb8751a Mon Sep 17 00:00:00 2001 From: moppeeer Date: Sun, 25 Oct 2026 17:22:39 +0100 Subject: [PATCH 3/4] New Section: the dialog's title in every interface language Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/i18n.rs | 1 + crates/ui-egui/src/i18n/ar.rs | 1 + crates/ui-egui/src/i18n/cs.rs | 1 + crates/ui-egui/src/i18n/de.rs | 1 + crates/ui-egui/src/i18n/it.rs | 1 + crates/ui-egui/src/i18n/ja.rs | 1 + crates/ui-egui/src/i18n/ka.rs | 1 + crates/ui-egui/src/i18n/pl.rs | 1 + crates/ui-egui/src/i18n/pt_br.rs | 1 + crates/ui-egui/src/i18n/ru.rs | 1 + crates/ui-egui/src/i18n/uk.rs | 1 + 11 files changed, 11 insertions(+) diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index e74cc0f73..b408c586a 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -1075,6 +1075,7 @@ const TABLE: &[(&str, [&str; 5])] = &[ ], ), ("Load Styles", ["Formate laden", "Charger les styles", "Cargar estilos", "スタイルを読み込み", "加载样式"]), + ("New Section", ["Neuer Abschnitt", "Nouvelle section", "Nueva sección", "新規セクション", "新建章节"]), ("Restart Numbering", ["Nummerierung neu beginnen", "Recommencer la numérotation", "Reiniciar numeración", "番号の振り直し", "重新开始编号"]), ("Continue Numbering", ["Nummerierung fortführen", "Poursuivre la numérotation", "Continuar numeración", "番号の継続", "继续编号"]), ("Keep Options…", ["Umbruchoptionen …", "Options d'enchaînement…", "Opciones de separación…", "段落分離禁止オプション…", "保持选项…"]), diff --git a/crates/ui-egui/src/i18n/ar.rs b/crates/ui-egui/src/i18n/ar.rs index 49fd3046e..bf6a50fa6 100644 --- a/crates/ui-egui/src/i18n/ar.rs +++ b/crates/ui-egui/src/i18n/ar.rs @@ -30,6 +30,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "قص"), ("Copy", "نسخ"), ("Paste", "لصق"), + ("New Section", "قسم جديد"), ("Restart Numbering", "إعادة الترقيم"), ("Continue Numbering", "متابعة الترقيم"), ("Keep Options…", "خيارات الإبقاء…"), diff --git a/crates/ui-egui/src/i18n/cs.rs b/crates/ui-egui/src/i18n/cs.rs index 6f47fdaff..a6442ab94 100644 --- a/crates/ui-egui/src/i18n/cs.rs +++ b/crates/ui-egui/src/i18n/cs.rs @@ -31,6 +31,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "Vyjmout"), ("Copy", "Kopírovat"), ("Paste", "Vložit"), + ("New Section", "Nový oddíl"), ("Restart Numbering", "Začít číslování znovu"), ("Continue Numbering", "Pokračovat v číslování"), ("Keep Options…", "Volby zachování…"), diff --git a/crates/ui-egui/src/i18n/de.rs b/crates/ui-egui/src/i18n/de.rs index 97b48316b..53dd46e7a 100644 --- a/crates/ui-egui/src/i18n/de.rs +++ b/crates/ui-egui/src/i18n/de.rs @@ -31,6 +31,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "Ausschneiden"), ("Copy", "Kopieren"), ("Paste", "Einfügen"), + ("New Section", "Neuer Abschnitt"), ("Restart Numbering", "Nummerierung neu beginnen"), ("Continue Numbering", "Nummerierung fortführen"), ("Keep Options…", "Umbruchoptionen …"), diff --git a/crates/ui-egui/src/i18n/it.rs b/crates/ui-egui/src/i18n/it.rs index 4a3656699..3e806899b 100644 --- a/crates/ui-egui/src/i18n/it.rs +++ b/crates/ui-egui/src/i18n/it.rs @@ -28,6 +28,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "Taglia"), ("Copy", "Copia"), ("Paste", "Incolla"), + ("New Section", "Nuova sezione"), ("Restart Numbering", "Riavvia numerazione"), ("Continue Numbering", "Continua numerazione"), ("Keep Options…", "Opzioni di separazione…"), diff --git a/crates/ui-egui/src/i18n/ja.rs b/crates/ui-egui/src/i18n/ja.rs index 6180858af..302aa8b11 100644 --- a/crates/ui-egui/src/i18n/ja.rs +++ b/crates/ui-egui/src/i18n/ja.rs @@ -30,6 +30,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "カット"), ("Copy", "コピー"), ("Paste", "ペースト"), + ("New Section", "新規セクション"), ("Restart Numbering", "番号の振り直し"), ("Continue Numbering", "番号の継続"), ("Keep Options…", "段落分離禁止オプション…"), diff --git a/crates/ui-egui/src/i18n/ka.rs b/crates/ui-egui/src/i18n/ka.rs index 4767b6c30..6bd45a2db 100644 --- a/crates/ui-egui/src/i18n/ka.rs +++ b/crates/ui-egui/src/i18n/ka.rs @@ -31,6 +31,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "ამოჭრა"), ("Copy", "კოპირება"), ("Paste", "ჩასმა"), + ("New Section", "ახალი სექცია"), ("Restart Numbering", "ნუმერაციის თავიდან დაწყება"), ("Continue Numbering", "ნუმერაციის გაგრძელება"), ("Keep Options…", "შენარჩუნების პარამეტრები…"), diff --git a/crates/ui-egui/src/i18n/pl.rs b/crates/ui-egui/src/i18n/pl.rs index bf0c7600b..d6a5f296c 100644 --- a/crates/ui-egui/src/i18n/pl.rs +++ b/crates/ui-egui/src/i18n/pl.rs @@ -29,6 +29,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "Wytnij"), ("Copy", "Kopiuj"), ("Paste", "Wklej"), + ("New Section", "Nowa sekcja"), ("Restart Numbering", "Zacznij numerowanie od nowa"), ("Continue Numbering", "Kontynuuj numerowanie"), ("Keep Options…", "Opcje utrzymywania…"), diff --git a/crates/ui-egui/src/i18n/pt_br.rs b/crates/ui-egui/src/i18n/pt_br.rs index 59c4f2e23..133ca9f98 100644 --- a/crates/ui-egui/src/i18n/pt_br.rs +++ b/crates/ui-egui/src/i18n/pt_br.rs @@ -31,6 +31,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "Recortar"), ("Copy", "Copiar"), ("Paste", "Colar"), + ("New Section", "Nova seção"), ("Restart Numbering", "Reiniciar numeração"), ("Continue Numbering", "Continuar numeração"), ("Keep Options…", "Opções de manutenção…"), diff --git a/crates/ui-egui/src/i18n/ru.rs b/crates/ui-egui/src/i18n/ru.rs index 412faea7e..abd94d8c1 100644 --- a/crates/ui-egui/src/i18n/ru.rs +++ b/crates/ui-egui/src/i18n/ru.rs @@ -33,6 +33,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "Вырезать"), ("Copy", "Копировать"), ("Paste", "Вставить"), + ("New Section", "Новый раздел"), ("Restart Numbering", "Начать нумерацию заново"), ("Continue Numbering", "Продолжить нумерацию"), ("Keep Options…", "Параметры удержания…"), diff --git a/crates/ui-egui/src/i18n/uk.rs b/crates/ui-egui/src/i18n/uk.rs index 62c594fd1..77b456a1a 100644 --- a/crates/ui-egui/src/i18n/uk.rs +++ b/crates/ui-egui/src/i18n/uk.rs @@ -29,6 +29,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[ ("Cut", "Вирізати"), ("Copy", "Копіювати"), ("Paste", "Вставити"), + ("New Section", "Новий розділ"), ("Restart Numbering", "Почати нумерацію знову"), ("Continue Numbering", "Продовжити нумерацію"), ("Keep Options…", "Параметри утримання…"), From 3959cafd12e5cd965a73367f06c23b615faaa25b Mon Sep 17 00:00:00 2001 From: moppeeer Date: Thu, 29 Oct 2026 15:26:16 +0100 Subject: [PATCH 4/4] Tests: the branch's dialog tests beside main's dialog test helpers (dialogs/tests.rs) Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/dialogs.rs | 49 ----------------------------- crates/ui-egui/src/dialogs/tests.rs | 49 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index 4801278db..ef6869f95 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -186,55 +186,6 @@ fn color_theme(app: &DesignApp, ui: &mut egui::Ui, d: &mut Dialog) { mod color_theme_tests { use super::*; - #[test] - fn on_a_page_without_a_section_the_window_is_new_section() { - // Chris Nielsen's "Numbering Page Sections" (4:43): page 3 selected, the Pages panel menu's - // Numbering & Section Options opens New Section with Start Section on and 1 offered. - let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default()); - app.run("file.new", json!({"pages": 6, "facingPages": true})).unwrap(); - app.canvas_rect = Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1000.0, 800.0))); - crate::canvas::go_to_page(&mut app, 2); - crate::menus::activate(&mut app, "layout.section", &Value::Null); - let d = app.ui.dialog.as_ref().unwrap(); - assert_eq!( - (d.n("page"), d.b("newSection"), d.b("startSection"), d.b("automatic"), d.s("startNumber")), - (Some(3.0), true, true, true, "1".into()) - ); - let ctx = dialog_context("en"); - let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1178.0, 814.0)); - for _ in 0..3 { - dialog_frame(&mut app, &ctx, screen, vec![]); - } - let labels = dialog_frame(&mut app, &ctx, screen, vec![]); - visible_label(&labels, screen, "New Section"); - // Typing a number in the field chooses Start Page Numbering at; OK: page 3 is page 5 (odd, - // so a facing page stays on the right). - let field = visible_label(&labels, screen, "1"); - click_dialog(&mut app, &ctx, screen, field.center()); - dialog_frame( - &mut app, - &ctx, - screen, - vec![ - egui::Event::Key { key: egui::Key::End, physical_key: None, pressed: true, repeat: false, modifiers: egui::Modifiers::NONE }, - egui::Event::Key { key: egui::Key::Backspace, physical_key: None, pressed: true, repeat: false, modifiers: egui::Modifiers::NONE }, - egui::Event::Text("5".into()), - ], - ); - assert!(!app.ui.dialog.as_ref().unwrap().b("automatic"), "typing chose Start Page Numbering at"); - let labels = dialog_frame(&mut app, &ctx, screen, vec![]); - click_dialog(&mut app, &ctx, screen, visible_label(&labels, screen, "OK").center()); - assert!(app.ui.dialog.is_none()); - assert_eq!(app.session.active().unwrap().doc.page_name(2), "5"); - // Opened again on that page, it is Numbering & Section Options, showing the section. - crate::menus::activate(&mut app, "layout.section", &Value::Null); - let d = app.ui.dialog.as_ref().unwrap(); - assert_eq!((d.b("newSection"), d.b("startSection"), d.b("automatic"), d.s("startNumber")), (false, true, false, "5".into())); - let labels = (0..4).map(|_| dialog_frame(&mut app, &ctx, screen, vec![])).last().unwrap(); - visible_label(&labels, screen, "Numbering & Section Options"); - assert!(!labels.iter().any(|(t, _, _)| t == "New Section")); - } - #[test] fn color_theme_cancel_is_read_only_and_confirm_adds_the_preview() { let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default()); diff --git a/crates/ui-egui/src/dialogs/tests.rs b/crates/ui-egui/src/dialogs/tests.rs index 84cbf3bea..23bc23ef9 100644 --- a/crates/ui-egui/src/dialogs/tests.rs +++ b/crates/ui-egui/src/dialogs/tests.rs @@ -3521,3 +3521,52 @@ fn character_style_options_set_the_digits() { assert_eq!(ctl(&mut app, "ui.dialog.confirm", json!({}))["ok"], true); assert_eq!(char_style(&app, "Emphasis").unwrap().chars.digits, Some(designcraft_doc::Digits::Farsi)); } + +#[test] +fn on_a_page_without_a_section_the_window_is_new_section() { + // Chris Nielsen's "Numbering Page Sections" (4:43): page 3 selected, the Pages panel menu's + // Numbering & Section Options opens New Section with Start Section on and 1 offered. + let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default()); + app.run("file.new", json!({"pages": 6, "facingPages": true})).unwrap(); + app.canvas_rect = Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1000.0, 800.0))); + crate::canvas::go_to_page(&mut app, 2); + crate::menus::activate(&mut app, "layout.section", &Value::Null); + let d = app.ui.dialog.as_ref().unwrap(); + assert_eq!( + (d.n("page"), d.b("newSection"), d.b("startSection"), d.b("automatic"), d.s("startNumber")), + (Some(3.0), true, true, true, "1".into()) + ); + let ctx = dialog_context("en"); + let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1178.0, 814.0)); + for _ in 0..3 { + dialog_frame(&mut app, &ctx, screen, vec![]); + } + let labels = dialog_frame(&mut app, &ctx, screen, vec![]); + visible_label(&labels, screen, "New Section"); + // Typing a number in the field chooses Start Page Numbering at; OK: page 3 is page 5 (odd, + // so a facing page stays on the right). + let field = visible_label(&labels, screen, "1"); + click_dialog(&mut app, &ctx, screen, field.center()); + dialog_frame( + &mut app, + &ctx, + screen, + vec![ + egui::Event::Key { key: egui::Key::End, physical_key: None, pressed: true, repeat: false, modifiers: egui::Modifiers::NONE }, + egui::Event::Key { key: egui::Key::Backspace, physical_key: None, pressed: true, repeat: false, modifiers: egui::Modifiers::NONE }, + egui::Event::Text("5".into()), + ], + ); + assert!(!app.ui.dialog.as_ref().unwrap().b("automatic"), "typing chose Start Page Numbering at"); + let labels = dialog_frame(&mut app, &ctx, screen, vec![]); + click_dialog(&mut app, &ctx, screen, visible_label(&labels, screen, "OK").center()); + assert!(app.ui.dialog.is_none()); + assert_eq!(app.session.active().unwrap().doc.page_name(2), "5"); + // Opened again on that page, it is Numbering & Section Options, showing the section. + crate::menus::activate(&mut app, "layout.section", &Value::Null); + let d = app.ui.dialog.as_ref().unwrap(); + assert_eq!((d.b("newSection"), d.b("startSection"), d.b("automatic"), d.s("startNumber")), (false, true, false, "5".into())); + let labels = (0..4).map(|_| dialog_frame(&mut app, &ctx, screen, vec![])).last().unwrap(); + visible_label(&labels, screen, "Numbering & Section Options"); + assert!(!labels.iter().any(|(t, _, _)| t == "New Section")); +}