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 <noreply@anthropic.com>
This commit is contained in:
moppeeerandClaude Opus 5.5 committed 2026-10-14 12:32:25 +02:00
1 parent 87bfdb7fe2
commit be0fbb75c0
9 files changed
+76 -5

No files matched your search

+1
View File
@@ -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.
+50
View File
@@ -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());
+1
View File
@@ -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", "消去", "清除"]),
+1
View File
@@ -30,6 +30,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[
("Cut", "قص"),
("Copy", "نسخ"),
("Paste", "لصق"),
("New Section", "قسم جديد"),
("Reverse Path", "عكس المسار"),
("Set Anchor", "تعيين نقطة الارتساء"),
("Clear", "مسح"),
+1
View File
@@ -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"),
+1
View File
@@ -30,6 +30,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[
("Cut", "カット"),
("Copy", "コピー"),
("Paste", "ペースト"),
("New Section", "新規セクション"),
("Reverse Path", "パスの方向反転"),
("Set Anchor", "アンカーポイントを設定"),
("Clear", "消去"),
+1
View File
@@ -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"),
+1
View File
@@ -31,6 +31,7 @@ pub(super) const TABLE: &[(&str, &str)] = &[
("Cut", "Вирізати"),
("Copy", "Копіювати"),
("Paste", "Вставити"),
("New Section", "Новий розділ"),
("Reverse Path", "Розвернути контур"),
("Set Anchor", "Задати прив’язку"),
("Clear", "Очистити"),
+19 -5
View File
@@ -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));
}
}
});
}