feat(iced): add transform handle hit-testing
This commit is contained in:
@@ -66,6 +66,8 @@ struct OverlayState {
|
||||
cursor_pos: Option<Point>,
|
||||
/// Whether the mouse is over the overlay.
|
||||
is_hovered: bool,
|
||||
/// Current handle being hovered (for cursor changes).
|
||||
hovered_handle: TransformHandle,
|
||||
}
|
||||
|
||||
impl OverlayProgram {
|
||||
@@ -84,6 +86,69 @@ impl OverlayProgram {
|
||||
(ox, oy, display_w, display_h)
|
||||
}
|
||||
|
||||
/// Test which transform handle is at the given viewport position.
|
||||
pub fn hit_test_handle(
|
||||
&self,
|
||||
viewport_x: f32,
|
||||
viewport_y: f32,
|
||||
) -> TransformHandle {
|
||||
let Some(ref tr) = self.selection_transform else {
|
||||
return TransformHandle::None;
|
||||
};
|
||||
if tr.is_empty() {
|
||||
return TransformHandle::None;
|
||||
}
|
||||
|
||||
let (origin_x, origin_y, _, _) = self.canvas_origin(iced::Size::new(0.0, 0.0));
|
||||
|
||||
// Convert viewport to canvas coordinates
|
||||
let canvas_x = (viewport_x - origin_x) / self.zoom;
|
||||
let canvas_y = (viewport_y - origin_y) / self.zoom;
|
||||
|
||||
let handle_size = TransformHandle::HANDLE_SIZE / self.zoom;
|
||||
let half = handle_size / 2.0;
|
||||
|
||||
let x = tr.pos.x;
|
||||
let y = tr.pos.y;
|
||||
let w = tr.size.x;
|
||||
let h = tr.size.y;
|
||||
|
||||
// Check rotation handle first (it's outside the box)
|
||||
let rotate_y = y - TransformHandle::ROTATE_OFFSET / self.zoom;
|
||||
let rotate_x = x + w / 2.0;
|
||||
let rotate_radius = 4.0 / self.zoom;
|
||||
let dx = canvas_x - rotate_x;
|
||||
let dy = canvas_y - rotate_y;
|
||||
if dx * dx + dy * dy <= rotate_radius * rotate_radius {
|
||||
return TransformHandle::Rotate;
|
||||
}
|
||||
|
||||
// Check corner and edge handles
|
||||
let handles = [
|
||||
(TransformHandle::TopLeft, x, y),
|
||||
(TransformHandle::TopRight, x + w, y),
|
||||
(TransformHandle::BottomLeft, x, y + h),
|
||||
(TransformHandle::BottomRight, x + w, y + h),
|
||||
(TransformHandle::Top, x + w / 2.0, y),
|
||||
(TransformHandle::Bottom, x + w / 2.0, y + h),
|
||||
(TransformHandle::Left, x, y + h / 2.0),
|
||||
(TransformHandle::Right, x + w, y + h / 2.0),
|
||||
];
|
||||
|
||||
for (handle, hx, hy) in &handles {
|
||||
if (canvas_x - hx).abs() <= half && (canvas_y - hy).abs() <= half {
|
||||
return *handle;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if inside the selection box (for move)
|
||||
if canvas_x >= x && canvas_x <= x + w && canvas_y >= y && canvas_y <= y + h {
|
||||
return TransformHandle::Move;
|
||||
}
|
||||
|
||||
TransformHandle::None
|
||||
}
|
||||
|
||||
/// Draw transform handles for selection.
|
||||
fn draw_transform_handles(
|
||||
&self,
|
||||
@@ -291,11 +356,19 @@ impl canvas::Program<Message> for OverlayProgram {
|
||||
);
|
||||
state.cursor_pos = Some(local_pos);
|
||||
state.is_hovered = bounds.contains(position);
|
||||
|
||||
// Update hovered handle
|
||||
if self.selection_transform.is_some() {
|
||||
state.hovered_handle = self.hit_test_handle(position.x, position.y);
|
||||
} else {
|
||||
state.hovered_handle = TransformHandle::None;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if state.is_hovered && !cursor.is_over(bounds) {
|
||||
state.is_hovered = false;
|
||||
state.cursor_pos = None;
|
||||
state.hovered_handle = TransformHandle::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user