ElyPDF: an edit keeps the page drawn until its repaint lands (R2.6)

This commit is contained in:
ZacharyZhang-NY committed 2026-10-09 02:34:32 -04:00
1 parent 5e2cf83da8
commit 9fe28f072b
4 files changed
+56 -2

No files matched your search

+8 -2
View File
@@ -97,9 +97,15 @@ impl PdfView {
self.version += 1;
self.edited += 1;
(self.doc, self.mark) = (fresh.doc, fresh.mark);
self.sizes = sizes(fresh.pages, &fresh.first);
let sizes = sizes(fresh.pages, &fresh.first);
// Same pages: old tiles stay until repainted, so nothing flashes.
if sizes == self.sizes {
self.stale.extend(self.tiles.keys().copied());
} else {
self.retire_tiles();
}
self.sizes = sizes;
self.marks = fresh.marks;
self.retire_tiles();
self.failed.clear();
self.text.clear();
self.asking_text.clear();
+5
View File
@@ -164,6 +164,9 @@ impl PdfView {
let key = TileKey { page: place.page, scale, rotate: self.rotate, dark, tx, ty };
let rect = [tx * TILE, ty * TILE, TILE.min(dw - tx * TILE), TILE.min(dh - ty * TILE)];
seen.insert(key);
if self.stale.contains(&key) && !self.failed.contains(&key) {
wanted.push((key, rect));
}
match self.tiles.get(&key) {
Some(image) => tiles.push(
drawn_tile(image.clone(), place.page, cx.entity())
@@ -190,6 +193,7 @@ impl PdfView {
}
kept
});
self.stale.retain(|key| seen.contains(key));
let pages_shown: Vec<usize> = shown.iter().map(|place| place.page).collect();
self.ask_layers(&pages_shown, cx);
self.ask_notes(pages_shown.iter().copied(), cx);
@@ -295,6 +299,7 @@ impl PdfView {
match painted.map(|bgra| picture((rect[2] as u32, rect[3] as u32), bgra)) {
Ok(Some(image)) => {
log::debug!("pdf: a tile of page {} came", key.page);
view.stale.remove(&key);
if let Some(old) = view.tiles.insert(key, image) {
view.retired.push(old);
}
+39
View File
@@ -185,3 +185,42 @@ fn tiles_beside_the_view_are_neither_asked_nor_kept() {
}));
settle(cx, |app| columns(app).contains(&last) && !columns(app).contains(&0));
}
#[test]
fn an_edit_keeps_the_page_drawn_until_its_repaint_lands() {
let dir = tempfile::tempdir().expect("a temp dir");
let mut cx = TestAppContext::single();
let (viewer, cx) = windowed(&mut cx, dir.path(), &[file(dir.path(), "one.pdf", &[(612.0, 792.0, &["Hello"])])], true);
let view = shown(cx, &viewer);
settle(cx, |app| !view.read(app).tiles.is_empty() && !view.read(app).painting);
let before: Vec<_> = cx.update(|_, app| view.read(app).tiles.values().cloned().collect());
let note = ely_pdf_engine::NewAnnot {
page: 0,
shape: ely_pdf_engine::Shape::Note((100.0, 500.0)),
rgb: None,
contents: "Look".into(),
author: String::new(),
};
// Watches every notify, the edit's own included, before repaints.
let blank = std::rc::Rc::new(std::cell::Cell::new(false));
let seen = blank.clone();
let _watch = cx.update(|_, app| {
app.observe(&view, move |view, app| {
let read = view.read(app);
if read.edited == 1 && read.tiles.is_empty() {
seen.set(true);
}
})
});
cx.update(|_, app| view.update(app, |view, cx| view.annotate("adding a note", vec![note], cx)));
settle(cx, |app| {
let read = view.read(app);
read.edited == 1 && read.stale.is_empty() && !read.painting
});
assert!(!blank.get(), "the page never goes blank");
let after = cx.update(|_, app| view.read(app).tiles.values().cloned().collect::<Vec<_>>());
assert!(
after.iter().all(|image| before.iter().all(|old| !std::sync::Arc::ptr_eq(old, image))),
"every tile was painted again"
);
}
+4
View File
@@ -41,6 +41,8 @@ pub struct PdfView {
pub drawn: bool,
/// Tiles the worker could not draw; not asked again.
pub failed: HashSet<TileKey>,
/// Tiles from before an edit: shown until their repaint lands.
pub stale: HashSet<TileKey>,
pub text: HashMap<usize, Arc<Vec<Char>>>,
pub asking_text: HashSet<usize>,
pub marks: Vec<Bookmark>,
@@ -166,6 +168,7 @@ impl PdfView {
retired: Default::default(),
drawn: false,
failed: HashSet::new(),
stale: HashSet::new(),
text: HashMap::new(),
asking_text: HashSet::new(),
marks: loaded.marks,
@@ -311,6 +314,7 @@ impl PdfView {
/// Every page tile goes; images are freed next render.
pub fn retire_tiles(&mut self) {
self.stale.clear();
for (_, image) in self.tiles.drain() {
self.retired.push(image);
}