Read mode: the rail's Bookmarks and other pane buttons open their panels (#932) (#947)

* Read mode: the rail's Bookmarks and other pane buttons open their panels (#932)

Read mode kept the right rail but never drew the right panel, so its
buttons did nothing. Read mode now hides the tools, not the navigation
panes: the panels open from the rail as in the other workspaces. Entering
Read mode closes the open pane and leaving it reopens it, and a document
opened into Read mode starts with the panes closed unless its Initial View
asks for one (/PageMode /UseOutlines).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M7: a pane closed in Read mode stays closed when leaving it (#932)

Review on #947: opening or closing a pane in Read mode now discards the
pane saved on the way in, so leaving Read mode no longer reopens a pane
the user closed there (and Comments keeps its close preference).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: DWAA1660 <DWAA1660@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DWAA1660
2026-10-11 13:15:26 -04:00
committed by GitHub
co-authored by Claude Opus 5.5 DWAA1660
parent 5d7700ead6
commit faea6d69b4
4 changed files with 142 additions and 4 deletions
+1 -1
View File
@@ -364,7 +364,7 @@ pub fn mode_bar(app: &mut PdfCraftApp, ui: &mut egui::Ui) {
[(Mode::AllTools, "All tools"), (Mode::Read, "Read"), (Mode::Edit, "Edit"), (Mode::Convert, "Convert"), (Mode::Sign, "E-Sign")]
{
if widgets::mode_tab(ui, tl!(label), app.mode == mode).clicked() {
app.select_mode(mode);
app.switch_mode(mode, true);
}
}
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
+1 -1
View File
@@ -242,7 +242,7 @@ impl PdfCraftApp {
None => self.full_screen = on,
}
}
"view.read_mode" => self.mode = if self.mode == Mode::Read { Mode::AllTools } else { Mode::Read },
"view.read_mode" => self.switch_mode(if self.mode == Mode::Read { Mode::AllTools } else { Mode::Read }, false),
"view.theme" => {
let next = if self.theme == ThemeKind::Light { ThemePreference::Dark } else { ThemePreference::Light };
self.set_theme_preference(next);
+47 -2
View File
@@ -408,6 +408,10 @@ pub struct PdfCraftApp {
pub left: LeftPanel,
pub left_open: bool,
pub right: Option<RightPanel>,
/// The right panel that was open when the user switched to Read mode, which starts with the
/// panes closed; leaving Read mode brings it back unless the user opened or closed a pane in
/// Read mode meanwhile.
right_before_read: Option<RightPanel>,
/// The user closed the Comments panel, so picking a comment tool leaves it closed until they
/// open it again (#225). Remembered across restarts.
pub comments_panel_closed: bool,
@@ -730,6 +734,7 @@ impl PdfCraftApp {
pending_panel_reveals: Vec::new(),
left_open: true,
right: None,
right_before_read: None,
comments_panel_closed: false,
quick_tool: QuickTool::Select,
space_hand: None,
@@ -957,7 +962,9 @@ impl PdfCraftApp {
let doc = self.session.get(id).ok_or("the document could not be opened")?;
let pages = doc.info.pages.len();
// Acrobat opens straight to the Comments panel when a document has comments.
if activate && self.right.is_none() {
// Read mode opens with the panes closed; the rail still opens them (#932).
let opening_mode = self.mode_override.unwrap_or(self.default_mode);
if activate && self.right.is_none() && opening_mode != Mode::Read {
self.right = if !doc.info.annotations.is_empty() {
Some(RightPanel::Comments)
} else if !doc.info.outline.is_empty() {
@@ -1421,9 +1428,36 @@ impl PdfCraftApp {
};
}
/// Switches workspace because the user chose to (mode bar, Read mode command). Entering Read
/// mode closes the right panel, as Acrobat's Read mode hides its panes, and leaving it reopens
/// that panel. The rail stays in Read mode, and its buttons open panels there too (#932).
pub(crate) fn switch_mode(&mut self, mode: Mode, select_tools: bool) {
let was_read = self.mode == Mode::Read;
if select_tools {
self.select_mode(mode);
} else {
self.mode = mode;
}
if mode == Mode::Read && !was_read {
self.right_before_read = self.right.take();
} else if mode != Mode::Read && was_read {
let before = self.right_before_read.take();
if self.right.is_none() {
self.right = before;
}
if self.right.is_some() {
self.pending_panel_reveals.push(docking::Panel::Inspector);
}
}
}
/// Opens a right panel, or closes it with `None`, because the user chose to. Closing Comments
/// keeps comment tools from reopening it; opening it again lets them (#225).
/// keeps comment tools from reopening it; opening it again lets them (#225). A choice made in
/// Read mode replaces the pane saved on the way in, so leaving Read mode doesn't undo it.
pub fn choose_right_panel(&mut self, panel: Option<RightPanel>) {
if self.mode == Mode::Read {
self.right_before_read = None;
}
if panel == Some(RightPanel::Comments) {
self.comments_panel_closed = false;
} else if panel.is_none() && self.right == Some(RightPanel::Comments) {
@@ -2165,6 +2199,17 @@ impl eframe::App for PdfCraftApp {
chrome::mode_bar(self, ui);
if self.active.is_some() {
chrome::right_rail(self, ui);
// Read mode hides the tools, not the navigation panes: the rail stays visible
// there, so its buttons (Bookmarks, Page thumbnails, ...) must open their panels.
// Outside Read mode the docking workspace draws the inspector.
if self.mode == Mode::Read && self.right.is_some() {
egui::Panel::right("read_right_panel")
.resizable(true)
.default_size(330.0)
.size_range(260.0..=520.0)
.frame(egui::Frame::NONE)
.show(ui, |ui| panels::right_panel(self, ui));
}
}
let t = theme::Tokens::get(&ctx);
egui::CentralPanel::default().frame(egui::Frame::NONE.fill(t.pasteboard)).show(ui, |ui| {
+93
View File
@@ -114,6 +114,99 @@ fn opening_a_pdf_shows_comments_and_bookmarks() {
h.get_by_label("Beta section");
}
/// Read mode hides the tools, not the navigation panes: the rail's Bookmarks button opens the
/// bookmarks when Read is the default workspace (#932), and the left tool pane stays hidden.
#[test]
fn read_mode_rail_buttons_open_their_panels() {
use pdfcraft_ui_egui::{Mode, RightPanel};
let mut h = harness(|app| {
app.default_mode = Mode::Read;
app.open_bytes("fixture.pdf", None, FIXTURE.to_vec()).expect("fixture opens");
});
assert_eq!(h.state().mode, Mode::Read);
assert_eq!(h.state().right, None, "Read mode opens with the panes closed");
assert!(h.query_by_label("Alpha section").is_none());
h.get_by_label("Bookmarks").click();
h.run_steps(3);
assert_eq!(h.state().right, Some(RightPanel::Bookmarks));
h.get_by_label("Alpha section");
h.get_by_label("Beta section");
// The same button closes them again (the open panel's title is also "Bookmarks").
h.get_by_role_and_label(egui::accesskit::Role::Button, "Bookmarks").click();
h.run_steps(3);
assert_eq!(h.state().right, None);
assert!(h.query_by_label("Alpha section").is_none());
// A document that asks for its outline (/PageMode /UseOutlines) shows it in Read mode too.
let pdf = String::from_utf8(FIXTURE.to_vec()).unwrap().replace("/Outlines 6 0 R", "/Outlines 6 0 R /PageMode /UseOutlines");
h.state_mut().open_bytes("outline.pdf", None, pdf.into_bytes()).unwrap();
h.run_steps(3);
assert_eq!(h.state().mode, Mode::Read);
h.get_by_label("Alpha section");
}
/// Switching to Read mode closes the open pane, and switching back reopens it.
#[test]
fn read_mode_closes_the_panes_and_leaving_it_brings_them_back() {
use pdfcraft_ui_egui::{Mode, RightPanel};
let mut h = harness(|app| app.open_bytes("fixture.pdf", None, FIXTURE.to_vec()).expect("fixture opens"));
assert_eq!(h.state().right, Some(RightPanel::Comments));
h.get_by_label("Read").click();
h.run_steps(3);
assert_eq!(h.state().mode, Mode::Read);
assert_eq!(h.state().right, None);
assert!(h.query_by_label_contains("Check the numbers").is_none(), "the Comments panel is hidden");
h.state_mut().run_command("view.read_mode");
h.run_steps(3);
assert_eq!(h.state().mode, Mode::AllTools);
assert_eq!(h.state().right, Some(RightPanel::Comments));
// A pane opened in Read mode stays open on the way out.
h.state_mut().run_command("view.read_mode");
h.run_steps(2);
h.get_by_label("Page thumbnails").click();
h.run_steps(2);
h.get_by_label("All tools").click();
h.run_steps(3);
assert_eq!(h.state().right, Some(RightPanel::Pages));
}
/// A pane closed in Read mode stays closed on the way out: leaving Read mode doesn't bring back the
/// pane saved on the way in, and closing Comments keeps its close preference.
#[test]
fn a_pane_closed_in_read_mode_stays_closed_when_leaving_it() {
use pdfcraft_ui_egui::{Mode, RightPanel};
let mut h = harness(|app| app.open_bytes("fixture.pdf", None, FIXTURE.to_vec()).expect("fixture opens"));
assert_eq!(h.state().right, Some(RightPanel::Comments));
h.get_by_label("Read").click();
h.run_steps(3);
assert_eq!(h.state().right, None);
// Open Comments from the rail, then close it again.
h.get_by_role_and_label(egui::accesskit::Role::Button, "Comments").click();
h.run_steps(3);
assert_eq!(h.state().right, Some(RightPanel::Comments));
h.get_by_role_and_label(egui::accesskit::Role::Button, "Comments").click();
h.run_steps(3);
assert_eq!(h.state().right, None);
assert!(h.state().comments_panel_closed);
// Leave Read mode through the toolbar.
h.get_by_label("All tools").click();
h.run_steps(3);
assert_eq!(h.state().mode, Mode::AllTools);
assert_eq!(h.state().right, None, "the pane closed in Read mode stays closed");
assert!(h.state().comments_panel_closed);
// The same through the Read mode command.
h.state_mut().choose_right_panel(Some(RightPanel::Comments));
h.state_mut().run_command("view.read_mode");
h.run_steps(2);
assert_eq!(h.state().mode, Mode::Read);
h.state_mut().choose_right_panel(Some(RightPanel::Comments));
h.state_mut().choose_right_panel(None);
h.state_mut().run_command("view.read_mode");
h.run_steps(3);
assert_ne!(h.state().mode, Mode::Read);
assert_eq!(h.state().right, None);
assert!(h.state().comments_panel_closed);
}
/// A right-to-left file name opens, lays out and paints; the tab's accessible name keeps the
/// logical text (only the painted label is put in visual order).
#[test]