feat: Neue Seite direkt nach der aktuellen einfügen

„Neue Seite einfügen“ (☰-Menü und Seitenauswahl) legt die leere Seite
direkt hinter der aktuellen an und wechselt dorthin, statt sie ans Ende
zu hängen – etwa zwischen zwei Folien eines PDFs (Issue #3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 08:02:30 +02:00
co-authored by Claude Opus 5.5
parent 438106cafd
commit 9fde59798f
4 changed files with 52 additions and 21 deletions
+1 -1
View File
@@ -55,7 +55,7 @@ Eine latenzoptimierte, hardwarebeschleunigte Inking- und Notizbuch-Anwendung fü
### 📄 Seiten & Hintergründe ### 📄 Seiten & Hintergründe
- **Virtuelles DIN-A4-Raster:** 2100 × 2970 Koordinateneinheiten (10 Einheiten = 1 mm). - **Virtuelles DIN-A4-Raster:** 2100 × 2970 Koordinateneinheiten (10 Einheiten = 1 mm).
- **Hintergrundmuster:** Blanko, Kariert (5 mm), Liniert (7.5 mm), Punktraster (5 mm). - **Hintergrundmuster:** Blanko, Kariert (5 mm), Liniert (7.5 mm), Punktraster (5 mm).
- **Seitenverwaltung:** Vor- und Zurückblättern, neue Seiten anfügen, Seitensprung-Menü. - **Seitenverwaltung:** Vor- und Zurückblättern, neue Seiten anhängen oder direkt nach der aktuellen Seite einfügen (☰-Menü oder Seitenauswahl, z. B. zwischen zwei PDF-Folien), Seitensprung-Menü.
### 💾 Autosave & Persistenz ### 💾 Autosave & Persistenz
- **Asynchroner Autosave-Controller:** Sichert Änderungen im Hintergrund nach 1.5 s Ruhezeit (Debounce), ohne das Zeichnen zu blockieren. - **Asynchroner Autosave-Controller:** Sichert Änderungen im Hintergrund nach 1.5 s Ruhezeit (Debounce), ohne das Zeichnen zu blockieren.
+31
View File
@@ -1149,6 +1149,16 @@ impl Notebook {
self.pages.len() - 1 self.pages.len() - 1
} }
/// Fügt direkt nach der aktuellen Seite eine leere Seite mit dem angegebenen
/// Hintergrund ein (die aktuelle Seite bleibt aktuell) und gibt ihren Index zurück.
pub fn insert_page_after_current(&mut self, background: PageBackground) -> usize {
let id = self.next_page_id;
self.next_page_id += 1;
let index = (self.current_page_index + 1).min(self.pages.len());
self.pages.insert(index, Page::new(id, background));
index
}
/// Durchgehende Ansicht: Reicht ein neuer Strich auf der letzten Seite bis in deren /// Durchgehende Ansicht: Reicht ein neuer Strich auf der letzten Seite bis in deren
/// unteres Fünftel ([`AUTO_APPEND_ZONE`]), wird darunter eine leere Seite mit gleichem /// unteres Fünftel ([`AUTO_APPEND_ZONE`]), wird darunter eine leere Seite mit gleichem
/// Hintergrund angehängt. `true`, wenn eine Seite angehängt wurde. /// Hintergrund angehängt. `true`, wenn eine Seite angehängt wurde.
@@ -1809,6 +1819,27 @@ mod tests {
)); ));
} }
#[test]
fn test_insert_page_after_current() {
let mut nb = Notebook::new();
nb.add_page(PageBackground::Lined);
nb.add_page(PageBackground::Lined);
let ids: Vec<u64> = nb.pages.iter().map(|p| p.id).collect();
nb.current_page_index = 1;
nb.pages[1].background = PageBackground::DotGrid;
let index = nb.insert_page_after_current(PageBackground::DotGrid);
assert_eq!(index, 2);
assert_eq!(nb.current_page_index, 1);
assert_eq!(nb.pages.len(), 4);
assert_eq!(nb.pages[0].id, ids[0]);
assert_eq!(nb.pages[1].id, ids[1]);
assert_eq!(nb.pages[3].id, ids[2], "alte Seite 3 ist jetzt Seite 4");
let new = &nb.pages[2];
assert!(!ids.contains(&new.id) && new.strokes.is_empty());
assert_eq!(new.background, PageBackground::DotGrid);
assert!(new.pdf_background.is_none());
}
#[test] #[test]
fn test_append_page_when_writing_at_bottom_of_last_page() { fn test_append_page_when_writing_at_bottom_of_last_page() {
let mut nb = Notebook::new(); let mut nb = Notebook::new();
+16 -18
View File
@@ -2800,26 +2800,24 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
} }
UiAction::AddPage => { UiAction::AddPage => {
selection_state.clear(); // Leere Seite direkt nach der aktuellen (in PDFs: nach der Folie,
// auf der man gerade ist) und dorthin wechseln
let bg = notebook.current_page().background; let bg = notebook.current_page().background;
notebook.add_page(bg); let index = notebook.insert_page_after_current(bg);
manifest.current_page_index = notebook.current_page_index;
manifest.next_page_id = notebook.next_page_id;
manifest.pages = notebook
.pages
.iter()
.map(|p| storage::PageMeta {
id: p.id,
background: Some(p.background),
title: p.title.clone(),
})
.collect();
canvas.sync_page(notebook.current_page_mut());
manifest.sync_from_notebook(&notebook); manifest.sync_from_notebook(&notebook);
autosave.mark_manifest_dirty(&manifest); switch_to_page(
autosave.mark_dirty(&notebook, &mut manifest); index,
update_window_title(&window, &notebook, &manifest, ui_state.active_tool, autosave.status()); false,
window.request_redraw(); &mut canvas,
&mut notebook,
&mut manifest,
&mut autosave,
&storage_dir,
&mut selection_state,
&mut ui_state,
&window,
);
ui_state.set_export_message(format!("Seite {} eingefügt", index + 1));
} }
UiAction::LoadAllPages => { UiAction::LoadAllPages => {
// Für die Vorschaubilder der Seitenauswahl; Fehler werden wie beim // Für die Vorschaubilder der Seitenauswahl; Fehler werden wie beim
+4 -2
View File
@@ -2561,7 +2561,8 @@ pub fn render_ui(
} }
}); });
if ui if ui
.add_sized([ui.available_width(), 30.0], egui::Button::new("➕ Neue Seite anhängen")) .add_sized([ui.available_width(), 30.0], egui::Button::new("➕ Neue Seite einfügen"))
.on_hover_text("Leere Seite direkt nach der aktuellen Seite")
.clicked() .clicked()
{ {
actions.push(UiAction::AddPage); actions.push(UiAction::AddPage);
@@ -2883,8 +2884,9 @@ pub fn render_ui(
if ui if ui
.add_sized( .add_sized(
[ui.available_width(), 36.0], [ui.available_width(), 36.0],
egui::Button::new("➕ Neue Seite anhängen"), egui::Button::new("➕ Neue Seite einfügen"),
) )
.on_hover_text("Leere Seite direkt nach der aktuellen Seite")
.clicked() .clicked()
{ {
actions.push(UiAction::AddPage); actions.push(UiAction::AddPage);