use objc2::{Encode, Encoding, RefEncode};
#[cfg(target_pointer_width = "64")]
type InnerFloat = f64;
#[cfg(not(target_pointer_width = "64"))]
type InnerFloat = f32;
pub type CGFloat = InnerFloat;
#[cfg(all(
feature = "apple",
not(all(target_os = "macos", target_pointer_width = "32"))
))]
mod names {
pub(super) const POINT: &str = "_CGPoint";
pub(super) const SIZE: &str = "_CGSize";
pub(super) const RECT: &str = "_CGRect";
}
#[cfg(any(
feature = "gnustep-1-7",
all(target_os = "macos", target_pointer_width = "32")
))]
mod names {
pub(super) const POINT: &str = "_NSPoint";
pub(super) const SIZE: &str = "_NSSize";
pub(super) const RECT: &str = "_NSRect";
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct NSPoint {
pub x: CGFloat,
pub y: CGFloat,
}
unsafe impl Encode for NSPoint {
const ENCODING: Encoding<'static> =
Encoding::Struct(names::POINT, &[CGFloat::ENCODING, CGFloat::ENCODING]);
}
unsafe impl RefEncode for NSPoint {
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
}
impl NSPoint {
#[inline]
#[doc(alias = "NSMakePoint")]
pub const fn new(x: CGFloat, y: CGFloat) -> Self {
Self { x, y }
}
#[doc(alias = "NSZeroPoint")]
#[doc(alias = "ORIGIN")]
pub const ZERO: Self = Self::new(0.0, 0.0);
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct NSSize {
width: CGFloat,
height: CGFloat,
}
unsafe impl Encode for NSSize {
const ENCODING: Encoding<'static> =
Encoding::Struct(names::SIZE, &[CGFloat::ENCODING, CGFloat::ENCODING]);
}
unsafe impl RefEncode for NSSize {
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
}
impl NSSize {
#[inline]
#[doc(alias = "NSMakeSize")]
pub fn new(width: CGFloat, height: CGFloat) -> Self {
match (width < 0.0, height < 0.0) {
(true, true) => panic!("NSSize cannot have negative width and height"),
(true, false) => panic!("NSSize cannot have negative width"),
(false, true) => panic!("NSSize cannot have negative height"),
(false, false) => Self { width, height },
}
}
#[doc(alias = "NSZeroSize")]
pub const ZERO: Self = Self {
width: 0.0,
height: 0.0,
};
#[inline]
pub const fn width(self) -> CGFloat {
self.width
}
#[inline]
pub const fn height(self) -> CGFloat {
self.height
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct NSRect {
pub origin: NSPoint,
pub size: NSSize,
}
unsafe impl Encode for NSRect {
const ENCODING: Encoding<'static> =
Encoding::Struct(names::RECT, &[NSPoint::ENCODING, NSSize::ENCODING]);
}
unsafe impl RefEncode for NSRect {
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
}
impl NSRect {
#[inline]
#[doc(alias = "NSMakeRect")]
pub const fn new(origin: NSPoint, size: NSSize) -> Self {
Self { origin, size }
}
#[doc(alias = "NSZeroRect")]
pub const ZERO: Self = Self::new(NSPoint::ZERO, NSSize::ZERO);
#[inline]
pub fn min(self) -> NSPoint {
self.origin
}
#[inline]
pub fn mid(self) -> NSPoint {
NSPoint::new(
self.origin.x + (self.size.width * 0.5),
self.origin.y + (self.size.height * 0.5),
)
}
#[inline]
pub fn max(self) -> NSPoint {
NSPoint::new(
self.origin.x + self.size.width,
self.origin.y + self.size.height,
)
}
#[inline]
pub fn is_empty(self) -> bool {
!(self.size.width > 0.0 && self.size.height > 0.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic = "NSSize cannot have negative width and height"]
fn test_nssize_new_both_negative() {
NSSize::new(-1.0, -1.0);
}
#[test]
#[should_panic = "NSSize cannot have negative width"]
fn test_nssize_new_width_negative() {
NSSize::new(-1.0, 1.0);
}
#[test]
#[should_panic = "NSSize cannot have negative height"]
fn test_nssize_new_height_negative() {
NSSize::new(1.0, -1.0);
}
#[test]
fn test_nssize_new() {
NSSize::new(1.0, 1.0);
NSSize::new(0.0, -0.0);
NSSize::new(-0.0, 0.0);
NSSize::new(-0.0, -0.0);
}
#[test]
#[cfg(any(all(feature = "apple", target_os = "macos"), feature = "gnustep-1-7"))] fn test_partial_eq() {
use objc2::runtime::Bool;
extern "C" {
fn NSEqualPoints(a: NSPoint, b: NSPoint) -> Bool;
fn NSEqualSizes(a: NSSize, b: NSSize) -> Bool;
fn NSEqualRects(a: NSRect, b: NSRect) -> Bool;
}
let cases: &[(CGFloat, CGFloat)] = &[
(0.0, 0.0),
(-0.0, -0.0),
(0.0, -0.0),
(1.0, 1.0 + CGFloat::EPSILON),
(0.0, CGFloat::MIN_POSITIVE),
(0.0, CGFloat::EPSILON),
(1.0, 1.0),
(1.0, -1.0),
(CGFloat::INFINITY, CGFloat::INFINITY),
(CGFloat::INFINITY, CGFloat::NEG_INFINITY),
(CGFloat::NEG_INFINITY, CGFloat::NEG_INFINITY),
(CGFloat::NAN, 0.0),
(CGFloat::NAN, 1.0),
(CGFloat::NAN, CGFloat::NAN),
(CGFloat::NAN, -CGFloat::NAN),
(-CGFloat::NAN, -CGFloat::NAN),
(CGFloat::NAN, CGFloat::INFINITY),
];
for case in cases {
let point_a = NSPoint::new(case.0, case.1);
let point_b = NSPoint::new(case.0, case.1);
let actual = unsafe { NSEqualPoints(point_a, point_b).as_bool() };
assert_eq!(point_a == point_b, actual);
if case.0 >= 0.0 && case.1 >= 0.0 {
let size_a = NSSize::new(case.0, case.1);
let size_b = NSSize::new(case.0, case.1);
let actual = unsafe { NSEqualSizes(size_a, size_b).as_bool() };
assert_eq!(size_a == size_b, actual);
let rect_a = NSRect::new(point_a, size_a);
let rect_b = NSRect::new(point_b, size_b);
let actual = unsafe { NSEqualRects(rect_a, rect_b).as_bool() };
assert_eq!(rect_a == rect_b, actual);
}
}
}
}