fix(overlay): hide system cursor over viewport, drop CAD over divider
Two cursors layered on top of each other (the OS arrow / crosshair icon plus the CAD crosshair drawn in `SelectionCanvas::draw`) looked busy and made it harder to see what was pickable. `Interaction::None` was the first try but `Stack::mouse_interaction` skips that value as "no opinion" — the correct hide signal is `Interaction::Hidden`, which iced_winit maps to a hidden cursor. Over a Model-tile divider the OS now wins (resize arrow), and the CAD crosshair is suppressed in `draw` via the same `tile_edge_under` helper that mouse_interaction uses.
This commit is contained in:
parent
39eab9a4c4
commit
168f248593
1 changed files with 62 additions and 37 deletions
|
|
@ -138,6 +138,46 @@ struct SelectionCanvas {
|
||||||
tile_edges: Vec<crate::scene::TileEdge>,
|
tile_edges: Vec<crate::scene::TileEdge>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SelectionCanvas {
|
||||||
|
/// Returns the orientation of the Model-tile divider the cursor sits
|
||||||
|
/// on (within a few pixels of perpendicular distance, inside the
|
||||||
|
/// edge's span). Used by both `mouse_interaction` (to pick a resize
|
||||||
|
/// cursor) and `draw` (to suppress the CAD crosshair).
|
||||||
|
fn tile_edge_under(
|
||||||
|
&self,
|
||||||
|
cursor: mouse::Cursor,
|
||||||
|
bounds: iced::Rectangle,
|
||||||
|
) -> Option<crate::scene::TileEdgeOrient> {
|
||||||
|
const TOL_PX: f32 = 4.0;
|
||||||
|
let pos = cursor.position_in(bounds)?;
|
||||||
|
for e in &self.tile_edges {
|
||||||
|
let (perp, edge_px, span_lo, span_hi, pos_along) = match e.orient {
|
||||||
|
crate::scene::TileEdgeOrient::Vertical => (
|
||||||
|
pos.x,
|
||||||
|
e.coord * bounds.width,
|
||||||
|
e.span.0 * bounds.height,
|
||||||
|
e.span.1 * bounds.height,
|
||||||
|
pos.y,
|
||||||
|
),
|
||||||
|
crate::scene::TileEdgeOrient::Horizontal => (
|
||||||
|
pos.y,
|
||||||
|
e.coord * bounds.height,
|
||||||
|
e.span.0 * bounds.width,
|
||||||
|
e.span.1 * bounds.width,
|
||||||
|
pos.x,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
if (perp - edge_px).abs() <= TOL_PX
|
||||||
|
&& pos_along >= span_lo
|
||||||
|
&& pos_along <= span_hi
|
||||||
|
{
|
||||||
|
return Some(e.orient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl canvas::Program<Message> for SelectionCanvas {
|
impl canvas::Program<Message> for SelectionCanvas {
|
||||||
type State = ();
|
type State = ();
|
||||||
|
|
||||||
|
|
@ -161,32 +201,12 @@ impl canvas::Program<Message> for SelectionCanvas {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Hover over a Model-tile divider → resize cursor cue.
|
// Hover over a Model-tile divider → resize cursor cue. The
|
||||||
if !self.tile_edges.is_empty() {
|
// system cursor is intentionally shown here so the OS arrow gives
|
||||||
if let Some(pos) = cursor.position_in(bounds) {
|
// its own resize affordance; the draw step suppresses the custom
|
||||||
const TOL_PX: f32 = 4.0;
|
// CAD crosshair while we're over a divider.
|
||||||
for e in &self.tile_edges {
|
if let Some(orient) = self.tile_edge_under(cursor, bounds) {
|
||||||
let (perp, edge_px, span_lo, span_hi, pos_along) = match e.orient {
|
return match orient {
|
||||||
crate::scene::TileEdgeOrient::Vertical => (
|
|
||||||
pos.x,
|
|
||||||
e.coord * bounds.width,
|
|
||||||
e.span.0 * bounds.height,
|
|
||||||
e.span.1 * bounds.height,
|
|
||||||
pos.y,
|
|
||||||
),
|
|
||||||
crate::scene::TileEdgeOrient::Horizontal => (
|
|
||||||
pos.y,
|
|
||||||
e.coord * bounds.height,
|
|
||||||
e.span.0 * bounds.width,
|
|
||||||
e.span.1 * bounds.width,
|
|
||||||
pos.x,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
if (perp - edge_px).abs() <= TOL_PX
|
|
||||||
&& pos_along >= span_lo
|
|
||||||
&& pos_along <= span_hi
|
|
||||||
{
|
|
||||||
return match e.orient {
|
|
||||||
crate::scene::TileEdgeOrient::Vertical => {
|
crate::scene::TileEdgeOrient::Vertical => {
|
||||||
mouse::Interaction::ResizingHorizontally
|
mouse::Interaction::ResizingHorizontally
|
||||||
}
|
}
|
||||||
|
|
@ -195,11 +215,12 @@ impl canvas::Program<Message> for SelectionCanvas {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
// Over the viewport (no divider, no viewcube): hide the system
|
||||||
}
|
// cursor entirely. `Interaction::None` would let the stack fall
|
||||||
}
|
// through to a sibling — `Hidden` is the explicit "no cursor"
|
||||||
|
// signal that actually suppresses the OS arrow.
|
||||||
if cursor.is_over(bounds) {
|
if cursor.is_over(bounds) {
|
||||||
mouse::Interaction::Crosshair
|
mouse::Interaction::Hidden
|
||||||
} else {
|
} else {
|
||||||
mouse::Interaction::default()
|
mouse::Interaction::default()
|
||||||
}
|
}
|
||||||
|
|
@ -525,7 +546,11 @@ impl canvas::Program<Message> for SelectionCanvas {
|
||||||
&& pos.y <= vc_y + VIEWCUBE_DRAW_PX
|
&& pos.y <= vc_y + VIEWCUBE_DRAW_PX
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
if !over_viewcube {
|
// Over a Model-tile divider the OS cursor switches to a resize
|
||||||
|
// arrow (see `mouse_interaction`); drawing the CAD crosshair on
|
||||||
|
// top of it would double up the visual feedback.
|
||||||
|
let over_divider = self.tile_edge_under(cursor, bounds).is_some();
|
||||||
|
if !over_viewcube && !over_divider {
|
||||||
if let Some(cp) = self.selection.last_move_pos {
|
if let Some(cp) = self.selection.last_move_pos {
|
||||||
let color = Color {
|
let color = Color {
|
||||||
r: 0.85,
|
r: 0.85,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue