mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 17:44:08 +00:00
Merge pull request #5795 from dotCooCoo/fix/layers-thumbs-close-cache
Release Layers thumbnails when a document closes
This commit is contained in:
@@ -26,6 +26,7 @@ impl VectorcraftApp {
|
||||
view.artboard = view.artboard.min(doc.doc.artboards.len().saturating_sub(1));
|
||||
}
|
||||
self.view_uids = docs.iter().map(|d| d.uid).collect();
|
||||
panels::layers::retain_open_thumbs(&self.view_uids);
|
||||
crate::doc_windows::retain_views(self);
|
||||
crate::doc_windows::opened(self);
|
||||
// Undo can remove the Artboard tool's active or additionally selected boards. Repair only
|
||||
|
||||
@@ -69,7 +69,7 @@ use model::{push_rows, rows_of_doc};
|
||||
use rows::{drag_ghost, key, rename_id, resolve_click, row};
|
||||
#[cfg(test)]
|
||||
use thumbs::THUMBS;
|
||||
pub(crate) use thumbs::{ThumbSource, node_arc, real_thumb, show_thumbs_of, thumb_options};
|
||||
pub(crate) use thumbs::{ThumbSource, node_arc, real_thumb, retain_open_thumbs, show_thumbs_of, thumb_options};
|
||||
|
||||
/// Width of the eye and the lock columns.
|
||||
const COLUMN: f32 = 25.0;
|
||||
|
||||
@@ -246,6 +246,29 @@ fn a_layer_that_does_not_print_has_its_name_italic() {
|
||||
assert!(!italic_names(&mut app, &ctx).iter().any(|n| n == "Layer 1"), "Print on again: upright again");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closing_a_document_releases_only_its_layer_thumbnails() {
|
||||
let (mut app, _, _) = two_rects();
|
||||
let ctx = egui::Context::default();
|
||||
let first = app.session.active().unwrap().uid;
|
||||
frame(&mut app, &ctx, vec![], false);
|
||||
assert!(THUMBS.with(|c| c.borrow().cached(first)));
|
||||
|
||||
app.session.execute("file.new", &json!({"width": 200, "height": 200})).unwrap();
|
||||
app.session.execute("shape.rectangle", &json!({"x": 10, "y": 10, "width": 50, "height": 50})).unwrap();
|
||||
let second = app.session.active().unwrap().uid;
|
||||
frame(&mut app, &ctx, vec![], false);
|
||||
assert!(THUMBS.with(|c| c.borrow().cached(second)));
|
||||
|
||||
app.session.execute("file.close", &json!({"index": 0})).unwrap();
|
||||
app.sync_views();
|
||||
THUMBS.with(|c| {
|
||||
let thumbs = c.borrow();
|
||||
assert!(!thumbs.cached(first), "closed document textures are released");
|
||||
assert!(thumbs.cached(second), "open document textures stay cached");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn row_thumbnails_wait_for_the_end_of_a_drag() {
|
||||
let (mut app, _, _) = two_rects();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::collections::HashSet;
|
||||
use vectorcraft_doc::{NodeKind, TextKind};
|
||||
use vectorcraft_geom::PathData;
|
||||
|
||||
@@ -51,6 +52,8 @@ type ThumbJob = ((u64, NodeId), Weak<Node>, u32);
|
||||
#[derive(Default)]
|
||||
pub(super) struct Thumbs {
|
||||
entries: HashMap<(u64, NodeId), ThumbEntry>,
|
||||
/// Documents still open. None until the app first synchronizes its documents.
|
||||
open_docs: Option<HashSet<u64>>,
|
||||
/// What the worker is rendering, by row.
|
||||
pending: HashMap<(u64, NodeId), (Weak<Node>, u32)>,
|
||||
worker: Option<ThumbWorker<(u64, NodeId), ThumbJob>>,
|
||||
@@ -249,6 +252,36 @@ fn render_thumb(r: &mut vectorcraft_render::Renderer, doc: &Document, n: &Node,
|
||||
}
|
||||
|
||||
impl Thumbs {
|
||||
#[cfg(test)]
|
||||
pub(super) fn cached(&self, uid: u64) -> bool {
|
||||
self.entries.keys().any(|key| key.0 == uid)
|
||||
}
|
||||
|
||||
/// Release thumbnails and queued jobs for documents that have closed, keeping the cache for
|
||||
/// other open documents even when the panels are hidden or another document is active.
|
||||
fn retain_open(&mut self, uids: &[u64]) {
|
||||
if self.open_docs.as_ref().is_some_and(|open| open.len() == uids.len() && uids.iter().all(|uid| open.contains(uid))) {
|
||||
return;
|
||||
}
|
||||
let open: HashSet<u64> = uids.iter().copied().collect();
|
||||
let stale =
|
||||
self.entries.keys().chain(self.pending.keys()).any(|key| !open.contains(&key.0)) || self.showing.is_some_and(|uid| !open.contains(&uid));
|
||||
self.entries.retain(|key, _| open.contains(&key.0));
|
||||
self.pending.retain(|key, _| open.contains(&key.0));
|
||||
self.showing = self.showing.filter(|uid| open.contains(uid));
|
||||
if stale
|
||||
&& let Some(w) = &self.worker
|
||||
&& !w.discard({
|
||||
let open = open.clone();
|
||||
move |key| !open.contains(&key.0)
|
||||
})
|
||||
{
|
||||
self.worker = None;
|
||||
self.pending.clear();
|
||||
}
|
||||
self.open_docs = Some(open);
|
||||
}
|
||||
|
||||
/// Keep `img` (None: nothing drawn) as the thumbnail of row `key` showing `node`.
|
||||
fn upload(&mut self, ctx: &egui::Context, key: (u64, NodeId), node: Weak<Node>, px: u32, img: Option<&vectorcraft_render::Rendered>, ms: f64) {
|
||||
let color = img.map(crate::canvas::color_image);
|
||||
@@ -280,7 +313,7 @@ impl Thumbs {
|
||||
/// The panels draw the rows of document `uid` (None: no document). When that is another
|
||||
/// document than before, the worker drops the jobs it has not started for every other
|
||||
/// document, so they don't hold up this one's thumbnails, and those rows are asked for again
|
||||
/// when their document shows again. The thumbnails made already stay.
|
||||
/// when their document shows again. The thumbnails of still-open documents stay.
|
||||
pub(super) fn show(&mut self, uid: Option<u64>) {
|
||||
if self.showing == uid {
|
||||
return;
|
||||
@@ -298,6 +331,9 @@ impl Thumbs {
|
||||
/// Keep the thumbnail `d` the worker finished.
|
||||
fn receive(&mut self, ctx: &egui::Context, d: Done<ThumbJob>) {
|
||||
let (key, node, px) = d.key;
|
||||
if self.open_docs.as_ref().is_some_and(|open| !open.contains(&key.0)) {
|
||||
return;
|
||||
}
|
||||
if self.pending.get(&key).is_some_and(|(p, ppx)| p.ptr_eq(&node) && *ppx == px) {
|
||||
self.pending.remove(&key);
|
||||
}
|
||||
@@ -383,6 +419,11 @@ pub(crate) fn show_thumbs_of(uid: Option<u64>) {
|
||||
THUMBS.with(|c| c.borrow_mut().show(uid));
|
||||
}
|
||||
|
||||
/// Forget thumbnails of documents no longer open, even if neither thumbnail panel is shown.
|
||||
pub(crate) fn retain_open_thumbs(uids: &[u64]) {
|
||||
THUMBS.with(|c| c.borrow_mut().retain_open(uids));
|
||||
}
|
||||
|
||||
/// A real rendered thumbnail of row `n`, cached by node identity (unchanged nodes keep their `Arc`
|
||||
/// allocation, so the address is a free change detector). Only rendered for visible rows; see
|
||||
/// [`Thumbs::texture`] for when.
|
||||
@@ -440,6 +481,17 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn a_finished_job_cannot_restore_a_closed_documents_thumbnail() {
|
||||
let mut thumbs = Thumbs::default();
|
||||
let ctx = egui::Context::default();
|
||||
thumbs.retain_open(&[2]);
|
||||
thumbs.receive(&ctx, Done { key: ((1, NodeId(1)), Weak::new(), 16), img: None, ms: 0.0 });
|
||||
assert!(thumbs.entries.is_empty(), "a closed document's late result is discarded");
|
||||
thumbs.receive(&ctx, Done { key: ((2, NodeId(2)), Weak::new(), 16), img: None, ms: 0.0 });
|
||||
assert!(thumbs.entries.contains_key(&(2, NodeId(2))), "an open document's result is kept");
|
||||
}
|
||||
|
||||
/// An open zigzag of `anchors` anchors.
|
||||
fn line(anchors: usize) -> PathData {
|
||||
let pts: Vec<Point> = (0..anchors).map(|i| Point::new(i as f64 * 0.01, (i % 2) as f64 * 50.0)).collect();
|
||||
|
||||
Reference in New Issue
Block a user