use iced::{Point, Rectangle, mouse};
use crate::rectangle::RectangleExt as _;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Corner {
TopLeft,
TopRight,
BottomLeft,
BottomRight,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Side {
Top,
Right,
Bottom,
Left,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum SideOrCorner {
Side(Side),
Corner(Corner),
}
impl Corner {
pub fn resize_rect(self, initial_rect: Rectangle, dy: f32, dx: f32) -> Rectangle {
match self {
Self::TopLeft => initial_rect
.with_y(|y| y + dy)
.with_x(|x| x + dx)
.with_width(|w| w - dx)
.with_height(|h| h - dy),
Self::TopRight => initial_rect
.with_y(|y| y + dy)
.with_width(|w| w + dx)
.with_height(|h| h - dy),
Self::BottomLeft => initial_rect
.with_x(|x| x + dx)
.with_width(|w| w - dx)
.with_height(|h| h + dy),
Self::BottomRight => initial_rect.with_width(|w| w + dx).with_height(|h| h + dy),
}
}
}
impl SideOrCorner {
pub const fn mouse_icon(self) -> mouse::Interaction {
match self {
Self::Side(side) => match side {
Side::Top | Side::Bottom => mouse::Interaction::ResizingVertically,
Side::Right | Side::Left => mouse::Interaction::ResizingHorizontally,
},
Self::Corner(corner) => match corner {
Corner::TopLeft | Corner::BottomRight => mouse::Interaction::ResizingDiagonallyDown,
Corner::TopRight | Corner::BottomLeft => mouse::Interaction::ResizingDiagonallyUp,
},
}
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct Corners {
pub top_left: Point,
pub top_right: Point,
pub bottom_left: Point,
pub bottom_right: Point,
}
impl Corners {
pub fn nearest_corner(&self, point: Point) -> (Point, Corner) {
let corners = [
(self.top_left, Corner::TopLeft),
(self.top_right, Corner::TopRight),
(self.bottom_left, Corner::BottomLeft),
(self.bottom_right, Corner::BottomRight),
];
corners
.into_iter()
.min_by(|(point_a, _), (point_b, _)| {
point
.distance(*point_a)
.total_cmp(&point.distance(*point_b))
})
.expect("`corners` has 4 elements. It would only be a None if it had `0` elements")
}
pub fn render_circles(
&self,
frame: &mut iced::widget::canvas::Frame,
accent_color: iced::Color,
) {
const FRAME_CIRCLE_RADIUS: f32 = 6.0;
for circle in [
self.top_left,
self.top_right,
self.bottom_left,
self.bottom_right,
]
.map(|corner| iced::widget::canvas::Path::circle(corner, FRAME_CIRCLE_RADIUS))
{
frame.fill(&circle, accent_color);
}
}
pub fn side_at(&self, point: Point) -> Option<SideOrCorner> {
pub const FRAME_INTERACTION_AREA: f32 = 35.0;
let top = Rectangle {
x: self.top_left.x,
y: self.top_left.y - FRAME_INTERACTION_AREA / 2.,
width: self.top_right.x - self.top_left.x,
height: FRAME_INTERACTION_AREA,
};
let bottom = Rectangle {
x: self.bottom_left.x,
y: self.bottom_left.y - FRAME_INTERACTION_AREA / 2.,
width: self.bottom_right.x - self.bottom_left.x,
height: FRAME_INTERACTION_AREA,
};
let left = Rectangle {
x: self.top_left.x - FRAME_INTERACTION_AREA / 2.,
y: self.top_left.y,
width: FRAME_INTERACTION_AREA,
height: self.bottom_left.y - self.top_left.y,
};
let right = Rectangle {
x: self.top_right.x - FRAME_INTERACTION_AREA / 2.,
y: self.top_right.y,
width: FRAME_INTERACTION_AREA,
height: self.bottom_right.y - self.top_right.y,
};
let top_left = Rectangle {
x: self.top_left.x - FRAME_INTERACTION_AREA / 2.,
y: self.top_left.y - FRAME_INTERACTION_AREA / 2.,
width: FRAME_INTERACTION_AREA,
height: FRAME_INTERACTION_AREA,
};
let top_right = Rectangle {
x: self.top_right.x - FRAME_INTERACTION_AREA / 2.,
y: self.top_right.y - FRAME_INTERACTION_AREA / 2.,
width: FRAME_INTERACTION_AREA,
height: FRAME_INTERACTION_AREA,
};
let bottom_left = Rectangle {
x: self.bottom_left.x - FRAME_INTERACTION_AREA / 2.,
y: self.bottom_left.y - FRAME_INTERACTION_AREA / 2.,
width: FRAME_INTERACTION_AREA,
height: FRAME_INTERACTION_AREA,
};
let bottom_right = Rectangle {
x: self.bottom_right.x - FRAME_INTERACTION_AREA / 2.,
y: self.bottom_right.y - FRAME_INTERACTION_AREA / 2.,
width: FRAME_INTERACTION_AREA,
height: FRAME_INTERACTION_AREA,
};
[
(top_left, SideOrCorner::Corner(Corner::TopLeft)),
(top_right, SideOrCorner::Corner(Corner::TopRight)),
(bottom_left, SideOrCorner::Corner(Corner::BottomLeft)),
(bottom_right, SideOrCorner::Corner(Corner::BottomRight)),
(top, SideOrCorner::Side(Side::Top)),
(right, SideOrCorner::Side(Side::Right)),
(left, SideOrCorner::Side(Side::Left)),
(bottom, SideOrCorner::Side(Side::Bottom)),
]
.into_iter()
.find_map(|(dir, side)| dir.contains(point).then_some(side))
}
}