use crate::{WindowsUtilsError, WindowsUtilsResult};
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Point {
pub x: i32,
pub y: i32,
}
impl Point {
pub const fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
pub const ORIGIN: Self = Self::new(0, 0);
pub fn is_valid(&self) -> bool {
self.x >= 0 && self.y >= 0
}
pub fn translate(&self, dx: i32, dy: i32) -> Self {
Self::new(self.x + dx, self.y + dy)
}
pub fn distance_to(&self, other: Point) -> f64 {
let dx = (other.x - self.x) as f64;
let dy = (other.y - self.y) as f64;
(dx * dx + dy * dy).sqrt()
}
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Rect {
pub origin: Point,
pub width: i32,
pub height: i32,
}
impl Rect {
pub const fn new(x: i32, y: i32, width: i32, height: i32) -> Self {
Self {
origin: Point::new(x, y),
width,
height,
}
}
pub fn from_points(top_left: Point, bottom_right: Point) -> Self {
Self {
origin: top_left,
width: bottom_right.x - top_left.x,
height: bottom_right.y - top_left.y,
}
}
pub fn left(&self) -> i32 {
self.origin.x
}
pub fn top(&self) -> i32 {
self.origin.y
}
pub fn right(&self) -> i32 {
self.origin.x + self.width
}
pub fn bottom(&self) -> i32 {
self.origin.y + self.height
}
pub fn center(&self) -> Point {
Point::new(
self.origin.x + self.width / 2,
self.origin.y + self.height / 2,
)
}
pub fn contains(&self, point: Point) -> bool {
point.x >= self.left()
&& point.x <= self.right()
&& point.y >= self.top()
&& point.y <= self.bottom()
}
pub fn area(&self) -> i32 {
self.width * self.height
}
pub fn intersects(&self, other: &Rect) -> bool {
self.left() <= other.right()
&& self.right() >= other.left()
&& self.top() <= other.bottom()
&& self.bottom() >= other.top()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct WindowStyle {
pub has_border: bool,
pub has_title_bar: bool,
pub border_width: i32,
pub title_bar_height: i32,
}
impl Default for WindowStyle {
fn default() -> Self {
Self {
has_border: true,
has_title_bar: true,
border_width: 8,
title_bar_height: 30,
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Window {
handle: isize,
window_rect: Rect,
style: WindowStyle,
}
impl Window {
pub fn new(handle: isize, window_rect: Rect, style: WindowStyle) -> Self {
Self {
handle,
window_rect,
style,
}
}
pub fn client_rect(&self) -> Rect {
let mut client = self.window_rect;
if self.style.has_title_bar {
client.origin.y += self.style.title_bar_height;
client.height -= self.style.title_bar_height;
}
if self.style.has_border {
client.origin.x += self.style.border_width;
client.origin.y += self.style.border_width;
client.width -= self.style.border_width * 2;
client.height -= self.style.border_width * 2;
}
if client.width < 0 {
client.width = 0;
}
if client.height < 0 {
client.height = 0;
}
client
}
pub fn handle(&self) -> isize {
self.handle
}
pub fn window_rect(&self) -> Rect {
self.window_rect
}
pub fn style(&self) -> WindowStyle {
self.style
}
pub fn move_to(&mut self, new_position: Point) {
self.window_rect.origin = new_position;
}
pub fn resize(&mut self, new_width: i32, new_height: i32) {
self.window_rect.width = new_width;
self.window_rect.height = new_height;
}
pub fn set_style(&mut self, style: WindowStyle) {
self.style = style;
}
}
pub trait CoordinateTransformer {
fn client_to_screen(&self, point: Point) -> WindowsUtilsResult<Point>;
fn screen_to_client(&self, point: Point) -> WindowsUtilsResult<Point>;
fn client_rect(&self) -> Rect;
fn window_rect(&self) -> Rect;
fn is_point_in_client(&self, point: Point) -> bool;
}
impl CoordinateTransformer for Window {
fn client_to_screen(&self, point: Point) -> WindowsUtilsResult<Point> {
if !point.is_valid() {
return Err(WindowsUtilsError::InvalidCoordinates {
x: point.x,
y: point.y,
});
}
let client_rect = self.client_rect();
Ok(Point::new(
client_rect.left() + point.x,
client_rect.top() + point.y,
))
}
fn screen_to_client(&self, point: Point) -> WindowsUtilsResult<Point> {
if !point.is_valid() {
return Err(WindowsUtilsError::InvalidCoordinates {
x: point.x,
y: point.y,
});
}
let client_rect = self.client_rect();
let client_point = Point::new(point.x - client_rect.left(), point.y - client_rect.top());
if client_point.x < 0
|| client_point.y < 0
|| client_point.x >= client_rect.width
|| client_point.y >= client_rect.height
{
return Err(WindowsUtilsError::OutOfBounds {
x: point.x,
y: point.y,
});
}
Ok(client_point)
}
fn client_rect(&self) -> Rect {
self.client_rect()
}
fn window_rect(&self) -> Rect {
self.window_rect
}
fn is_point_in_client(&self, point: Point) -> bool {
let client_rect = self.client_rect();
point.x >= 0 && point.y >= 0 && point.x < client_rect.width && point.y < client_rect.height
}
}