mirror of
https://github.com/storytold/cadcraft.git
synced 2026-11-15 10:23:43 -06:00
Rollover highlight: a resting pointer follows the layers frozen in the active viewport
The object under the cursor and selection cycling's badge were worked out only when the pointer moved. Since selection respects the layers frozen in the active viewport, both depend on them: after a layer was frozen or thawed there (VPLAYER, undo), or a viewport that freezes other layers became the active one, a pointer that had not moved kept the object the viewport no longer draws. That object was not highlighted, but the object now on top was not either, and the badge stayed, until the pointer moved. `CanvasState` remembers the frozen layers both were worked out for (`hover_frozen`), and `update_hover` works them out again when that list changes. Tests, each failing without its line: `the_highlight_follows_the_frozen_layers_under_a_resting_pointer` (crates/ui-egui/tests/viewport_frozen_grips.rs) and `the_cycling_badge_follows_the_frozen_layers_under_a_resting_pointer` (canvas/tests.rs).
This commit is contained in:
@@ -176,6 +176,9 @@ pub struct CanvasState {
|
||||
pub build_ms: f64,
|
||||
pub draw_ms: f64,
|
||||
hover_at: Option<Pos2>,
|
||||
/// The layers frozen in the active viewport when the object and the cycling badge under the
|
||||
/// cursor were last worked out ([`cadcraft_engine::DocState::viewport_frozen`]).
|
||||
hover_frozen: Vec<String>,
|
||||
/// Constraint glyphs for the parametric overlay (cached per drawing revision).
|
||||
pub param: crate::parametric::Cache,
|
||||
/// A selection window opened by a press-and-drag, to close where the button is let go.
|
||||
@@ -695,8 +698,17 @@ fn update_hover(app: &mut CadApp, ui: &egui::Ui, inside: bool, hover_pos: Option
|
||||
// The cursor picks objects (pick box, rollover highlight): "Select objects" and single-object
|
||||
// prompts, or no command with PICKFIRST on; SELECTIONPREVIEW says in which of those it shows.
|
||||
let selecting = app.session.previews_selection();
|
||||
// What the cursor is over also depends on the layers frozen in the active viewport, and those
|
||||
// can change while the pointer rests (VPLAYER, undo, another viewport made the active one).
|
||||
let refrozen = match app.session.state() {
|
||||
Ok(st) if st.viewport_frozen() != app.canvas.hover_frozen.as_slice() => {
|
||||
app.canvas.hover_frozen = st.viewport_frozen().to_vec();
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if inside && selecting && app.session.pending_window.is_none() {
|
||||
if hover_pos != app.canvas.hover_at
|
||||
if (hover_pos != app.canvas.hover_at || refrozen)
|
||||
&& let (Some(w), Ok(st)) = (raw_world, app.session.state())
|
||||
{
|
||||
let filter = app.session.settings.previewfilter;
|
||||
@@ -718,7 +730,7 @@ fn update_hover(app: &mut CadApp, ui: &egui::Ui, inside: bool, hover_pos: Option
|
||||
app.canvas.hover_at = None;
|
||||
}
|
||||
shift_space_cycle(app, ui, hover_pos.filter(|_| inside), raw_world);
|
||||
if inside && hover_pos != app.canvas.cycle_badge_at {
|
||||
if inside && (hover_pos != app.canvas.cycle_badge_at || refrozen) {
|
||||
app.canvas.cycle_badge = raw_world.is_some_and(|w| app.session.cycling_badge(w));
|
||||
app.canvas.cycle_badge_at = hover_pos;
|
||||
} else if !inside {
|
||||
|
||||
@@ -366,3 +366,32 @@ fn the_viewport_highlight_leaves_out_objects_the_viewport_does_not_draw() {
|
||||
app.session.execute("vplayer", &serde_json::json!({ "handles": [b], "thaw": ["Hidden"] })).unwrap();
|
||||
assert_eq!(outlined(&app, &mut highlight, both), both);
|
||||
}
|
||||
|
||||
/// Selection cycling's badge beside a resting pointer follows the layers frozen in the active
|
||||
/// viewport: over two overlapping lines it shows while the viewport draws both, goes when the
|
||||
/// layer of one of them is frozen there, and comes back when the layer is thawed.
|
||||
#[test]
|
||||
fn the_cycling_badge_follows_the_frozen_layers_under_a_resting_pointer() {
|
||||
let mut app = CadApp::new(cadcraft_engine::Session::new(), crate::Services::default());
|
||||
for _ in 0..2 {
|
||||
app.run("line", serde_json::json!({ "points": [[0, 0], [100, 0]] })).unwrap();
|
||||
}
|
||||
let on_top = app.session.doc().unwrap().model.handles()[1];
|
||||
let (_, b) = viewports_freezing_hidden(&mut app);
|
||||
app.session.execute("properties.set", &serde_json::json!({ "handles": [on_top.hex()], "layer": "Hidden" })).unwrap();
|
||||
app.session.execute("setvar", &serde_json::json!({ "name": "SELECTIONCYCLING", "value": 1 })).unwrap();
|
||||
app.session.execute("mspace", &serde_json::json!({ "handle": b })).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
/// Whether the badge shows after a frame with the pointer resting on the two lines.
|
||||
fn badge(app: &mut CadApp, ctx: &egui::Context) -> bool {
|
||||
let (at, on_lines) = (Some(Pos2::new(200.0, 150.0)), Some(Vec2::new(50.0, 0.0)));
|
||||
let mut output = ctx.run_ui(egui::RawInput::default(), |ui| update_hover(app, ui, true, at, on_lines, 2.0));
|
||||
output.textures_delta.clear();
|
||||
app.canvas.cycle_badge
|
||||
}
|
||||
assert!(badge(&mut app, &ctx), "this viewport draws both lines: a click would choose between them");
|
||||
app.session.execute("vplayer", &serde_json::json!({ "handles": [b], "freeze": ["Hidden"] })).unwrap();
|
||||
assert!(!badge(&mut app, &ctx), "the badge stayed for a line the viewport no longer draws");
|
||||
app.session.execute("vplayer", &serde_json::json!({ "handles": [b], "thaw": ["Hidden"] })).unwrap();
|
||||
assert!(badge(&mut app, &ctx), "thawed in the viewport: two lines are under the cursor again");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Inside a layout viewport an object on a layer frozen in that viewport is not drawn: selected,
|
||||
//! it paints no grips there, and the cursor does not highlight it.
|
||||
//! it paints no grips there, and the cursor does not highlight it, also when the layer is frozen
|
||||
//! while the pointer rests over the object.
|
||||
|
||||
use cadcraft_engine::doc::Handle;
|
||||
use cadcraft_engine::geom::Vec2;
|
||||
@@ -143,3 +144,27 @@ fn the_cursor_highlights_the_object_the_viewport_draws() {
|
||||
assert_eq!(app.canvas.hover, Some(under_cursor));
|
||||
}
|
||||
}
|
||||
|
||||
/// The layer of the line under a resting pointer is frozen in the active viewport, then thawed:
|
||||
/// the highlight goes to the line the viewport still draws and comes back, without the pointer
|
||||
/// moving in between.
|
||||
#[test]
|
||||
fn the_highlight_follows_the_frozen_layers_under_a_resting_pointer() {
|
||||
let Sheet { mut app, shown, hidden, a: _, b } = sheet();
|
||||
let ctx = egui::Context::default();
|
||||
painted_grips(&mut app, &ctx);
|
||||
enter(&mut app, &b);
|
||||
painted_grips(&mut app, &ctx);
|
||||
let on_lines = app.canvas.xf.unwrap().to_screen(Vec2::new(50.0, 0.0));
|
||||
assert!(app.canvas.rect.unwrap().contains(on_lines), "the viewport must show the lines");
|
||||
frame(&mut app, &ctx, Some(on_lines));
|
||||
frame(&mut app, &ctx, Some(on_lines));
|
||||
assert_eq!(app.canvas.hover, Some(hidden), "this viewport draws both lines, the later one on top");
|
||||
|
||||
app.run("vplayer", json!({ "handles": [b], "freeze": ["Hidden"] })).unwrap();
|
||||
frame(&mut app, &ctx, Some(on_lines));
|
||||
assert_eq!(app.canvas.hover, Some(shown), "the cursor kept the line the viewport no longer draws");
|
||||
app.run("vplayer", json!({ "handles": [b], "thaw": ["Hidden"] })).unwrap();
|
||||
frame(&mut app, &ctx, Some(on_lines));
|
||||
assert_eq!(app.canvas.hover, Some(hidden), "thawed in the viewport: the line on top is under the cursor again");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user