ElyPDF: page pictures stay through an edit until they repaint (P-02)
This commit is contained in:
8 files changed
+60
-9
No files matched your search
@@ -115,9 +115,14 @@ impl PdfView {
|
||||
// A page may now stand where another stood.
|
||||
self.retire_tiles();
|
||||
self.sizes = measured;
|
||||
self.thumbs_stale.clear();
|
||||
for (_, image) in self.thumbs.drain() {
|
||||
self.retired.push(image);
|
||||
}
|
||||
} else {
|
||||
// Same pages: old tiles stay under them until repainted.
|
||||
// Same pages: old pictures stay until repainted.
|
||||
self.keep_all();
|
||||
self.thumbs_stale.extend(self.thumbs.keys().copied());
|
||||
let read = fresh.first.len();
|
||||
self.sizes[..read].copy_from_slice(&measured[..read]);
|
||||
}
|
||||
@@ -125,9 +130,6 @@ impl PdfView {
|
||||
self.failed.clear();
|
||||
self.text.clear();
|
||||
self.asking_text.clear();
|
||||
for (_, image) in self.thumbs.drain() {
|
||||
self.retired.push(image);
|
||||
}
|
||||
self.thumbs_failed.clear();
|
||||
// Indices shift with any edit; aims start over.
|
||||
(self.layers, self.aim, self.drag) = (Default::default(), None, None);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Page pictures, and bookmarks if any (PDF-01, PDF-02).
|
||||
|
||||
use std::{collections::BTreeSet, ops::Range};
|
||||
use std::{collections::BTreeSet, ops::Range, sync::Arc};
|
||||
|
||||
use ely_gpui_component::{
|
||||
buttons::SegmentedControl,
|
||||
@@ -9,7 +9,7 @@ use ely_gpui_component::{
|
||||
theme::{ActiveTheme as _, ControlSize},
|
||||
};
|
||||
use gpui::{
|
||||
Action, App, Context, Entity, InteractiveElement as _, IntoElement, ParentElement as _, Render, Role,
|
||||
Action, App, Context, Entity, InteractiveElement as _, IntoElement, ParentElement as _, Render, RenderImage, Role,
|
||||
StatefulInteractiveElement as _, Styled as _, WeakEntity, Window, div, img, prelude::FluentBuilder as _, px,
|
||||
uniform_list,
|
||||
};
|
||||
@@ -104,7 +104,8 @@ impl PdfView {
|
||||
if self.thumbing {
|
||||
return;
|
||||
}
|
||||
let Some(page) = rows.clone().find(|page| !self.thumbs.contains_key(&(*page, dark)) && !self.thumbs_failed.contains(page)) else {
|
||||
let due = |page: &usize| !self.thumbs.contains_key(&(*page, dark)) || self.thumbs_stale.contains(&(*page, dark));
|
||||
let Some(page) = rows.clone().find(|page| due(page) && !self.thumbs_failed.contains(page)) else {
|
||||
return;
|
||||
};
|
||||
let (w, h) = self.sizes[page];
|
||||
@@ -127,6 +128,7 @@ impl PdfView {
|
||||
});
|
||||
match painted.map(|bgra| crate::pages::picture((tile[2] as u32, tile[3] as u32), bgra)) {
|
||||
Ok(Some(image)) => {
|
||||
view.thumbs_stale.remove(&(page, dark));
|
||||
if let Some(old) = view.thumbs.insert((page, dark), image) {
|
||||
view.retired.push(old);
|
||||
}
|
||||
@@ -145,13 +147,20 @@ impl PdfView {
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfView {
|
||||
/// The picture a row draws; an old one until repainted.
|
||||
pub fn picture(&self, page: usize, dark: bool) -> Option<Arc<RenderImage>> {
|
||||
self.thumbs.get(&(page, dark)).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
/// One row: the page number and its picture.
|
||||
fn row(view: &Entity<PdfView>, page: usize, dark: bool, cx: &App) -> gpui::AnyElement {
|
||||
let read = view.read(cx);
|
||||
let colors = &cx.theme().colors;
|
||||
let (w, h) = read.sizes[page];
|
||||
let picked = read.picked.contains(&page);
|
||||
let picture = read.thumbs.get(&(page, dark)).cloned();
|
||||
let picture = read.picture(page, dark);
|
||||
let count = read.sizes.len();
|
||||
let label = Viewer::text(cx, "pdf.page.label", &[("n", &(page + 1).to_string())]);
|
||||
let (clicked, pressed, dropped) = (view.clone(), view.clone(), view.clone());
|
||||
|
||||
@@ -326,3 +326,31 @@ fn old_tiles_are_painted_and_freed_with_their_tab() {
|
||||
cx.update(|_, app| view.update(app, |view, _| view.retire_all()));
|
||||
assert!(cx.update(|_, app| view.read(app).backdrop.is_empty()), "closing hands every old tile over");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_edit_keeps_page_pictures_until_they_repaint() {
|
||||
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);
|
||||
let dark = cx.update(|_, app| ely_gpui_component::theme::ActiveTheme::theme(app).is_dark());
|
||||
settle(cx, |app| view.read(app).picture(0, dark).is_some() && repainted(view.read(app)));
|
||||
let old = cx.update(|_, app| view.read(app).picture(0, dark).expect("a picture"));
|
||||
// A picture on its way holds back the repaint.
|
||||
cx.update(|_, app| view.update(app, |view, _| view.thumbing = true));
|
||||
cx.update(|_, app| view.update(app, |view, cx| view.annotate("adding a note", vec![note()], cx)));
|
||||
settle(cx, |app| view.read(app).edited == 1 && repainted(view.read(app)));
|
||||
// Dropped from the atlas, it returns only if drawn.
|
||||
cx.update(|window, _| window.drop_image(old.clone())).expect("drops");
|
||||
super::frames(cx);
|
||||
assert!(cx.update(|window, _| window.has_image_atlas_entry(&old)), "the old picture shows meanwhile");
|
||||
cx.update(|_, app| view.update(app, |view, cx| {
|
||||
view.thumbing = false;
|
||||
cx.notify()
|
||||
}));
|
||||
settle(cx, |app| view.read(app).thumbs_stale.is_empty());
|
||||
let new = cx.update(|_, app| view.read(app).picture(0, dark).expect("a picture"));
|
||||
assert!(!std::sync::Arc::ptr_eq(&new, &old), "the new picture replaces it");
|
||||
super::frames(cx);
|
||||
assert!(cx.update(|window, _| window.has_image_atlas_entry(&new)), "and is drawn");
|
||||
}
|
||||
@@ -71,6 +71,8 @@ pub struct PdfView {
|
||||
/// Page pictures for the side panel, by page and darkness.
|
||||
pub thumbs: HashMap<(usize, bool), Arc<RenderImage>>,
|
||||
pub thumbs_failed: HashSet<usize>,
|
||||
/// Pictures from before an edit; shown until repainted.
|
||||
pub thumbs_stale: HashSet<(usize, bool)>,
|
||||
/// A picture is on its way from the worker.
|
||||
pub thumbing: bool,
|
||||
/// Pages picked in the side panel, and the anchor.
|
||||
@@ -188,6 +190,7 @@ impl PdfView {
|
||||
mark: loaded.mark,
|
||||
thumbs: HashMap::new(),
|
||||
thumbs_failed: HashSet::new(),
|
||||
thumbs_stale: HashSet::new(),
|
||||
thumbing: false,
|
||||
picked: BTreeSet::new(),
|
||||
anchor: None,
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@
|
||||
- [ ] S-09 改尺寸失败后 `size_asked` 一直留着
|
||||
- [ ] S-10 开始放映的两处拒绝无提示(规则 2)
|
||||
- [x] P-01 文件系统的 `PermissionDenied` 被拼成不存在的文案键,应用崩溃 — worker 的拒绝改为自有错误类型 Refused;R1 测试不跨平台、不看提示;组件库 f834f55 加 Toaster::shown;R2 PASS
|
||||
- [ ] P-02 每次编辑落地清掉全部图块与缩略图,页面空白、刚画的墨迹消失
|
||||
- [x] P-02 每次编辑落地清掉全部图块与缩略图,页面空白、刚画的墨迹消失 — 图块随 R2.6;缩略图在编辑后留到重画;R1、R2 测试未证实际绘制;R3 PASS
|
||||
- [ ] P-03 “编辑文字和图片”先命中图片对象再命中文字;一点抖动就移动了背景图
|
||||
- [ ] P-04 `TEXT_BLOCKS` 对每个文字块重跑一遍整页内容过滤,编辑工具长时间无反应
|
||||
- [ ] P-05 连续快画或快点被拒:“文档在此间已改动,未执行”
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
FAIL
|
||||
|
||||
apps/ely-pdf/src/tests/input.rs:340 — 新增回归测试仅检查缩略图缓存与 Arc 身份,P-02 要求的等待期间持续绘制旧图缺少断言。具体复现:在隔离副本中将 side.rs:156 的图片读取加上 `.filter(|_| !read.thumbs_stale.contains(&(page, dark)))`,编辑后的过期缩略图随即停止绘制,新增测试 `an_edit_keeps_page_pictures_until_they_repaint` 仍通过。另一个探针暂停替换请求、清除旧图的图集项并重绘,原实现通过,该变异实现因旧图未进入绘制而失败;恢复原实现后再次通过。证据保存在 `/tmp/ely-p02-mutation-existing.log`、`/tmp/ely-p02-mutation-visual.log`、`/tmp/ely-p02-restored.log`。修复方向:给新增测试加入可控的替换等待阶段,验证旧缩略图在该阶段实际绘制,并验证新图到达后的替换。
|
||||
@@ -0,0 +1,5 @@
|
||||
FAIL
|
||||
|
||||
apps/ely-pdf/src/tests/input.rs:344 — 上一轮的实际绘制测试缺口仍在:`drawn` 调用的 `picture()` 返回 `thumbs` 缓存,Arc 身份断言验证的是缓存保留。具体复现:在隔离副本中将 `side.rs:163` 改成 `let picture = read.picture(page, dark).filter(|_| !read.thumbs_stale.contains(&(page, dark)));`,编辑后的过期缩略图停止绘制,`an_edit_keeps_page_pictures_until_they_repaint` 仍通过。暂停重画期间清除旧图图集项、重绘并检查 `window.has_image_atlas_entry(&old)` 的探针,在原实现上通过、该变异上失败、恢复后再次通过。worker 构建与 ElyPDF 全部 123 项测试通过;变异证据见 `/tmp/ely-p02-r2-mutation-existing.log`、`/tmp/ely-p02-r2-mutation-visual.log`、`/tmp/ely-p02-r2-restored.log`,探针说明见 `/tmp/ely-p02-r2-probe.log`。修复方向:在受控等待阶段及新图替换后断言缩略图实际进入绘制,覆盖行组件隐藏旧图的回归。
|
||||
|
||||
apps/ely-pdf/src/tests/input.rs:339 — 新增注释 `Hold the repaint: a picture on its way blocks the next.` 含 11 个英文单词,超过 agents.md 规则 5 的十词上限。具体复现:运行 `python3 tests/scripts/comments.py`,退出码为 1,唯一报告指向该行;证据见 `/tmp/ely-p02-r2-comments.log`。修复方向:将该单行注释缩短至十词以内。
|
||||
@@ -0,0 +1 @@
|
||||
PASS
|
||||
Reference in new issue
Block a user