pixi-watchface-engine 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/API.md CHANGED
@@ -9,6 +9,7 @@
9
9
  5. [Plugins](#plugins)
10
10
  6. [Events](#events)
11
11
  7. [Types](#types)
12
+ 8. [Display Geometry](#display-geometry)
12
13
 
13
14
  ---
14
15
 
@@ -734,6 +735,34 @@ interface Bounds {
734
735
 
735
736
  ---
736
737
 
738
+ ## Display Geometry
739
+
740
+ Display geometry describes the face's logical shape and dimensions separately
741
+ from the editor's pixel dimensions. The geometry helpers are device-independent;
742
+ they accept circular and rectangular profiles without inferring a shape.
743
+
744
+ ```typescript
745
+ const geometry = { shape: 'rectangle', width: 448, height: 486 } as const;
746
+ const fitted = fitDisplayGeometry(geometry, {
747
+ x: 0, y: 0, width: 600, height: 600, // editor bounds, not logical face size
748
+ });
749
+ const editorPoint = displayToViewportPoint(
750
+ { x: 224, y: 243 }, geometry, fitted,
751
+ );
752
+ const hit = isPointInDisplay({ x: 0, y: 0 }, geometry); // true for rectangles
753
+ ```
754
+
755
+ `fitDisplayGeometry` centers the logical display in editor bounds and uses one
756
+ uniform scale. `displayToViewportPoint` and `viewportToDisplayPoint` convert
757
+ coordinates while preserving either the default `top-left` or `center` origin.
758
+ `isPointInDisplay` uses rectangular bounds or the circular visible boundary.
759
+ `createDisplayMask` creates a Pixi `Graphics` mask for the same shape;
760
+ `applyDisplayMask` attaches one to a container and returns it for cleanup.
761
+ Circular profiles must have equal positive width and height. Invalid dimensions
762
+ and unknown shapes throw instead of silently selecting a default.
763
+
764
+ ---
765
+
737
766
  ## Complete Example
738
767
 
739
768
  ```typescript
@@ -7,17 +7,34 @@ export interface TextElementStyle {
7
7
  fontWeight?: TextStyleFontWeight;
8
8
  align?: TextStyleAlign;
9
9
  color?: number;
10
+ lineHeight?: number | null;
11
+ letterSpacing?: number;
12
+ boxWidth?: number | null;
13
+ breakWords?: boolean;
14
+ textTransform?: TextTransform;
15
+ maxLines?: number | null;
16
+ truncationCharacter?: string;
10
17
  }
11
18
  type TextStyleAlign = 'left' | 'center' | 'right';
19
+ type TextTransform = 'none' | 'uppercase' | 'lowercase' | 'capitalize';
12
20
  export declare class TextElement extends BaseElement {
13
21
  private pixiText;
14
22
  private charSprites;
23
+ private boxProxy;
24
+ private _boxWidth;
25
+ private _boxHeight;
15
26
  private _text;
16
27
  private _fontFamily;
17
28
  private _fontSize;
18
29
  private _fontWeight;
19
30
  private _align;
20
31
  private _color;
32
+ private _lineHeight;
33
+ private _letterSpacing;
34
+ private _breakWords;
35
+ private _textTransform;
36
+ private _maxLines;
37
+ private _truncationCharacter;
21
38
  private _arcPath;
22
39
  constructor(text: string, x: number, y: number, style?: TextElementStyle);
23
40
  get arcPath(): ArcPath | null;
@@ -34,13 +51,30 @@ export declare class TextElement extends BaseElement {
34
51
  set align(value: TextStyleAlign);
35
52
  get color(): number;
36
53
  set color(value: number);
54
+ get lineHeight(): number | null;
55
+ set lineHeight(value: number | null);
56
+ get letterSpacing(): number;
57
+ set letterSpacing(value: number);
58
+ get boxWidth(): number | null;
59
+ set boxWidth(value: number | null);
60
+ get breakWords(): boolean;
61
+ set breakWords(value: boolean);
62
+ get textTransform(): TextTransform;
63
+ set textTransform(value: TextTransform);
64
+ get maxLines(): number | null;
65
+ set maxLines(value: number | null);
66
+ get truncationCharacter(): string;
67
+ set truncationCharacter(value: string);
37
68
  get width(): number;
38
- set width(_value: number);
69
+ set width(value: number);
39
70
  get height(): number;
40
- set height(_value: number);
71
+ set height(value: number);
41
72
  protected redraw(): void;
42
73
  private drawNormalText;
43
74
  private drawArcText;
75
+ private displayText;
76
+ private transformText;
77
+ private makeTextStyle;
44
78
  private makeText;
45
79
  private applyStyleToAll;
46
80
  toJSON(): SerializedElement;
@@ -1 +1 @@
1
- {"version":3,"file":"TextElement.d.ts","sourceRoot":"","sources":["../../src/elements/TextElement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE9D,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,cAAc,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElD,qBAAa,WAAY,SAAQ,WAAW;IAC1C,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,WAAW,CAAc;IAEjC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAwB;gBAE5B,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,gBAAgB;IAkBxE,IAAI,OAAO,IAAI,OAAO,GAAG,IAAI,CAA0B;IACvD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,EAKhC;IAID,IAAI,IAAI,IAAI,MAAM,CAAuB;IACzC,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAMrB;IAED,IAAI,UAAU,IAAI,MAAM,CAA6B;IACrD,IAAI,UAAU,CAAC,KAAK,EAAE,MAAM,EAM3B;IAED,IAAI,QAAQ,IAAI,MAAM,CAA2B;IACjD,IAAI,QAAQ,CAAC,KAAK,EAAE,MAAM,EAMzB;IAED,IAAI,UAAU,IAAI,mBAAmB,CAA6B;IAClE,IAAI,UAAU,CAAC,KAAK,EAAE,mBAAmB,EAMxC;IAED,IAAI,KAAK,IAAI,cAAc,CAAwB;IACnD,IAAI,KAAK,CAAC,KAAK,EAAE,cAAc,EAM9B;IAED,IAAI,KAAK,IAAI,MAAM,CAAwB;IAC3C,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAMtB;IAED,IAAa,KAAK,IAAI,MAAM,CAG3B;IACD,IAAa,KAAK,CAAC,MAAM,EAAE,MAAM,EAEhC;IAED,IAAa,MAAM,IAAI,MAAM,CAG5B;IACD,IAAa,MAAM,CAAC,MAAM,EAAE,MAAM,EAEjC;cAEkB,MAAM,IAAI,IAAI;IAQjC,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,WAAW;IAsDnB,OAAO,CAAC,QAAQ;IAmBhB,OAAO,CAAC,eAAe;IAKd,MAAM,IAAI,iBAAiB;IAa3B,SAAS,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI;IAiBjD,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,GAAG,WAAW;CAgBtD"}
1
+ {"version":3,"file":"TextElement.d.ts","sourceRoot":"","sources":["../../src/elements/TextElement.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { Container, Graphics } from 'pixi.js';
2
+ import type { Bounds, CoordinateOrigin } from '../types.ts';
3
+ /** Display shape supported by watch-face geometry. */
4
+ export type DisplayShape = 'circle' | 'rectangle';
5
+ /** Logical display dimensions, independent of an editor's pixel dimensions. */
6
+ export interface DisplayGeometry {
7
+ shape: DisplayShape;
8
+ width: number;
9
+ height: number;
10
+ }
11
+ export interface DisplayPoint {
12
+ x: number;
13
+ y: number;
14
+ }
15
+ /** A display's fitted rectangle in the editor, plus its uniform scale. */
16
+ export interface DisplayViewport extends Bounds {
17
+ scale: number;
18
+ }
19
+ /**
20
+ * Check and narrow geometry received from saved data or device integrations.
21
+ * Circular displays use a square logical coordinate space.
22
+ */
23
+ export declare function validateDisplayGeometry(value: unknown): asserts value is DisplayGeometry;
24
+ /** Return logical display bounds for the requested coordinate origin. */
25
+ export declare function getDisplayBounds(geometry: DisplayGeometry, coordinateOrigin?: CoordinateOrigin): Bounds;
26
+ /**
27
+ * Fit a logical display inside an editor-space rectangle without distortion.
28
+ * The returned bounds are centered in the editor rectangle; the editor
29
+ * rectangle is never interpreted as the face's logical dimensions.
30
+ */
31
+ export declare function fitDisplayGeometry(geometry: DisplayGeometry, editorBounds: Bounds): DisplayViewport;
32
+ /** Convert a logical face coordinate to the fitted editor viewport. */
33
+ export declare function displayToViewportPoint(point: DisplayPoint, geometry: DisplayGeometry, viewport: DisplayViewport, coordinateOrigin?: CoordinateOrigin): DisplayPoint;
34
+ /** Convert a fitted editor-space point to logical face coordinates. */
35
+ export declare function viewportToDisplayPoint(point: DisplayPoint, geometry: DisplayGeometry, viewport: DisplayViewport, coordinateOrigin?: CoordinateOrigin): DisplayPoint;
36
+ /** Test whether a logical coordinate falls inside the visible display shape. */
37
+ export declare function isPointInDisplay(point: DisplayPoint, geometry: DisplayGeometry, coordinateOrigin?: CoordinateOrigin): boolean;
38
+ /**
39
+ * Create a Pixi mask matching the visible display shape. Add the returned
40
+ * Graphics to the masked container (or a sibling in the same coordinate
41
+ * space) and assign it as that container's mask.
42
+ */
43
+ export declare function createDisplayMask(geometry: DisplayGeometry, coordinateOrigin?: CoordinateOrigin): Graphics;
44
+ /** Attach a shape-aware Pixi mask and return it for later removal/destruction. */
45
+ export declare function applyDisplayMask(target: Container, geometry: DisplayGeometry, coordinateOrigin?: CoordinateOrigin): Graphics;
46
+ //# sourceMappingURL=DisplayGeometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DisplayGeometry.d.ts","sourceRoot":"","sources":["../../src/geometry/DisplayGeometry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE5D,sDAAsD;AACtD,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,WAAW,CAAC;AAElD,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,0EAA0E;AAC1E,MAAM,WAAW,eAAgB,SAAQ,MAAM;IAC7C,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,eAAe,CAkBxF;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,eAAe,EACzB,gBAAgB,GAAE,gBAA6B,GAC9C,MAAM,CASR;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,eAAe,EACzB,YAAY,EAAE,MAAM,GACnB,eAAe,CAcjB;AAED,uEAAuE;AACvE,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,eAAe,EACzB,QAAQ,EAAE,eAAe,EACzB,gBAAgB,GAAE,gBAA6B,GAC9C,YAAY,CASd;AAED,uEAAuE;AACvE,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,eAAe,EACzB,QAAQ,EAAE,eAAe,EACzB,gBAAgB,GAAE,gBAA6B,GAC9C,YAAY,CASd;AAED,gFAAgF;AAChF,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,eAAe,EACzB,gBAAgB,GAAE,gBAA6B,GAC9C,OAAO,CAcT;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,eAAe,EACzB,gBAAgB,GAAE,gBAA6B,GAC9C,QAAQ,CAUV;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,QAAQ,EAAE,eAAe,EACzB,gBAAgB,GAAE,gBAA6B,GAC9C,QAAQ,CAKV"}
package/dist/index.d.ts CHANGED
@@ -25,6 +25,8 @@ export { ZOrderManager } from "./managers/ZOrderManager.ts";
25
25
  export { SelectionManager } from "./selection/SelectionManager.ts";
26
26
  export { SerializationManager } from "./managers/SerializationManager.ts";
27
27
  export type { FillDirection, ShapeType, LineCap, ArcPath, ElementType, CoordinateOrigin, SerializedElement, WatchfaceState, UndoableAction, HandleConfig, Bounds, } from "./types.ts";
28
+ export { applyDisplayMask, createDisplayMask, displayToViewportPoint, fitDisplayGeometry, getDisplayBounds, isPointInDisplay, validateDisplayGeometry, viewportToDisplayPoint, } from "./geometry/DisplayGeometry.ts";
29
+ export type { DisplayGeometry, DisplayPoint, DisplayShape, DisplayViewport, } from "./geometry/DisplayGeometry.ts";
28
30
  export { EventBus } from "./EventBus.ts";
29
31
  export type { EngineEventMap } from "./EventBus.ts";
30
32
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAGnE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAGhE,YAAY,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,YAAY,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AAGvF,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAG1E,YAAY,EACV,aAAa,EACb,SAAS,EACT,OAAO,EACP,OAAO,EACP,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,YAAY,EACZ,MAAM,GACP,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAGnE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAGhE,YAAY,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,YAAY,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AAGvF,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAG1E,YAAY,EACV,aAAa,EACb,SAAS,EACT,OAAO,EACP,OAAO,EACP,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,YAAY,EACZ,MAAM,GACP,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -30734,6 +30734,7 @@ init_textureFrom();
30734
30734
  init_Container();
30735
30735
  init_Graphics();
30736
30736
  init_Sprite();
30737
+ init_CanvasTextMetrics();
30737
30738
  init_TextStyle();
30738
30739
  init_Ticker();
30739
30740
  init_eventemitter3();
@@ -31101,12 +31102,21 @@ class BaseElement {
31101
31102
  class TextElement extends BaseElement {
31102
31103
  pixiText;
31103
31104
  charSprites = [];
31105
+ boxProxy;
31106
+ _boxWidth = null;
31107
+ _boxHeight = null;
31104
31108
  _text;
31105
31109
  _fontFamily;
31106
31110
  _fontSize;
31107
31111
  _fontWeight;
31108
31112
  _align;
31109
31113
  _color;
31114
+ _lineHeight;
31115
+ _letterSpacing;
31116
+ _breakWords;
31117
+ _textTransform;
31118
+ _maxLines;
31119
+ _truncationCharacter;
31110
31120
  _arcPath = null;
31111
31121
  constructor(text, x2, y2, style) {
31112
31122
  super("text");
@@ -31116,10 +31126,20 @@ class TextElement extends BaseElement {
31116
31126
  this._fontWeight = style?.fontWeight ?? "normal";
31117
31127
  this._align = style?.align ?? "left";
31118
31128
  this._color = style?.color ?? 16777215;
31119
- this.pixiText = this.makeText(this._text);
31129
+ this._lineHeight = style?.lineHeight ?? null;
31130
+ this._letterSpacing = style?.letterSpacing ?? 0;
31131
+ this._boxWidth = style?.boxWidth ?? null;
31132
+ this._breakWords = style?.breakWords ?? false;
31133
+ this._textTransform = style?.textTransform ?? "none";
31134
+ this._maxLines = style?.maxLines ?? null;
31135
+ this._truncationCharacter = style?.truncationCharacter ?? "…";
31136
+ this.pixiText = this.makeText(this.displayText(this._text));
31120
31137
  this.container.addChild(this.pixiText);
31138
+ this.boxProxy = new Graphics;
31139
+ this.container.addChild(this.boxProxy);
31121
31140
  this.x = x2;
31122
31141
  this.y = y2;
31142
+ this.redraw();
31123
31143
  }
31124
31144
  get arcPath() {
31125
31145
  return this._arcPath;
@@ -31190,6 +31210,7 @@ class TextElement extends BaseElement {
31190
31210
  this._align = value;
31191
31211
  if (this.pixiText)
31192
31212
  this.pixiText.style.align = value;
31213
+ this.redraw();
31193
31214
  this.emitChange("align", old, value);
31194
31215
  }
31195
31216
  get color() {
@@ -31205,18 +31226,124 @@ class TextElement extends BaseElement {
31205
31226
  });
31206
31227
  this.emitChange("color", old, value);
31207
31228
  }
31229
+ get lineHeight() {
31230
+ return this._lineHeight;
31231
+ }
31232
+ set lineHeight(value) {
31233
+ const old = this._lineHeight;
31234
+ if (old === value)
31235
+ return;
31236
+ this._lineHeight = value;
31237
+ this.applyStyleToAll((s2) => {
31238
+ if (value != null)
31239
+ s2.lineHeight = value;
31240
+ });
31241
+ this.redraw();
31242
+ this.emitChange("lineHeight", old, value);
31243
+ }
31244
+ get letterSpacing() {
31245
+ return this._letterSpacing;
31246
+ }
31247
+ set letterSpacing(value) {
31248
+ const old = this._letterSpacing;
31249
+ if (old === value)
31250
+ return;
31251
+ this._letterSpacing = value;
31252
+ this.applyStyleToAll((s2) => {
31253
+ s2.letterSpacing = value;
31254
+ });
31255
+ this.emitChange("letterSpacing", old, value);
31256
+ }
31257
+ get boxWidth() {
31258
+ return this._boxWidth;
31259
+ }
31260
+ set boxWidth(value) {
31261
+ const old = this._boxWidth;
31262
+ if (old === value)
31263
+ return;
31264
+ this._boxWidth = value;
31265
+ this.applyStyleToAll((s2) => {
31266
+ s2.wordWrap = value != null;
31267
+ s2.wordWrapWidth = value ?? 100;
31268
+ });
31269
+ this.emitChange("boxWidth", old, value);
31270
+ }
31271
+ get breakWords() {
31272
+ return this._breakWords;
31273
+ }
31274
+ set breakWords(value) {
31275
+ const old = this._breakWords;
31276
+ if (old === value)
31277
+ return;
31278
+ this._breakWords = value;
31279
+ this.applyStyleToAll((s2) => {
31280
+ s2.breakWords = value;
31281
+ });
31282
+ this.emitChange("breakWords", old, value);
31283
+ }
31284
+ get textTransform() {
31285
+ return this._textTransform;
31286
+ }
31287
+ set textTransform(value) {
31288
+ const old = this._textTransform;
31289
+ if (old === value)
31290
+ return;
31291
+ this._textTransform = value;
31292
+ this.redraw();
31293
+ this.emitChange("textTransform", old, value);
31294
+ }
31295
+ get maxLines() {
31296
+ return this._maxLines;
31297
+ }
31298
+ set maxLines(value) {
31299
+ const old = this._maxLines;
31300
+ if (old === value)
31301
+ return;
31302
+ this._maxLines = value;
31303
+ this.redraw();
31304
+ this.emitChange("maxLines", old, value);
31305
+ }
31306
+ get truncationCharacter() {
31307
+ return this._truncationCharacter;
31308
+ }
31309
+ set truncationCharacter(value) {
31310
+ const old = this._truncationCharacter;
31311
+ if (old === value)
31312
+ return;
31313
+ this._truncationCharacter = value;
31314
+ this.redraw();
31315
+ this.emitChange("truncationCharacter", old, value);
31316
+ }
31208
31317
  get width() {
31209
31318
  if (this._arcPath)
31210
31319
  return this._arcPath.radius * 2;
31211
- return this.pixiText?.width ?? 0;
31320
+ return this._boxWidth ?? this.pixiText?.width ?? 0;
31321
+ }
31322
+ set width(value) {
31323
+ if (this._arcPath)
31324
+ return;
31325
+ const old = this.width;
31326
+ if (old === value)
31327
+ return;
31328
+ this._boxWidth = value;
31329
+ this.redraw();
31330
+ this.emitChange("width", old, value);
31212
31331
  }
31213
- set width(_value) {}
31214
31332
  get height() {
31215
31333
  if (this._arcPath)
31216
31334
  return this._arcPath.radius * 2;
31217
- return this.pixiText?.height ?? 0;
31335
+ return this._boxHeight ?? this.pixiText?.height ?? 0;
31336
+ }
31337
+ set height(value) {
31338
+ if (this._arcPath)
31339
+ return;
31340
+ const old = this.height;
31341
+ if (old === value)
31342
+ return;
31343
+ this._boxHeight = value;
31344
+ this.redraw();
31345
+ this.emitChange("height", old, value);
31218
31346
  }
31219
- set height(_value) {}
31220
31347
  redraw() {
31221
31348
  if (this._arcPath) {
31222
31349
  this.drawArcText();
@@ -31231,15 +31358,25 @@ class TextElement extends BaseElement {
31231
31358
  }
31232
31359
  this.charSprites = [];
31233
31360
  if (!this.pixiText) {
31234
- this.pixiText = this.makeText(this._text);
31361
+ this.pixiText = this.makeText(this.displayText(this._text));
31235
31362
  this.container.addChild(this.pixiText);
31236
31363
  } else {
31237
- this.pixiText.text = this._text;
31364
+ this.pixiText.text = this.displayText(this._text);
31238
31365
  this.pixiText.visible = true;
31239
31366
  }
31367
+ const text = this.pixiText;
31368
+ const bw = this._boxWidth ?? text.width;
31369
+ const bh = this._boxHeight ?? text.height;
31370
+ this.boxProxy.clear();
31371
+ this.boxProxy.rect(0, 0, bw, bh);
31372
+ this.boxProxy.fill({ color: 16777215, alpha: 0 });
31373
+ text.position.y = bh / 2;
31374
+ const tw = text.width;
31375
+ text.position.x = this._align === "left" ? tw / 2 : this._align === "right" ? bw - tw / 2 : bw / 2;
31240
31376
  }
31241
31377
  drawArcText() {
31242
31378
  const arc = this._arcPath;
31379
+ this.boxProxy.clear();
31243
31380
  if (this.pixiText) {
31244
31381
  this.pixiText.visible = false;
31245
31382
  }
@@ -31271,17 +31408,61 @@ class TextElement extends BaseElement {
31271
31408
  angle += halfAngular * Math.sign(span);
31272
31409
  }
31273
31410
  }
31411
+ displayText(value) {
31412
+ const transformed = this.transformText(value);
31413
+ if (!this._maxLines || this._maxLines <= 0)
31414
+ return transformed;
31415
+ const style = this.makeTextStyle();
31416
+ const lines = CanvasTextMetrics.measureText(transformed, style).lines;
31417
+ const maxLines = Math.floor(this._maxLines);
31418
+ if (lines.length <= maxLines)
31419
+ return transformed;
31420
+ const visibleLines = lines.slice(0, maxLines);
31421
+ let lastLine = visibleLines[visibleLines.length - 1]?.replace(/\s+$/, "") ?? "";
31422
+ if (this._boxWidth != null) {
31423
+ while (lastLine && CanvasTextMetrics.measureText(`${lastLine}${this._truncationCharacter}`, style, undefined, false).width > this._boxWidth) {
31424
+ lastLine = lastLine.slice(0, -1);
31425
+ }
31426
+ }
31427
+ visibleLines[visibleLines.length - 1] = `${lastLine}${this._truncationCharacter}`;
31428
+ return visibleLines.join(`
31429
+ `);
31430
+ }
31431
+ transformText(value) {
31432
+ if (this._textTransform === "uppercase")
31433
+ return value.toUpperCase();
31434
+ if (this._textTransform === "lowercase")
31435
+ return value.toLowerCase();
31436
+ if (this._textTransform === "capitalize") {
31437
+ return value.replace(/\p{L}[\p{L}\p{M}'’-]*/gu, (word) => {
31438
+ const first = word[0];
31439
+ return first ? first.toUpperCase() + word.slice(1).toLowerCase() : word;
31440
+ });
31441
+ }
31442
+ return value;
31443
+ }
31444
+ makeTextStyle() {
31445
+ const style = new TextStyle({
31446
+ fontFamily: this._fontFamily,
31447
+ fontSize: this._fontSize,
31448
+ fontWeight: this._fontWeight,
31449
+ fill: this._color,
31450
+ align: this._align,
31451
+ letterSpacing: this._letterSpacing,
31452
+ wordWrap: this._boxWidth != null,
31453
+ wordWrapWidth: this._boxWidth ?? 100,
31454
+ breakWords: this._breakWords
31455
+ });
31456
+ if (this._lineHeight != null)
31457
+ style.lineHeight = this._lineHeight;
31458
+ return style;
31459
+ }
31274
31460
  makeText(content) {
31461
+ const style = this.makeTextStyle();
31275
31462
  return new Text({
31276
31463
  text: content,
31277
31464
  anchor: 0.5,
31278
- style: new TextStyle({
31279
- fontFamily: this._fontFamily,
31280
- fontSize: this._fontSize,
31281
- fontWeight: this._fontWeight,
31282
- fill: this._color,
31283
- align: this._align
31284
- })
31465
+ style
31285
31466
  });
31286
31467
  }
31287
31468
  applyStyleToAll(mutate) {
@@ -31299,6 +31480,13 @@ class TextElement extends BaseElement {
31299
31480
  fontWeight: this._fontWeight,
31300
31481
  align: this._align,
31301
31482
  color: this._color,
31483
+ lineHeight: this._lineHeight,
31484
+ letterSpacing: this._letterSpacing,
31485
+ boxWidth: this._boxWidth,
31486
+ breakWords: this._breakWords,
31487
+ textTransform: this._textTransform,
31488
+ maxLines: this._maxLines,
31489
+ truncationCharacter: this._truncationCharacter,
31302
31490
  arcPath: this._arcPath ?? undefined
31303
31491
  };
31304
31492
  }
@@ -31315,8 +31503,26 @@ class TextElement extends BaseElement {
31315
31503
  this.align = data.align;
31316
31504
  if (data.color !== undefined)
31317
31505
  this.color = data.color;
31506
+ if (data.lineHeight !== undefined)
31507
+ this.lineHeight = data.lineHeight;
31508
+ if (data.letterSpacing !== undefined)
31509
+ this.letterSpacing = data.letterSpacing;
31510
+ if (data.boxWidth !== undefined)
31511
+ this.boxWidth = data.boxWidth;
31512
+ if (data.breakWords !== undefined)
31513
+ this.breakWords = data.breakWords;
31514
+ if (data.textTransform !== undefined)
31515
+ this.textTransform = data.textTransform;
31516
+ if (data.maxLines !== undefined)
31517
+ this.maxLines = data.maxLines;
31518
+ if (data.truncationCharacter !== undefined)
31519
+ this.truncationCharacter = data.truncationCharacter;
31318
31520
  if ("arcPath" in data)
31319
31521
  this.arcPath = data.arcPath ?? null;
31522
+ if (data.width !== undefined)
31523
+ this.width = data.width;
31524
+ if (data.height !== undefined)
31525
+ this.height = data.height;
31320
31526
  this.x = data.x;
31321
31527
  this.y = data.y;
31322
31528
  this.rotation = data.rotation;
@@ -31331,10 +31537,21 @@ class TextElement extends BaseElement {
31331
31537
  fontSize: data.fontSize,
31332
31538
  fontWeight: data.fontWeight,
31333
31539
  align: data.align,
31334
- color: data.color
31540
+ color: data.color,
31541
+ lineHeight: data.lineHeight,
31542
+ letterSpacing: data.letterSpacing,
31543
+ boxWidth: data.boxWidth,
31544
+ breakWords: data.breakWords,
31545
+ textTransform: data.textTransform,
31546
+ maxLines: data.maxLines,
31547
+ truncationCharacter: data.truncationCharacter
31335
31548
  });
31336
31549
  if (data.arcPath)
31337
31550
  el.arcPath = data.arcPath;
31551
+ if (data.width !== undefined)
31552
+ el.width = data.width;
31553
+ if (data.height !== undefined)
31554
+ el.height = data.height;
31338
31555
  el.rotation = data.rotation;
31339
31556
  el.zIndex = data.zIndex;
31340
31557
  el.visible = data.visible;
@@ -34579,7 +34796,131 @@ class AlignmentGuidesPlugin {
34579
34796
  this.graphics.destroy();
34580
34797
  }
34581
34798
  }
34799
+ // src/geometry/DisplayGeometry.ts
34800
+ function validateDisplayGeometry(value) {
34801
+ if (typeof value !== "object" || value === null) {
34802
+ throw new TypeError("Display geometry must be an object.");
34803
+ }
34804
+ const geometry = value;
34805
+ if (geometry.shape !== "circle" && geometry.shape !== "rectangle") {
34806
+ throw new TypeError(`Unsupported display shape: ${String(geometry.shape)}.`);
34807
+ }
34808
+ if (typeof geometry.width !== "number" || !Number.isFinite(geometry.width) || geometry.width <= 0) {
34809
+ throw new RangeError("Display geometry width must be a positive finite number.");
34810
+ }
34811
+ if (typeof geometry.height !== "number" || !Number.isFinite(geometry.height) || geometry.height <= 0) {
34812
+ throw new RangeError("Display geometry height must be a positive finite number.");
34813
+ }
34814
+ if (geometry.shape === "circle" && geometry.width !== geometry.height) {
34815
+ throw new RangeError("Circular display geometry must have equal width and height.");
34816
+ }
34817
+ }
34818
+ function getDisplayBounds(geometry, coordinateOrigin = "top-left") {
34819
+ validateDisplayGeometry(geometry);
34820
+ validateCoordinateOrigin(coordinateOrigin);
34821
+ return {
34822
+ x: coordinateOrigin === "center" ? -geometry.width / 2 : 0,
34823
+ y: coordinateOrigin === "center" ? -geometry.height / 2 : 0,
34824
+ width: geometry.width,
34825
+ height: geometry.height
34826
+ };
34827
+ }
34828
+ function fitDisplayGeometry(geometry, editorBounds) {
34829
+ validateDisplayGeometry(geometry);
34830
+ validateBounds(editorBounds, "Editor bounds");
34831
+ const scale = Math.min(editorBounds.width / geometry.width, editorBounds.height / geometry.height);
34832
+ const width = geometry.width * scale;
34833
+ const height = geometry.height * scale;
34834
+ return {
34835
+ x: editorBounds.x + (editorBounds.width - width) / 2,
34836
+ y: editorBounds.y + (editorBounds.height - height) / 2,
34837
+ width,
34838
+ height,
34839
+ scale
34840
+ };
34841
+ }
34842
+ function displayToViewportPoint(point, geometry, viewport, coordinateOrigin = "top-left") {
34843
+ validateDisplayGeometry(geometry);
34844
+ validateViewport(viewport);
34845
+ validatePoint(point, "Display point");
34846
+ const bounds = getDisplayBounds(geometry, coordinateOrigin);
34847
+ return {
34848
+ x: viewport.x + (point.x - bounds.x) * viewport.scale,
34849
+ y: viewport.y + (point.y - bounds.y) * viewport.scale
34850
+ };
34851
+ }
34852
+ function viewportToDisplayPoint(point, geometry, viewport, coordinateOrigin = "top-left") {
34853
+ validateDisplayGeometry(geometry);
34854
+ validateViewport(viewport);
34855
+ validatePoint(point, "Viewport point");
34856
+ const bounds = getDisplayBounds(geometry, coordinateOrigin);
34857
+ return {
34858
+ x: bounds.x + (point.x - viewport.x) / viewport.scale,
34859
+ y: bounds.y + (point.y - viewport.y) / viewport.scale
34860
+ };
34861
+ }
34862
+ function isPointInDisplay(point, geometry, coordinateOrigin = "top-left") {
34863
+ validateDisplayGeometry(geometry);
34864
+ validatePoint(point, "Display point");
34865
+ const bounds = getDisplayBounds(geometry, coordinateOrigin);
34866
+ if (geometry.shape === "rectangle") {
34867
+ return point.x >= bounds.x && point.x <= bounds.x + bounds.width && point.y >= bounds.y && point.y <= bounds.y + bounds.height;
34868
+ }
34869
+ const centerX = bounds.x + bounds.width / 2;
34870
+ const centerY = bounds.y + bounds.height / 2;
34871
+ const radius = geometry.width / 2;
34872
+ return (point.x - centerX) ** 2 + (point.y - centerY) ** 2 <= radius ** 2;
34873
+ }
34874
+ function createDisplayMask(geometry, coordinateOrigin = "top-left") {
34875
+ const bounds = getDisplayBounds(geometry, coordinateOrigin);
34876
+ const mask = new Graphics;
34877
+ if (geometry.shape === "circle") {
34878
+ mask.circle(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2, bounds.width / 2);
34879
+ } else {
34880
+ mask.rect(bounds.x, bounds.y, bounds.width, bounds.height);
34881
+ }
34882
+ mask.fill({ color: 16777215 });
34883
+ return mask;
34884
+ }
34885
+ function applyDisplayMask(target, geometry, coordinateOrigin = "top-left") {
34886
+ const mask = createDisplayMask(geometry, coordinateOrigin);
34887
+ target.addChild(mask);
34888
+ target.mask = mask;
34889
+ return mask;
34890
+ }
34891
+ function validateCoordinateOrigin(value) {
34892
+ if (value !== "top-left" && value !== "center") {
34893
+ throw new TypeError(`Unsupported coordinate origin: ${String(value)}.`);
34894
+ }
34895
+ }
34896
+ function validatePoint(point, label) {
34897
+ if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) {
34898
+ throw new RangeError(`${label} coordinates must be finite numbers.`);
34899
+ }
34900
+ }
34901
+ function validateBounds(bounds, label) {
34902
+ if (!Number.isFinite(bounds.x) || !Number.isFinite(bounds.y)) {
34903
+ throw new RangeError(`${label} position must use finite numbers.`);
34904
+ }
34905
+ if (!Number.isFinite(bounds.width) || bounds.width <= 0 || !Number.isFinite(bounds.height) || bounds.height <= 0) {
34906
+ throw new RangeError(`${label} width and height must be positive finite numbers.`);
34907
+ }
34908
+ }
34909
+ function validateViewport(viewport) {
34910
+ validateBounds(viewport, "Viewport bounds");
34911
+ if (!Number.isFinite(viewport.scale) || viewport.scale <= 0) {
34912
+ throw new RangeError("Viewport scale must be a positive finite number.");
34913
+ }
34914
+ }
34582
34915
  export {
34916
+ viewportToDisplayPoint,
34917
+ validateDisplayGeometry,
34918
+ isPointInDisplay,
34919
+ getDisplayBounds,
34920
+ fitDisplayGeometry,
34921
+ displayToViewportPoint,
34922
+ createDisplayMask,
34923
+ applyDisplayMask,
34583
34924
  ZOrderManager,
34584
34925
  WatchfaceEngine,
34585
34926
  ViewportPlugin,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixi-watchface-engine",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
4
4
  "description": "A PixiJS v8 watchface editor engine with visual editor, plugin system, and intuitive API",
5
5
  "author": "Eric Schmar (ericschmar@gmail.com)",
6
6
  "type": "module",
@@ -20,6 +20,7 @@
20
20
  "LICENSE"
21
21
  ],
22
22
  "scripts": {
23
+ "test": "bun test",
23
24
  "typecheck": "tsc --noEmit",
24
25
  "build": "bun build src/index.ts --outdir dist --target browser && tsc --emitDeclarationOnly --declaration",
25
26
  "prepublishOnly": "bun run typecheck && bun run build"