diff --git a/src/dpi.rs b/src/dpi.rs index 4972800f8ce..449b106e205 100644 --- a/src/dpi.rs +++ b/src/dpi.rs @@ -185,259 +185,184 @@ pub fn validate_scale_factor(scale_factor: f64) -> bool { scale_factor.is_sign_positive() && scale_factor.is_normal() } -/// A position represented in logical pixels. -/// -/// The position is stored as floats, so please be careful. Casting floats to integers truncates the -/// fractional part, which can cause noticable issues. To help with that, an `Into<(i32, i32)>` -/// implementation is provided which does the rounding for you. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct LogicalPosition<P> { - pub x: P, - pub y: P, -} - -impl<P> LogicalPosition<P> { - #[inline] - pub const fn new(x: P, y: P) -> Self { - LogicalPosition { x, y } - } -} - -impl<P: Pixel> LogicalPosition<P> { - #[inline] - pub fn from_physical<T: Into<PhysicalPosition<X>>, X: Pixel>( - physical: T, - scale_factor: f64, - ) -> Self { - physical.into().to_logical(scale_factor) - } - - #[inline] - pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<X> { - assert!(validate_scale_factor(scale_factor)); - let x = self.x.into() * scale_factor; - let y = self.y.into() * scale_factor; - PhysicalPosition::new(x, y).cast() - } - - #[inline] - pub fn cast<X: Pixel>(&self) -> LogicalPosition<X> { - LogicalPosition { - x: self.x.cast(), - y: self.y.cast(), +macro_rules! dpi_type { + ( + let a = $a:ident; + let b = $b:ident; + let mint = $mint:ident; + + $(#[$logical_meta:meta])* + pub struct $LogicalType:ident; + $(#[$physical_meta:meta])* + pub struct $PhysicalType:ident; + $(#[$unified_meta:meta])* + pub enum $UnifiedType:ident { + Physical($unified_physical:ty), + Logical($unified_logical:ty), } - } -} - -vec2_from_impls!(LogicalPosition, x, y, Point2); - -/// A position represented in physical pixels. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PhysicalPosition<P> { - pub x: P, - pub y: P, -} - -impl<P> PhysicalPosition<P> { - #[inline] - pub const fn new(x: P, y: P) -> Self { - PhysicalPosition { x, y } - } -} - -impl<P: Pixel> PhysicalPosition<P> { - #[inline] - pub fn from_logical<T: Into<LogicalPosition<X>>, X: Pixel>( - logical: T, - scale_factor: f64, - ) -> Self { - logical.into().to_physical(scale_factor) - } - - #[inline] - pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalPosition<X> { - assert!(validate_scale_factor(scale_factor)); - let x = self.x.into() / scale_factor; - let y = self.y.into() / scale_factor; - LogicalPosition::new(x, y).cast() - } - - #[inline] - pub fn cast<X: Pixel>(&self) -> PhysicalPosition<X> { - PhysicalPosition { - x: self.x.cast(), - y: self.y.cast(), + ) => { + $(#[$logical_meta])* + #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, PartialOrd, Ord)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct $LogicalType<P> { + pub $a: P, + pub $b: P, } - } -} -vec2_from_impls!(PhysicalPosition, x, y, Point2); - -/// A size represented in logical pixels. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct LogicalSize<P> { - pub width: P, - pub height: P, -} - -impl<P> LogicalSize<P> { - #[inline] - pub const fn new(width: P, height: P) -> Self { - LogicalSize { width, height } - } -} - -impl<P: Pixel> LogicalSize<P> { - #[inline] - pub fn from_physical<T: Into<PhysicalSize<X>>, X: Pixel>( - physical: T, - scale_factor: f64, - ) -> Self { - physical.into().to_logical(scale_factor) - } - - #[inline] - pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalSize<X> { - assert!(validate_scale_factor(scale_factor)); - let width = self.width.into() * scale_factor; - let height = self.height.into() * scale_factor; - PhysicalSize::new(width, height).cast() - } - - #[inline] - pub fn cast<X: Pixel>(&self) -> LogicalSize<X> { - LogicalSize { - width: self.width.cast(), - height: self.height.cast(), + impl<P> $LogicalType<P> { + #[inline] + pub const fn new($a: P, $b: P) -> Self { + $LogicalType { $a, $b } + } } - } -} - -vec2_from_impls!(LogicalSize, width, height, Vector2); -/// A size represented in physical pixels. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, PartialOrd, Ord)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PhysicalSize<P> { - pub width: P, - pub height: P, -} + impl<P: Pixel> $LogicalType<P> { + #[inline] + pub fn from_physical<T: Into<$PhysicalType<X>>, X: Pixel>( + physical: T, + scale_factor: f64, + ) -> Self { + physical.into().to_logical(scale_factor) + } -impl<P> PhysicalSize<P> { - #[inline] - pub const fn new(width: P, height: P) -> Self { - PhysicalSize { width, height } - } -} + #[inline] + pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> $PhysicalType<X> { + assert!(validate_scale_factor(scale_factor)); + let $a = self.$a.into() * scale_factor; + let $b = self.$b.into() * scale_factor; + $PhysicalType::new($a, $b).cast() + } -impl<P: Pixel> PhysicalSize<P> { - #[inline] - pub fn from_logical<T: Into<LogicalSize<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self { - logical.into().to_physical(scale_factor) - } + #[inline] + pub fn cast<X: Pixel>(&self) -> $LogicalType<X> { + $LogicalType { + $a: self.$a.cast(), + $b: self.$b.cast(), + } + } + } - #[inline] - pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalSize<X> { - assert!(validate_scale_factor(scale_factor)); - let width = self.width.into() / scale_factor; - let height = self.height.into() / scale_factor; - LogicalSize::new(width, height).cast() - } + vec2_from_impls!($LogicalType, $b, $a, $mint); - #[inline] - pub fn cast<X: Pixel>(&self) -> PhysicalSize<X> { - PhysicalSize { - width: self.width.cast(), - height: self.height.cast(), + $(#[$physical_meta])* + #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, PartialOrd, Ord)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub struct $PhysicalType<P> { + pub $a: P, + pub $b: P, } - } -} -vec2_from_impls!(PhysicalSize, width, height, Vector2); + impl<P> $PhysicalType<P> { + #[inline] + pub const fn new($a: P, $b: P) -> Self { + $PhysicalType { $a, $b } + } + } -/// A size that's either physical or logical. -#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum Size { - Physical(PhysicalSize<u32>), - Logical(LogicalSize<f64>), -} + impl<P: Pixel> $PhysicalType<P> { + #[inline] + pub fn from_logical<T: Into<$LogicalType<X>>, X: Pixel>( + logical: T, + scale_factor: f64, + ) -> Self { + logical.into().to_physical(scale_factor) + } -impl Size { - pub fn new<S: Into<Size>>(size: S) -> Size { - size.into() - } + #[inline] + pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> $LogicalType<X> { + assert!(validate_scale_factor(scale_factor)); + let $a = self.$a.into() / scale_factor; + let $b = self.$b.into() / scale_factor; + $LogicalType::new($a, $b).cast() + } - pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalSize<P> { - match *self { - Size::Physical(size) => size.to_logical(scale_factor), - Size::Logical(size) => size.cast(), + #[inline] + pub fn cast<X: Pixel>(&self) -> $PhysicalType<X> { + $PhysicalType { + $a: self.$a.cast(), + $b: self.$b.cast(), + } + } } - } - pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalSize<P> { - match *self { - Size::Physical(size) => size.cast(), - Size::Logical(size) => size.to_physical(scale_factor), - } - } -} + vec2_from_impls!($PhysicalType, $b, $a, $mint); -impl<P: Pixel> From<PhysicalSize<P>> for Size { - #[inline] - fn from(size: PhysicalSize<P>) -> Size { - Size::Physical(size.cast()) - } -} + $(#[$unified_meta])* + #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + pub enum $UnifiedType { + Physical($unified_physical), + Logical($unified_logical), + } -impl<P: Pixel> From<LogicalSize<P>> for Size { - #[inline] - fn from(size: LogicalSize<P>) -> Size { - Size::Logical(size.cast()) - } -} + impl $UnifiedType { + pub fn new<S: Into<$UnifiedType>>(val: S) -> $UnifiedType { + val.into() + } -/// A position that's either physical or logical. -#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum Position { - Physical(PhysicalPosition<i32>), - Logical(LogicalPosition<f64>), -} + pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> $LogicalType<P> { + match *self { + $UnifiedType::Physical(val) => val.to_logical(scale_factor), + $UnifiedType::Logical(val) => val.cast(), + } + } -impl Position { - pub fn new<S: Into<Position>>(position: S) -> Position { - position.into() - } + pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> $PhysicalType<P> { + match *self { + $UnifiedType::Physical(val) => val.cast(), + $UnifiedType::Logical(val) => val.to_physical(scale_factor), + } + } + } - pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalPosition<P> { - match *self { - Position::Physical(position) => position.to_logical(scale_factor), - Position::Logical(position) => position.cast(), + impl<P: Pixel> From<$PhysicalType<P>> for $UnifiedType { + #[inline] + fn from(val: $PhysicalType<P>) -> $UnifiedType { + $UnifiedType::Physical(val.cast()) + } } - } - pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<P> { - match *self { - Position::Physical(position) => position.cast(), - Position::Logical(position) => position.to_physical(scale_factor), + impl<P: Pixel> From<$LogicalType<P>> for $UnifiedType { + #[inline] + fn from(val: $LogicalType<P>) -> $UnifiedType { + $UnifiedType::Logical(val.cast()) + } } - } + }; } -impl<P: Pixel> From<PhysicalPosition<P>> for Position { - #[inline] - fn from(position: PhysicalPosition<P>) -> Position { - Position::Physical(position.cast()) +dpi_type! { + let a = x; + let b = y; + let mint = Point2; + + /// A position represented in logical pixels. + /// + /// The position is stored as floats, so please be careful. Casting floats to integers truncates the + /// fractional part, which can cause noticable issues. To help with that, an `Into<(i32, i32)>` + /// implementation is provided which does the rounding for you. + pub struct LogicalPosition; + /// A position represented in physical pixels. + pub struct PhysicalPosition; + /// A position that's either physical or logical. + pub enum Position { + Physical(PhysicalPosition<i32>), + Logical(LogicalPosition<f64>), } } -impl<P: Pixel> From<LogicalPosition<P>> for Position { - #[inline] - fn from(position: LogicalPosition<P>) -> Position { - Position::Logical(position.cast()) +dpi_type! { + let a = width; + let b = height; + let mint = Vector2; + + /// A size represented in logical pixels. + pub struct LogicalSize; + /// A size represented in physical pixels. + pub struct PhysicalSize; + /// A size that's either physical or logical. + pub enum Size { + Physical(PhysicalSize<u32>), + Logical(LogicalSize<f64>), } }