image_viewer: Preserve PDF state across page changes
- Add ImageItemEvent::PageChanged and emit it from ImageItem::set_page instead of Reloaded and MetadataUpdated, since turning a page does not change the document bytes - Handle PageChanged in ImageView by only refreshing layout state, keeping the outline, page and thumbnail caches, form edits, undo history, and annotations intact - Jumping via the outline no longer resets the outline panel to "No outline"
This commit is contained in:
1 parent
ad13ed3b7d
commit
1b2685ac9b
2 files changed
+43
-17
No files matched your search
@@ -498,6 +498,15 @@ impl ImageView {
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
ImageItemEvent::PageChanged => {
|
||||
self.page_bounds.borrow_mut().clear();
|
||||
self.image_size = self
|
||||
.image_item
|
||||
.read(cx)
|
||||
.image_metadata
|
||||
.map(|m| (m.width, m.height));
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5292,6 +5301,7 @@ mod tests {
|
||||
assert_eq!(cache.current_memory_bytes(), 0);
|
||||
assert!(cache.is_empty());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mod persistence {
|
||||
|
||||
@@ -21,9 +21,9 @@ use util::{ResultExt, rel_path::RelPath};
|
||||
use worktree::{LoadedBinaryFile, PathChange, Worktree, WorktreeId};
|
||||
|
||||
const EMPTY_1X1_PNG: &[u8] = &[
|
||||
137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1,
|
||||
8, 6, 0, 0, 0, 31, 21, 196, 137, 0, 0, 0, 13, 73, 68, 65, 84, 120, 156, 99, 96, 96,
|
||||
96, 96, 0, 0, 0, 5, 0, 1, 165, 246, 69, 64, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130,
|
||||
137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1, 8, 6, 0,
|
||||
0, 0, 31, 21, 196, 137, 0, 0, 0, 13, 73, 68, 65, 84, 120, 156, 99, 96, 96, 96, 96, 0, 0, 0, 5,
|
||||
0, 1, 165, 246, 69, 64, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130,
|
||||
];
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, PartialOrd, Ord, Eq)]
|
||||
@@ -53,6 +53,7 @@ pub enum ImageItemEvent {
|
||||
Reloaded,
|
||||
FileHandleChanged,
|
||||
MetadataUpdated,
|
||||
PageChanged,
|
||||
}
|
||||
|
||||
impl EventEmitter<ImageItemEvent> for ImageItem {}
|
||||
@@ -180,7 +181,9 @@ impl ImageItem {
|
||||
if image_bytes.starts_with(b"%PDF-") {
|
||||
let engine = pdf_viewer::pdfium::PdfiumEngine::new();
|
||||
let doc = engine.load_document_from_bytes(image_bytes, None)?;
|
||||
let dim = doc.page_size(0).unwrap_or(pdf_viewer::PageDimensions::new(612.0, 792.0));
|
||||
let dim = doc
|
||||
.page_size(0)
|
||||
.unwrap_or(pdf_viewer::PageDimensions::new(612.0, 792.0));
|
||||
let (width, height) = dim.to_pixel_size(1.0, 144.0);
|
||||
return Ok(ImageMetadata {
|
||||
width,
|
||||
@@ -278,7 +281,9 @@ impl ImageItem {
|
||||
if let Some(ref text) = info.full_text {
|
||||
Some(text.clone())
|
||||
} else {
|
||||
pdf_viewer::PdfiumEngine::new().extract_text_from_bytes(&info.pdf_bytes, None).ok()
|
||||
pdf_viewer::PdfiumEngine::new()
|
||||
.extract_text_from_bytes(&info.pdf_bytes, None)
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,8 +304,9 @@ impl ImageItem {
|
||||
format: image::ImageFormat::Png,
|
||||
colors: ImageColorInfo::from_color_type(image::ColorType::Rgba8),
|
||||
});
|
||||
cx.emit(ImageItemEvent::Reloaded);
|
||||
cx.emit(ImageItemEvent::MetadataUpdated);
|
||||
// A page turn keeps the same document bytes, so document-derived state
|
||||
// (outline, caches, edits) stays valid; only the layout changes.
|
||||
cx.emit(ImageItemEvent::PageChanged);
|
||||
cx.notify();
|
||||
true
|
||||
} else {
|
||||
@@ -340,13 +346,15 @@ impl ImageItem {
|
||||
self.reload_task = Some(cx.spawn(async move |this, cx| {
|
||||
if let Ok(bytes) = content.await.context("Failed to load image content") {
|
||||
let is_pdf = bytes.starts_with(b"%PDF-");
|
||||
let cur_page = this.read_with(cx, |this, _| this.current_page()).unwrap_or(0);
|
||||
let cur_page = this
|
||||
.read_with(cx, |this, _| this.current_page())
|
||||
.unwrap_or(0);
|
||||
|
||||
if is_pdf {
|
||||
let bytes_clone = bytes.clone();
|
||||
let pdf_res = background.spawn(async move {
|
||||
create_gpui_images_from_pdf(&bytes_clone)
|
||||
}).await;
|
||||
let pdf_res = background
|
||||
.spawn(async move { create_gpui_images_from_pdf(&bytes_clone) })
|
||||
.await;
|
||||
if let Ok((pages, full_text)) = pdf_res {
|
||||
let total_pages = pages.len();
|
||||
let target_page = cur_page.min(total_pages.saturating_sub(1));
|
||||
@@ -403,7 +411,9 @@ pub fn is_image_file(project: &Entity<Project>, path: &ProjectPath, cx: &App) ->
|
||||
});
|
||||
|
||||
match ext {
|
||||
Some(ext) => (Img::extensions().contains(&ext.as_str()) || ext == "pdf") && !ext.contains("svg"),
|
||||
Some(ext) => {
|
||||
(Img::extensions().contains(&ext.as_str()) || ext == "pdf") && !ext.contains("svg")
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
@@ -759,7 +769,10 @@ impl RemoteImageStore {
|
||||
pages,
|
||||
full_text,
|
||||
};
|
||||
let dummy_img = Arc::new(gpui::Image::from_bytes(gpui::ImageFormat::Png, EMPTY_1X1_PNG.to_vec()));
|
||||
let dummy_img = Arc::new(gpui::Image::from_bytes(
|
||||
gpui::ImageFormat::Png,
|
||||
EMPTY_1X1_PNG.to_vec(),
|
||||
));
|
||||
(dummy_img, Some(pdf_info))
|
||||
} else {
|
||||
(create_gpui_image(content.clone())?, None)
|
||||
@@ -829,9 +842,9 @@ impl ImageStoreImpl for Entity<LocalImageStore> {
|
||||
let is_pdf = content.starts_with(b"%PDF-");
|
||||
let (image, pdf_info) = if is_pdf {
|
||||
let content_clone = content.clone();
|
||||
let pdf_res = background.spawn(async move {
|
||||
create_gpui_images_from_pdf(&content_clone)
|
||||
}).await;
|
||||
let pdf_res = background
|
||||
.spawn(async move { create_gpui_images_from_pdf(&content_clone) })
|
||||
.await;
|
||||
if let Ok((pages, full_text)) = pdf_res {
|
||||
let total_pages = pages.len();
|
||||
let pdf_info = PdfPageInfo {
|
||||
@@ -841,7 +854,10 @@ impl ImageStoreImpl for Entity<LocalImageStore> {
|
||||
pages,
|
||||
full_text,
|
||||
};
|
||||
let dummy_img = Arc::new(gpui::Image::from_bytes(gpui::ImageFormat::Png, EMPTY_1X1_PNG.to_vec()));
|
||||
let dummy_img = Arc::new(gpui::Image::from_bytes(
|
||||
gpui::ImageFormat::Png,
|
||||
EMPTY_1X1_PNG.to_vec(),
|
||||
));
|
||||
(dummy_img, Some(pdf_info))
|
||||
} else {
|
||||
(create_gpui_image(content.clone())?, None)
|
||||
|
||||
Reference in new issue
Block a user