瀏覽代碼

Add `Size` and `Position` dpi types

amrbashir 9 月之前
父節點
當前提交
4ed185fa31

+ 10 - 0
.changes/api-dpi-toIPC.md

@@ -0,0 +1,10 @@
+---
+"@tauri-apps/api": "minor:feat"
+"tauri": "minor:feat"
+---
+
+Improved support for `dpi` module types to allow these types to be used without manual conversions with `invoke`:
+
+- Added `SERIALIZE_TO_IPC_FN` const in `core` module which can be used to implement custom IPC serialization for types passed to `invoke`.
+- Added `Size` and `Position` classes in `dpi` module.
+- Implementd `SERIALIZE_TO_IPC_FN` method on `PhysicalSize`, `PhysicalPosition`, `LogicalSize` and `LogicalPosition` to convert it into a valid IPC-compatible value that can be deserialized correctly on the Rust side into its equivalent struct.

+ 0 - 8
.changes/api-dpi-toJSON-toIPC.md

@@ -1,8 +0,0 @@
----
-"@tauri-apps/api": "minor:enhance"
----
-
-Improved support for `dpi` module types to allow these types to be used without manual conversions with `invoke`:
-
-- Added `toIPC`  method on `PhysicalSize`, `PhysicalPosition`, `LogicalSize` and `LogicalPosition` to convert it into a valid IPC-compatible value that can be deserialized correctly on the Rust side into its equivalent struct.
-- Implemented `toJSON`  method on `PhysicalSize`, `PhysicalPosition`, `LogicalSize` and `LogicalPosition` that calls the new `toIPC` method, so `JSON.stringify` would serialize these types correctly.

+ 5 - 0
.changes/tauri-toIPC copy.md

@@ -0,0 +1,5 @@
+---
+"tauri": "minor:feat"
+---
+
+Detect if `SERIALIZE_TO_IPC_FN`, const from the JS `core` module, is implemented on objects when serializing over IPC and use it.

File diff suppressed because it is too large
+ 0 - 0
crates/tauri/scripts/bundle.global.js


+ 154 - 117
packages/api/src/dpi.ts

@@ -56,43 +56,13 @@ class LogicalSize {
     return new PhysicalSize(this.width * scaleFactor, this.height * scaleFactor)
   }
 
-  /**
-   * Converts this size into IPC-compatible value, so it can be
-   * deserialized correctly on the Rust side using `tauri::LogicalSize` struct.
-   * @example
-   * ```typescript
-   * import { LogicalSize } from '@tauri-apps/api/dpi';
-   * import { invoke } from '@tauri-apps/api/core';
-   *
-   * const size = new LogicalSize(400, 500);
-   * await invoke("do_something_with_size", { size: size.toIPC() })
-   * ```
-   *
-   * @since 2.0.0
-   */
   [SERIALIZE_TO_IPC_FN]() {
     return {
-      Logical: {
-        width: this.width,
-        height: this.height
-      }
+      width: this.width,
+      height: this.height
     }
   }
 
