mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-24 10:08:00 -06:00
Merge PR #4211: feat(ui): add 'Split document...' to page thumbnail context menus
This commit is contained in:
@@ -665,6 +665,8 @@ pub enum ViewAction {
|
||||
Save,
|
||||
Extract,
|
||||
Split,
|
||||
/// Split before each selected page: opens Split on that mode.
|
||||
SplitBeforeSelected,
|
||||
/// Copy the selected pages (Cut also deletes them).
|
||||
CopyPages {
|
||||
cut: bool,
|
||||
@@ -5489,6 +5491,10 @@ fn organize_grid(view: &mut DocView, info: &DocInfo, editable: bool, dirty: bool
|
||||
view.pending_action = Some(ViewAction::Extract);
|
||||
ui.close();
|
||||
}
|
||||
if ui.button(tl!("Split document…")).clicked() {
|
||||
view.pending_action = Some(ViewAction::SplitBeforeSelected);
|
||||
ui.close();
|
||||
}
|
||||
if ui.add_enabled(editable, egui::Button::new(tl!("Replace pages…"))).clicked() {
|
||||
view.pending_action = Some(ViewAction::ReplacePages);
|
||||
ui.close();
|
||||
|
||||
@@ -265,6 +265,10 @@ impl PdfCraftApp {
|
||||
}
|
||||
Some(crate::canvas::ViewAction::Extract) => self.open_extract_dialog(),
|
||||
Some(crate::canvas::ViewAction::Split) => self.dialog = Some(crate::Dialog::Split),
|
||||
Some(crate::canvas::ViewAction::SplitBeforeSelected) => {
|
||||
self.split_draft.mode = crate::SplitMode::Selection;
|
||||
self.dialog = Some(crate::Dialog::Split);
|
||||
}
|
||||
Some(crate::canvas::ViewAction::CopyPages { cut }) => self.copy_pages(cut),
|
||||
Some(crate::canvas::ViewAction::PastePages) => self.paste_pages(),
|
||||
Some(crate::canvas::ViewAction::EnhanceCamera) => self.enhance_camera(i),
|
||||
|
||||
@@ -1410,6 +1410,13 @@ fn pages(ui: &mut egui::Ui, t: &Tokens, info: &DocInfo, view: &mut crate::DocVie
|
||||
view.pending_action = Some(crate::canvas::ViewAction::Extract);
|
||||
ui.close();
|
||||
}
|
||||
if ui.button(tl!("Split document…")).clicked() {
|
||||
if view.selected.is_empty() {
|
||||
view.select_pages(&[i]);
|
||||
}
|
||||
view.pending_action = Some(crate::canvas::ViewAction::SplitBeforeSelected);
|
||||
ui.close();
|
||||
}
|
||||
if ui.add_enabled(editable, egui::Button::new(tl!("Replace pages…"))).clicked() {
|
||||
view.pending_action = Some(crate::canvas::ViewAction::ReplacePages);
|
||||
ui.close();
|
||||
|
||||
@@ -2241,6 +2241,88 @@ fn split_before_selected_pages() {
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_from_organize_grid_context_menu() {
|
||||
let dir = temp_path("split-org-ctx");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let mut h = organize(4);
|
||||
h.state_mut().export_dir_override = Some(dir.to_string_lossy().into_owned());
|
||||
h.get_by_label("Page 3").click_button(egui::PointerButton::Secondary);
|
||||
h.run_steps(2);
|
||||
h.get_all_by_label("Split document…").last().unwrap().click();
|
||||
h.run_steps(3);
|
||||
assert_eq!(h.state().dialog, Some(pdfcraft_ui_egui::Dialog::Split));
|
||||
assert_eq!(h.state().split_draft.mode, pdfcraft_ui_egui::SplitMode::Selection);
|
||||
h.get_by_label_contains("Creates 2 files from 4 pages");
|
||||
h.get_all_by_label("Split").last().unwrap().click();
|
||||
h.run_steps(3);
|
||||
let mut names: Vec<String> = std::fs::read_dir(&dir).unwrap().map(|e| e.unwrap().file_name().to_string_lossy().into_owned()).collect();
|
||||
names.sort();
|
||||
assert_eq!(names, ["doc (pages 1-2).pdf", "doc (pages 3-4).pdf"]);
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_from_pages_panel_context_menu() {
|
||||
let mut h = harness(4, |app| app.right = Some(pdfcraft_ui_egui::RightPanel::Pages));
|
||||
let thumb = h.get_all_by_label("Page 3").max_by(|a, b| a.rect().left().total_cmp(&b.rect().left())).unwrap();
|
||||
thumb.click_button(egui::PointerButton::Secondary);
|
||||
h.run_steps(2);
|
||||
h.get_all_by_label("Split document…").last().unwrap().click();
|
||||
h.run_steps(3);
|
||||
assert_eq!(h.state().dialog, Some(pdfcraft_ui_egui::Dialog::Split));
|
||||
assert_eq!(h.state().split_draft.mode, pdfcraft_ui_egui::SplitMode::Selection);
|
||||
h.get_by_label_contains("Creates 2 files from 4 pages");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_from_pages_panel_context_menu_on_current_page_without_selection() {
|
||||
let mut h = harness(4, |app| app.right = Some(pdfcraft_ui_egui::RightPanel::Pages));
|
||||
// Navigate to Page 2 (0-indexed 1) so it is current, but keep selected empty
|
||||
h.state_mut().views[0].go_to_page(1);
|
||||
h.state_mut().views[0].selected.clear();
|
||||
h.run_steps(2);
|
||||
assert_eq!(h.state().views[0].current, 1);
|
||||
assert!(h.state().views[0].selected.is_empty());
|
||||
let thumb = h.get_all_by_label("Page 2").max_by(|a, b| a.rect().left().total_cmp(&b.rect().left())).unwrap();
|
||||
thumb.click_button(egui::PointerButton::Secondary);
|
||||
h.run_steps(2);
|
||||
h.get_all_by_label("Split document…").last().unwrap().click();
|
||||
h.run_steps(3);
|
||||
assert_eq!(h.state().dialog, Some(pdfcraft_ui_egui::Dialog::Split));
|
||||
assert_eq!(h.state().split_draft.mode, pdfcraft_ui_egui::SplitMode::Selection);
|
||||
h.get_by_label_contains("Creates 2 files from 4 pages");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_from_pages_panel_context_menu_on_page_one_falls_back_to_pages() {
|
||||
let mut h = harness(4, |app| app.right = Some(pdfcraft_ui_egui::RightPanel::Pages));
|
||||
let thumb = h.get_all_by_label("Page 1").max_by(|a, b| a.rect().left().total_cmp(&b.rect().left())).unwrap();
|
||||
thumb.click_button(egui::PointerButton::Secondary);
|
||||
h.run_steps(2);
|
||||
h.get_all_by_label("Split document…").last().unwrap().click();
|
||||
h.run_steps(3);
|
||||
assert_eq!(h.state().dialog, Some(pdfcraft_ui_egui::Dialog::Split));
|
||||
assert_eq!(h.state().split_draft.mode, pdfcraft_ui_egui::SplitMode::Pages);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_from_pages_panel_context_menu_with_multi_selection() {
|
||||
let mut h = harness(4, |app| app.right = Some(pdfcraft_ui_egui::RightPanel::Pages));
|
||||
// Select pages 2 and 4 (indices 1 and 3)
|
||||
h.state_mut().views[0].select_pages(&[1, 3]);
|
||||
h.run_steps(2);
|
||||
let thumb = h.get_all_by_label("Page 2").max_by(|a, b| a.rect().left().total_cmp(&b.rect().left())).unwrap();
|
||||
thumb.click_button(egui::PointerButton::Secondary);
|
||||
h.run_steps(2);
|
||||
h.get_all_by_label("Split document…").last().unwrap().click();
|
||||
h.run_steps(3);
|
||||
assert_eq!(h.state().dialog, Some(pdfcraft_ui_egui::Dialog::Split));
|
||||
assert_eq!(h.state().split_draft.mode, pdfcraft_ui_egui::SplitMode::Selection);
|
||||
h.get_by_label_contains("Creates 3 files from 4 pages");
|
||||
}
|
||||
|
||||
// ── Encrypted documents ───────────────────────────────────────────────────────────────────────
|
||||
|
||||
fn protected(user: &str, owner: &str, permissions: i32) -> Vec<u8> {
|
||||
|
||||
@@ -1393,7 +1393,7 @@ fn pages_panel_thumbnails_have_a_context_menu() {
|
||||
h.run_steps(2);
|
||||
// The page right-clicked becomes the selection the menu acts on.
|
||||
assert_eq!(h.state().views[0].target_pages(), vec![2]);
|
||||
for item in ["Extract pages", "Cut", "Paste after"] {
|
||||
for item in ["Extract pages", "Split document…", "Cut", "Paste after"] {
|
||||
h.get_by_label(item);
|
||||
}
|
||||
h.get_by_label("Copy").click();
|
||||
|
||||
@@ -1274,8 +1274,8 @@ milestone = "M4"
|
||||
status = "shipped"
|
||||
commands = []
|
||||
tools = []
|
||||
evidence = ["crates/ui-egui/tests/it/editing.rs::dragging_pages_panel_thumbnails_reorders_pages", "crates/ui-egui/tests/it/viewer.rs::pages_panel_thumbnails_have_a_context_menu", "crates/ui-egui/tests/it/editing.rs::deleting_pages_from_the_pages_panel_menu", "crates/ui-egui/tests/it/editing.rs::the_delete_key_deletes_pages_picked_in_the_pages_panel_but_never_every_page"]
|
||||
notes = "Drag the selection (or the page grabbed) to reorder, with a drop bar and edge scrolling; right-click offers Extract pages, Cut, Copy, Paste after and Delete pages (also the Delete key over the panel). Not yet: Rotate, Insert and Page labels in the menu, dragging pages between documents, or dropping files onto the panel."
|
||||
evidence = ["crates/ui-egui/tests/it/editing.rs::dragging_pages_panel_thumbnails_reorders_pages", "crates/ui-egui/tests/it/viewer.rs::pages_panel_thumbnails_have_a_context_menu", "crates/ui-egui/tests/it/editing.rs::split_from_pages_panel_context_menu", "crates/ui-egui/tests/it/editing.rs::deleting_pages_from_the_pages_panel_menu", "crates/ui-egui/tests/it/editing.rs::the_delete_key_deletes_pages_picked_in_the_pages_panel_but_never_every_page"]
|
||||
notes = "Drag the selection (or the page grabbed) to reorder, with a drop bar and edge scrolling; right-click offers Split document…, Extract pages, Cut, Copy, Paste after and Delete pages (also the Delete key over the panel). Not yet: Rotate, Insert and Page labels in the menu, dragging pages between documents, or dropping files onto the panel."
|
||||
|
||||
[[feature]]
|
||||
id = "view.bookmarks-panel"
|
||||
@@ -2920,7 +2920,7 @@ milestone = "M4"
|
||||
status = "shipped"
|
||||
commands = ["page.split"]
|
||||
tools = ["doc_split"]
|
||||
evidence = ["crates/ui-egui/tests/it/editing.rs::split_before_selected_pages"]
|
||||
evidence = ["crates/ui-egui/tests/it/editing.rs::split_before_selected_pages", "crates/ui-egui/tests/it/editing.rs::split_from_organize_grid_context_menu"]
|
||||
notes = ""
|
||||
|
||||
[[feature]]
|
||||
|
||||
Reference in New Issue
Block a user