Fix remaining Hide Guides rendering cases (#3219)

Include guide visibility in effect raster cache inputs, skip hidden guide paths in outline members, and clear selected guide paths when hiding guides. Add regression coverage for cached effects and selection overlays.\n\nThe cached-effect regression test is adapted from the overlapping #3264 implementation.\n\nCo-authored-by: Mahtab <smmalam9@gmail.com>
This commit is contained in:
Mirko Kerezovic
2026-10-21 15:42:35 +07:00
committed by bflatastic
parent 2305ebb931
commit d2625ec108
5 changed files with 79 additions and 4 deletions
+2
View File
@@ -2065,6 +2065,8 @@ impl Renderer {
let doc = self.doc_inputs(f.doc);
let mut h = DefaultHasher::new();
(doc.cms, f.opts.anti_alias.threshold(), self.knockout, f.doc.raster_effects_ppi.to_bits()).hash(&mut h);
// Cached effects on containers can include paths made into guides.
f.opts.hide_guides.hash(&mut h);
if let Some((isolated, path)) = self.isolation_path(f)
&& n.id != isolated
&& path.contains(&n.id)
+1 -1
View File
@@ -1386,7 +1386,7 @@ impl Renderer {
/// Outline view, culled by its cached bounds ([`Self::bounds_of`]) and drawn with `c` as
/// `fx_owner`, so its path and live-effect geometry come from the caches.
fn draw_outline_member(&mut self, ctx: &mut RenderContext, f: &Frame, c: &Arc<Node>) {
if !c.visible || f.opts.hidden.contains(&c.id) {
if !c.visible || f.opts.hidden.contains(&c.id) || f.opts.hide_guides && matches!(c.kind, NodeKind::Path { guide: true, .. }) {
return;
}
match self.bounds_of(f.symbols, c) {
+48
View File
@@ -186,3 +186,51 @@ fn hide_guides_hides_guide_paths_on_screen() {
assert!(pixels(false) > 0, "Show Guides draws the guide path");
assert_eq!(pixels(true), 0, "Hide Guides leaves the guide path out");
}
/// #3219: hiding guides omits guide paths, including those inside cached group effects, without
/// hiding ordinary artwork. The same renderer must show them again when the view is toggled back.
#[test]
fn hidden_guide_paths_leave_the_artwork_and_refresh_cached_effects() {
for effect in [false, true] {
let mut d = Document::new(120.0, 120.0);
let layer = d.layers[0].id;
let mut guide =
Node::path(d.alloc_id(), shapes::rectangle(Rect::new(20.5, 20.5, 60.5, 60.5)), Appearance::basic(Paint::None, Paint::None, 0.0));
if let NodeKind::Path { guide, .. } = &mut guide.kind {
*guide = true;
}
let id = guide.id;
let art = Node::path(
d.alloc_id(),
shapes::rectangle(Rect::new(80.0, 80.0, 100.0, 100.0)),
Appearance::basic(Paint::solid(Color::rgb(0.0, 0.0, 1.0)), Paint::None, 0.0),
);
let mut group = Node::group(d.alloc_id(), vec![Arc::new(guide), Arc::new(art)]);
if effect {
group.appearance.effects.push(vectorcraft_doc::Effect {
id: "blur.gaussian".into(),
params: serde_json::json!({"radius":2}),
visible: true,
});
}
d.insert(Some(layer), 0, group).unwrap();
let mut without_guide = d.clone();
without_guide.remove(id).unwrap();
let mut renderer = Renderer::new();
for outline in [false, true] {
let opts = RenderOptions { outline, background: Some([255; 4]), ..Default::default() };
let shown = renderer.render(&d, 120, 120, Affine::IDENTITY, &opts);
let hidden = RenderOptions { hide_guides: true, ..opts.clone() };
let expected = Renderer::new().render(&without_guide, 120, 120, Affine::IDENTITY, &hidden);
assert_ne!(shown.pixels, expected.pixels, "the guide is initially visible, effect={effect}, outline={outline}");
assert!(
renderer.render(&d, 120, 120, Affine::IDENTITY, &hidden).pixels == expected.pixels,
"only guides hide, effect={effect}, outline={outline}"
);
assert!(
renderer.render(&d, 120, 120, Affine::IDENTITY, &opts).pixels == shown.pixels,
"guides show again, effect={effect}, outline={outline}"
);
}
}
}
+16
View File
@@ -5630,6 +5630,22 @@ mod tests {
assert_eq!(dots(&mut app).len(), 2, "one anchor still shows them");
}
/// #3219: a selected path made into a guide is deselected when guides are hidden, so the
/// selection overlay cannot reveal the hidden path.
#[test]
fn hide_guides_deselects_selected_guide_paths() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.run("file.new", json!({"width": 400, "height": 300})).unwrap();
let id = app.run("shape.rectangle", json!({"x": 100, "y": 100, "width": 100, "height": 100})).unwrap()["id"].as_u64().unwrap();
app.run("view.guides.make", json!({})).unwrap();
app.run("select.set", json!({"ids": [id]})).unwrap();
assert_eq!(app.session.active().unwrap().selection.objects, vec![vectorcraft_doc::NodeId(id)]);
app.run("view.guides", json!({})).unwrap();
assert!(app.session.active().unwrap().selection.objects.is_empty());
}
/// Smart Guides › Object Highlighting (#394): the outline of the object under the pointer does not
/// show with View › Snap to Grid or View › Pixel Preview on, even with Smart Guides and the option on.
/// #3219: guide paths hidden by View > Hide Guides are not hittable and cannot reveal
+12 -3
View File
@@ -1250,9 +1250,18 @@ pub fn run_ui_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<R
"view.boundingBox" => flag(&mut app.ui.view.bounding_box),
"view.guides" => {
let r = flag(&mut app.ui.view.guides);
// Hidden guides can't stay selected (selected, they are all that is).
if !app.ui.view.guides && app.session.active().is_some_and(|d| !d.selection.guides.is_empty()) {
// Deselecting a document that is open can't fail.
// Hidden ruler guides and guide paths cannot stay selected: their selection overlay
// would otherwise reveal geometry that Hide Guides just hid.
let hidden_selection = !app.ui.view.guides
&& app.session.active().is_some_and(|d| {
!d.selection.guides.is_empty()
|| d.selection
.objects
.iter()
.any(|id| matches!(d.doc.node(*id).map(|n| &n.kind), Some(vectorcraft_doc::NodeKind::Path { guide: true, .. })))
});
if hidden_selection {
// Deselecting a document that is open cannot fail.
let _ = app.run("select.none", json!({}));
}
r