-  /**
-   * Converts this size into JSON value, that can be deserialized
-   * correctly on the Rust side using `tauri::LogicalSize` struct.
-   * @example
-   * ```typescript
-   * import { LogicalSize } from '@tauri-apps/api/dpi';
-   * import { invoke } from '@tauri-apps/api/core';
-   *
-   * const size = new LogicalSize(400, 500);
-   * await invoke("do_something_with_size", { size: size.toJSON() })
-   * ```
-   *
-   * @since 2.0.0
-   */
   toJSON() {
     // eslint-disable-next-line security/detect-object-injection
     return this[SERIALIZE_TO_IPC_FN]()
@@ -147,43 +117,78 @@ class PhysicalSize {
     return new LogicalSize(this.width / scaleFactor, this.height / scaleFactor)
   }
 
-  /**
-   * Converts this size into IPC-compatible value, so it can be
-   * deserialized correctly on the Rust side using `tauri::PhysicalSize` struct.
-   * @example
-   * ```typescript
-   * import { PhysicalSize } from '@tauri-apps/api/dpi';
-   * import { invoke } from '@tauri-apps/api/core';
-   *
-   * const size = new PhysicalSize(400, 500);
-   * await invoke("do_something_with_size", { size: size.toIPC() })
-   * ```
-   *
-   * @since 2.0.0
-   */
   [SERIALIZE_TO_IPC_FN]() {
     return {
-      Physical: {
-        width: this.width,
-        height: this.height
+      width: this.width,
+      height: this.height
+    }
+  }
+
+  toJSON() {
+    // eslint-disable-next-line security/detect-object-injection
+    return this[SERIALIZE_TO_IPC_FN]()
+  }
+}
+
+/**
+ * A size represented either in physical or in logical pixels.
+ *
+ * This type is basically a union type of {@linkcode LogicalSize} and {@linkcode PhysicalSize}
+ * but comes in handy when using `tauri::Size` in Rust as an argument to a command, as this class
+ * automatically serializes into a valid format so it can be deserialized correctly into `tauri::Size`
+ *
+ * So instead of
+ * ```typescript
+ * import { invoke } from '@tauri-apps/api/core';
+ * import { LogicalSize, PhysicalSize } from '@tauri-apps/api/dpi';
+ *
+ * const size: LogicalSize | PhysicalSize = someFunction(); // where someFunction returns either LogicalSize or PhysicalSize
+ * const validSize = size instanceof LogicalSize
+ *   ? { Logical: { width: size.width, height: size.height } }
+ *   : { Physical: { width: size.width, height: size.height } }
+ * await invoke("do_something_with_size", { size: validSize });
+ * ```
+ *
+ * You can just use {@linkcode Size}
+ * ```typescript
+ * import { invoke } from '@tauri-apps/api/core';
+ * import { LogicalSize, PhysicalSize, Size } from '@tauri-apps/api/dpi';
+ *
+ * const size: LogicalSize | PhysicalSize = someFunction(); // where someFunction returns either LogicalSize or PhysicalSize
+ * const validSize = new Size(size);
+ * await invoke("do_something_with_size", { size: validSize });
+ * ```
+ *
+ * @since 2.1.0
+ */
+class Size {
+  size: LogicalSize | PhysicalSize
+
+  constructor(size: LogicalSize | PhysicalSize) {
+    this.size = size
+  }
+
+  toLogical(scaleFactor: number): LogicalSize {
+    return this.size instanceof LogicalSize
+      ? this.size
+      : this.size.toLogical(scaleFactor)
+  }
+
+  toPhysical(scaleFactor: number): PhysicalSize {
+    return this.size instanceof PhysicalSize
+      ? this.size
+      : this.size.toPhysical(scaleFactor)
+  }
+
+  [SERIALIZE_TO_IPC_FN]() {
+    return {
+      [`${this.size.type}`]: {
+        width: this.size.width,
+        height: this.size.height
       }
     }
   }
 
-  /**
-   * Converts this size into JSON value, that can be deserialized
-   * correctly on the Rust side using `tauri::PhysicalSize` struct.
-   * @example
-   * ```typescript
-   * import { PhysicalSize } from '@tauri-apps/api/dpi';
-   * import { invoke } from '@tauri-apps/api/core';
-   *
-   * const size = new PhysicalSize(400, 500);
-   * await invoke("do_something_with_size", { size: size.toJSON() })
-   * ```
-   *
-   * @since 2.0.0
-   */
   toJSON() {
     // eslint-disable-next-line security/detect-object-injection
     return this[SERIALIZE_TO_IPC_FN]()
@@ -242,43 +247,13 @@ class LogicalPosition {
     return new PhysicalPosition(this.x * scaleFactor, this.x * scaleFactor)
   }
 
-  /**
-   * Converts this position into IPC-compatible value, so it can be
-   * deserialized correctly on the Rust side using `tauri::LogicalPosition` struct.
-   * @example
-   * ```typescript
-   * import { LogicalPosition } from '@tauri-apps/api/dpi';
-   * import { invoke } from '@tauri-apps/api/core';
-   *
-   * const position = new LogicalPosition(400, 500);
-   * await invoke("do_something_with_position", { position: position.toIPC() })
-   * ```
-   *
-   * @since 2.0.0
-   */
   [SERIALIZE_TO_IPC_FN]() {
     return {
-      Logical: {
-        x: this.x,
-        y: this.y
-      }
+      x: this.x,
+      y: this.y
     }
   }
 
-  /**
-   * Converts this position into JSON value, that can be deserialized
-   * correctly on the Rust side using `tauri::LogicalPosition` struct.
-   * @example
-   * ```typescript
-   * import { LogicalPosition } from '@tauri-apps/api/dpi';
-   * import { invoke } from '@tauri-apps/api/core';
-   *
-   * const position = new LogicalPosition(400, 500);
-   * await invoke("do_something_with_position", { position: position.toJSON() })
-   * ```
-   *
-   * @since 2.0.0
-   */
   toJSON() {
     // eslint-disable-next-line security/detect-object-injection
     return this[SERIALIZE_TO_IPC_FN]()
@@ -319,45 +294,107 @@ class PhysicalPosition {
   }
 
   /**
-   * Converts this position into IPC-compatible value, so it can be
-   * deserialized correctly on the Rust side using `tauri::PhysicalPosition` struct.
+   * Converts the physical position to a logical one.
    * @example
    * ```typescript
    * import { PhysicalPosition } from '@tauri-apps/api/dpi';
-   * import { invoke } from '@tauri-apps/api/core';
+   * import { getCurrentWindow } from '@tauri-apps/api/window';
    *
+   * const appWindow = getCurrentWindow();
+   * const factor = await appWindow.scaleFactor();
    * const position = new PhysicalPosition(400, 500);
-   * await invoke("do_something_with_position", { position: position.toIPC() })
+   * const physical = position.toLogical(factor);
    * ```
    *
    * @since 2.0.0
    */
+  toLogical(scaleFactor: number): LogicalPosition {
+    return new LogicalPosition(this.x / scaleFactor, this.x / scaleFactor)
+  }
+
   [SERIALIZE_TO_IPC_FN]() {
     return {
-      Physical: {
-        x: this.x,
-        y: this.y
+      x: this.x,
+      y: this.y
+    }
+  }
+
+  toJSON() {
+    // eslint-disable-next-line security/detect-object-injection
+    return this[SERIALIZE_TO_IPC_FN]()
+  }
+}
+
+/**
+ * A position represented either in physical or in logical pixels.
+ *
+ * This type is basically a union type of {@linkcode LogicalSize} and {@linkcode PhysicalSize}
+ * but comes in handy when using `tauri::Position` in Rust as an argument to a command, as this class
+ * automatically serializes into a valid format so it can be deserialized correctly into `tauri::Position`
+ *
+ * So instead of
+ * ```typescript
+ * import { invoke } from '@tauri-apps/api/core';
+ * import { LogicalPosition, PhysicalPosition } from '@tauri-apps/api/dpi';
+ *
+ * const position: LogicalPosition | PhysicalPosition = someFunction(); // where someFunction returns either LogicalPosition or PhysicalPosition
+ * const validPosition = position instanceof LogicalPosition
+ *   ? { Logical: { x: position.x, y: position.y } }
+ *   : { Physical: { x: position.x, y: position.y } }
+ * await invoke("do_something_with_position", { position: validPosition });
+ * ```
+ *
+ * You can just use {@linkcode Position}
+ * ```typescript
+ * import { invoke } from '@tauri-apps/api/core';
+ * import { LogicalPosition, PhysicalPosition, Position } from '@tauri-apps/api/dpi';
+ *
+ * const position: LogicalPosition | PhysicalPosition = someFunction(); // where someFunction returns either LogicalPosition or PhysicalPosition
+ * const validPosition = new Position(position);
+ * await invoke("do_something_with_position", { position: validPosition });
+ * ```
+ *
+ * @since 2.1.0
+ */
+class Position {
+  position: LogicalPosition | PhysicalPosition
+
+  constructor(position: LogicalPosition | PhysicalPosition) {
+    this.position = position
+  }
+
+  toLogical(scaleFactor: number): LogicalPosition {
+    return this.position instanceof LogicalPosition
+      ? this.position
+      : this.position.toLogical(scaleFactor)
+  }
+
+  toPhysical(scaleFactor: number): PhysicalPosition {
+    return this.position instanceof PhysicalPosition
+      ? this.position
+      : this.position.toPhysical(scaleFactor)
+  }
+
+  [SERIALIZE_TO_IPC_FN]() {
+    return {
+      [`${this.position.type}`]: {
+        x: this.position.x,
+        y: this.position.y
       }
     }
   }
 
-  /**
-   * Converts this position into JSON value, that can be deserialized
-   * correctly on the Rust side using `tauri::PhysicalPosition` struct.
-   * @example
-   * ```typescript
-   * import { PhysicalPosition } from '@tauri-apps/api/dpi';
-   * import { invoke } from '@tauri-apps/api/core';
-   *
-   * const position = new PhysicalPosition(400, 500);
-   * await invoke("do_something_with_position", { position: position.toJSON() })
-   * ```
-   *
-   * @since 2.0.0
-   */
   toJSON() {
+    // eslint-disable-next-line security/detect-object-injection
     return this[SERIALIZE_TO_IPC_FN]()
   }
 }
 
-export { LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize }
+export {
+  LogicalPosition,
+  LogicalSize,
+  Size,
+  PhysicalPosition,
+  PhysicalSize,
+  Position
+}

+ 5 - 4
packages/api/src/webview.ts

@@ -18,6 +18,7 @@
 
 import { PhysicalPosition, PhysicalSize } from './dpi'
 import type { LogicalPosition, LogicalSize } from './dpi'
+import { Position, Size } from './dpi'
 import type { EventName, EventCallback, UnlistenFn } from './event'
 import {
   TauriEvent,
@@ -413,10 +414,10 @@ class Webview {
    * @param size The logical or physical size.
    * @returns A promise indicating the success or failure of the operation.
    */
-  async setSize(size: LogicalSize | PhysicalSize): Promise<void> {
+  async setSize(size: LogicalSize | PhysicalSize | Size): Promise<void> {
     return invoke('plugin:webview|set_webview_size', {
       label: this.label,
-      value: size
+      value: size instanceof Size ? size : new Size(size)
     })
   }
 
@@ -432,11 +433,11 @@ class Webview {
    * @returns A promise indicating the success or failure of the operation.
    */
   async setPosition(
-    position: LogicalPosition | PhysicalPosition
+    position: LogicalPosition | PhysicalPosition | Position
   ): Promise<void> {
     return invoke('plugin:webview|set_webview_position', {
       label: this.label,
-      value: position
+      value: position instanceof Position ? position : new Position(position)
     })
   }
 

+ 13 - 11
packages/api/src/window.ts

@@ -20,7 +20,9 @@ import {
   LogicalPosition,
   LogicalSize,
   PhysicalPosition,
-  PhysicalSize
+  PhysicalSize,
+  Position,
+  Size
 } from './dpi'
 import type { Event, EventName, EventCallback, UnlistenFn } from './event'
 import {
@@ -1268,10 +1270,10 @@ class Window {
    * @param size The logical or physical inner size.
    * @returns A promise indicating the success or failure of the operation.
    */
-  async setSize(size: LogicalSize | PhysicalSize): Promise<void> {
+  async setSize(size: LogicalSize | PhysicalSize | Size): Promise<void> {
     return invoke('plugin:window|set_size', {
       label: this.label,
-      value: size
+      value: size instanceof Size ? size : new Size(size)
     })
   }
 
@@ -1287,11 +1289,11 @@ class Window {
    * @returns A promise indicating the success or failure of the operation.
    */
   async setMinSize(
-    size: LogicalSize | PhysicalSize | null | undefined
+    size: LogicalSize | PhysicalSize | Size | null | undefined
   ): Promise<void> {
     return invoke('plugin:window|set_min_size', {
       label: this.label,
-      value: size
+      value: size instanceof Size ? size : size ? new Size(size) : null
     })
   }
 
@@ -1307,11 +1309,11 @@ class Window {
    * @returns A promise indicating the success or failure of the operation.
    */
   async setMaxSize(
-    size: LogicalSize | PhysicalSize | null | undefined
+    size: LogicalSize | PhysicalSize | Size | null | undefined
   ): Promise<void> {
     return invoke('plugin:window|set_max_size', {
       label: this.label,
-      value: size
+      value: size instanceof Size ? size : size ? new Size(size) : null
     })
   }
 
@@ -1356,11 +1358,11 @@ class Window {
    * @returns A promise indicating the success or failure of the operation.
    */
   async setPosition(
-    position: LogicalPosition | PhysicalPosition
+    position: LogicalPosition | PhysicalPosition | Position
   ): Promise<void> {
     return invoke('plugin:window|set_position', {
       label: this.label,
-      value: position
+      value: position instanceof Position ? position : new Position(position)
     })
   }
 
@@ -1527,11 +1529,11 @@ class Window {
    * @returns A promise indicating the success or failure of the operation.
    */
   async setCursorPosition(
-    position: LogicalPosition | PhysicalPosition
+    position: LogicalPosition | PhysicalPosition | Position
   ): Promise<void> {
     return invoke('plugin:window|set_cursor_position', {
       label: this.label,
-      value: position
+      value: position instanceof Position ? position : new Position(position)
     })
   }
 

Some files were not shown because too many files changed in this diff