Просмотр исходного кода

feat(core): add missing methods to the dpi module (#4393)

* feat(core): add missing methods to the dpi module

* cleanup impl
Lucas Fernandes Nogueira 3 лет назад
Родитель
Сommit
c7d13a1c60
2 измененных файлов с 303 добавлено и 66 удалено
  1. 6 0
      .changes/add-missing-dpi-methods.md
  2. 297 66
      core/tauri-runtime/src/window/dpi.rs

+ 6 - 0
.changes/add-missing-dpi-methods.md

@@ -0,0 +1,6 @@
+---
+"tauri-runtime": patch
+"tauri": patch
+---
+
+Added `fn new` constructors for `PhysicalSize`, `LogicalSize`, `PhysicalPosition` and `LogicalPosition` and missing conversion methods.

+ 297 - 66
core/tauri-runtime/src/window/dpi.rs

@@ -4,15 +4,8 @@
 
 use serde::{Deserialize, Serialize};
 
-fn validate_scale_factor(scale_factor: f64) -> bool {
-  scale_factor.is_sign_positive() && scale_factor.is_normal()
-}
-
-/// A pixel definition. Must be created from a `f64` value.
 pub trait Pixel: Copy + Into<f64> {
-  /// Creates the pixel from the `f64` value.
   fn from_f64(f: f64) -> Self;
-  /// Casts a pixel.
   fn cast<P: Pixel>(self) -> P {
     P::from_f64(self.into())
   }
@@ -23,81 +16,87 @@ impl Pixel for u8 {
     f.round() as u8
   }
 }
-
 impl Pixel for u16 {
   fn from_f64(f: f64) -> Self {
     f.round() as u16
   }
 }
-
 impl Pixel for u32 {
   fn from_f64(f: f64) -> Self {
     f.round() as u32
   }
 }
-
 impl Pixel for i8 {
   fn from_f64(f: f64) -> Self {
     f.round() as i8
   }
 }
-
 impl Pixel for i16 {
   fn from_f64(f: f64) -> Self {
     f.round() as i16
   }
 }
-
 impl Pixel for i32 {
   fn from_f64(f: f64) -> Self {
     f.round() as i32
   }
 }
-
 impl Pixel for f32 {
   fn from_f64(f: f64) -> Self {
     f as f32
   }
 }
-
 impl Pixel for f64 {
-  #[allow(clippy::wrong_self_convention)]
   fn from_f64(f: f64) -> Self {
     f
   }
 }
 
-/// A position represented in physical pixels.
+/// Checks that the scale factor is a normal positive `f64`.
+///
+/// All functions that take a scale factor assert that this will return `true`. If you're sourcing scale factors from
+/// anywhere other than tao, it's recommended to validate them using this function before passing them to tao;
+/// otherwise, you risk panics.
+#[inline]
+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 noticeable 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, Serialize, Deserialize)]
-pub struct PhysicalPosition<P> {
-  /// Vertical axis value.
+pub struct LogicalPosition<P> {
   pub x: P,
-  /// Horizontal axis value.
   pub y: P,
 }
 
-impl<P: Pixel> PhysicalPosition<P> {
-  /// Converts the physical position to a logical one, using the scale factor.
+impl<P> LogicalPosition<P> {
   #[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 { x, y }.cast()
+  pub const fn new(x: P, y: P) -> Self {
+    LogicalPosition { x, y }
   }
 }
 
-/// A position represented in logical pixels.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)]
-pub struct LogicalPosition<P> {
-  /// Vertical axis value.
-  pub x: P,
-  /// Horizontal axis value.
-  pub y: P,
-}
+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()
+  }
 
-impl<T: Pixel> LogicalPosition<T> {
-  /// Casts the logical size to another pixel type.
   #[inline]
   pub fn cast<X: Pixel>(&self) -> LogicalPosition<X> {
     LogicalPosition {
@@ -107,47 +106,122 @@ impl<T: Pixel> LogicalPosition<T> {
   }
 }
 
-/// A position that's either physical or logical.
-#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
-#[serde(tag = "type", content = "data")]
-pub enum Position {
-  /// Physical position.
-  Physical(PhysicalPosition<i32>),
-  /// Logical position.
-  Logical(LogicalPosition<f64>),
+impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalPosition<P> {
+  fn from((x, y): (X, X)) -> LogicalPosition<P> {
+    LogicalPosition::new(x.cast(), y.cast())
+  }
 }
 
-/// A size represented in physical pixels.
+impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for (X, X) {
+  fn from(pos: LogicalPosition<P>) -> Self {
+    (pos.x.cast(), pos.y.cast())
+  }
+}
+
+impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalPosition<P> {
+  fn from([x, y]: [X; 2]) -> LogicalPosition<P> {
+    LogicalPosition::new(x.cast(), y.cast())
+  }
+}
+
+impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for [X; 2] {
+  fn from(pos: LogicalPosition<P>) -> Self {
+    [pos.x.cast(), pos.y.cast()]
+  }
+}
+
+/// A position represented in physical pixels.
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)]
-pub struct PhysicalSize<T> {
-  /// Width.
-  pub width: T,
-  /// Height.
-  pub height: T,
+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<T: Pixel> PhysicalSize<T> {
-  /// Converts the physical size to a logical one, applying the scale factor.
+impl<P: Pixel> PhysicalPosition<P> {
   #[inline]
-  pub fn to_logical<X: Pixel>(self, scale_factor: f64) -> LogicalSize<X> {
+  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 width = self.width.into() / scale_factor;
-    let height = self.height.into() / scale_factor;
-    LogicalSize { width, height }.cast()
+    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(),
+    }
+  }
+}
+
+impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalPosition<P> {
+  fn from((x, y): (X, X)) -> PhysicalPosition<P> {
+    PhysicalPosition::new(x.cast(), y.cast())
+  }
+}
+
+impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for (X, X) {
+  fn from(pos: PhysicalPosition<P>) -> Self {
+    (pos.x.cast(), pos.y.cast())
+  }
+}
+
+impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalPosition<P> {
+  fn from([x, y]: [X; 2]) -> PhysicalPosition<P> {
+    PhysicalPosition::new(x.cast(), y.cast())
+  }
+}
+
+impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for [X; 2] {
+  fn from(pos: PhysicalPosition<P>) -> Self {
+    [pos.x.cast(), pos.y.cast()]
   }
 }
 
 /// A size represented in logical pixels.
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)]
-pub struct LogicalSize<T> {
-  /// Width.
-  pub width: T,
-  /// Height.
-  pub height: T,
+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<T: Pixel> LogicalSize<T> {
-  /// Casts the logical size to another pixel type.
+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 {
@@ -157,12 +231,169 @@ impl<T: Pixel> LogicalSize<T> {
   }
 }
 
+impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalSize<P> {
+  fn from((x, y): (X, X)) -> LogicalSize<P> {
+    LogicalSize::new(x.cast(), y.cast())
+  }
+}
+
+impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for (X, X) {
+  fn from(size: LogicalSize<P>) -> Self {
+    (size.width.cast(), size.height.cast())
+  }
+}
+
+impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalSize<P> {
+  fn from([x, y]: [X; 2]) -> LogicalSize<P> {
+    LogicalSize::new(x.cast(), y.cast())
+  }
+}
+
+impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for [X; 2] {
+  fn from(size: LogicalSize<P>) -> Self {
+    [size.width.cast(), size.height.cast()]
+  }
+}
+
+/// A size represented in physical pixels.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)]
+pub struct PhysicalSize<P> {
+  pub width: P,
+  pub height: P,
+}
+
+impl<P> PhysicalSize<P> {
+  #[inline]
+  pub const fn new(width: P, height: P) -> Self {
+    PhysicalSize { width, height }
+  }
+}
+
+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 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()
+  }
+
+  #[inline]
+  pub fn cast<X: Pixel>(&self) -> PhysicalSize<X> {
+    PhysicalSize {
+      width: self.width.cast(),
+      height: self.height.cast(),
+    }
+  }
+}
+
+impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalSize<P> {
+  fn from((x, y): (X, X)) -> PhysicalSize<P> {
+    PhysicalSize::new(x.cast(), y.cast())
+  }
+}
+
+impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for (X, X) {
+  fn from(size: PhysicalSize<P>) -> Self {
+    (size.width.cast(), size.height.cast())
+  }
+}
+
+impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalSize<P> {
+  fn from([x, y]: [X; 2]) -> PhysicalSize<P> {
+    PhysicalSize::new(x.cast(), y.cast())
+  }
+}
+
+impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for [X; 2] {
+  fn from(size: PhysicalSize<P>) -> Self {
+    [size.width.cast(), size.height.cast()]
+  }
+}
+
 /// A size that's either physical or logical.
 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
-#[serde(tag = "type", content = "data")]
 pub enum Size {
-  /// Physical size.
   Physical(PhysicalSize<u32>),
-  /// Logical size.
   Logical(LogicalSize<f64>),
 }
+
+impl Size {
+  pub fn new<S: Into<Size>>(size: S) -> Size {
+    size.into()
+  }
+
+  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(),
+    }
+  }
+
+  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),
+    }
+  }
+}
+
+impl<P: Pixel> From<PhysicalSize<P>> for Size {
+  #[inline]
+  fn from(size: PhysicalSize<P>) -> Size {
+    Size::Physical(size.cast())
+  }
+}
+
+impl<P: Pixel> From<LogicalSize<P>> for Size {
+  #[inline]
+  fn from(size: LogicalSize<P>) -> Size {
+    Size::Logical(size.cast())
+  }
+}
+
+/// A position that's either physical or logical.
+#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
+pub enum Position {
+  Physical(PhysicalPosition<i32>),
+  Logical(LogicalPosition<f64>),
+}
+
+impl Position {
+  pub fn new<S: Into<Position>>(position: S) -> Position {
+    position.into()
+  }
+
+  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(),
+    }
+  }
+
+  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<PhysicalPosition<P>> for Position {
+  #[inline]
+  fn from(position: PhysicalPosition<P>) -> Position {
+    Position::Physical(position.cast())
+  }
+}
+
+impl<P: Pixel> From<LogicalPosition<P>> for Position {
+  #[inline]
+  fn from(position: LogicalPosition<P>) -> Position {
+    Position::Logical(position.cast())
+  }
+}