drawnui-react 0.1.0-preview.2 → 0.1.0-preview.21

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.
Files changed (87) hide show
  1. package/README.md +17 -4
  2. package/SKIPPED.md +33 -27
  3. package/dist/controls/AnimatedFramesRenderer.d.ts +7 -0
  4. package/dist/controls/AnimatedFramesRenderer.js +16 -1
  5. package/dist/controls/DrawnGame.d.ts +40 -0
  6. package/dist/controls/DrawnGame.js +64 -0
  7. package/dist/controls/GridStructure.d.ts +16 -2
  8. package/dist/controls/GridStructure.js +40 -4
  9. package/dist/controls/SkiaBackdrop.d.ts +8 -2
  10. package/dist/controls/SkiaBackdrop.js +48 -8
  11. package/dist/controls/SkiaButton.d.ts +19 -2
  12. package/dist/controls/SkiaButton.js +41 -11
  13. package/dist/controls/SkiaCarousel.d.ts +9 -1
  14. package/dist/controls/SkiaCarousel.js +16 -3
  15. package/dist/controls/SkiaCheckbox.d.ts +0 -1
  16. package/dist/controls/SkiaCheckbox.js +0 -6
  17. package/dist/controls/SkiaDrawer.d.ts +2 -0
  18. package/dist/controls/SkiaDrawer.js +2 -0
  19. package/dist/controls/SkiaEditor.d.ts +12 -1
  20. package/dist/controls/SkiaEditor.js +46 -19
  21. package/dist/controls/SkiaGif.d.ts +3 -0
  22. package/dist/controls/SkiaGif.js +12 -2
  23. package/dist/controls/SkiaImage.js +15 -10
  24. package/dist/controls/SkiaLabel.d.ts +54 -3
  25. package/dist/controls/SkiaLabel.js +235 -52
  26. package/dist/controls/SkiaLabelFps.d.ts +16 -0
  27. package/dist/controls/SkiaLabelFps.js +41 -0
  28. package/dist/controls/SkiaLayout.d.ts +64 -7
  29. package/dist/controls/SkiaLayout.js +316 -19
  30. package/dist/controls/SkiaLottie.js +4 -2
  31. package/dist/controls/SkiaProgress.d.ts +2 -1
  32. package/dist/controls/SkiaProgress.js +6 -3
  33. package/dist/controls/SkiaRadioButton.js +4 -3
  34. package/dist/controls/SkiaScroll.d.ts +35 -5
  35. package/dist/controls/SkiaScroll.js +233 -23
  36. package/dist/controls/SkiaScrollBar.d.ts +27 -0
  37. package/dist/controls/SkiaScrollBar.js +51 -2
  38. package/dist/controls/SkiaShaderCarousel.d.ts +5 -0
  39. package/dist/controls/SkiaShaderCarousel.js +5 -0
  40. package/dist/controls/SkiaShape.d.ts +13 -2
  41. package/dist/controls/SkiaShape.js +50 -10
  42. package/dist/controls/SkiaSlider.d.ts +20 -2
  43. package/dist/controls/SkiaSlider.js +59 -5
  44. package/dist/controls/SkiaSprite.d.ts +1 -0
  45. package/dist/controls/SkiaSprite.js +4 -0
  46. package/dist/controls/SkiaSvg.js +1 -1
  47. package/dist/controls/SkiaSwitch.d.ts +0 -1
  48. package/dist/controls/SkiaSwitch.js +0 -6
  49. package/dist/controls/SkiaToggle.d.ts +25 -5
  50. package/dist/controls/SkiaToggle.js +58 -5
  51. package/dist/controls/SnappingLayout.d.ts +12 -1
  52. package/dist/controls/SnappingLayout.js +19 -1
  53. package/dist/core/Accessibility.d.ts +80 -0
  54. package/dist/core/Accessibility.js +365 -8
  55. package/dist/core/Animators.d.ts +37 -1
  56. package/dist/core/Animators.js +102 -9
  57. package/dist/core/Canvas.d.ts +108 -7
  58. package/dist/core/Canvas.js +363 -35
  59. package/dist/core/Easing.d.ts +12 -1
  60. package/dist/core/Easing.js +25 -1
  61. package/dist/core/Gestures.d.ts +48 -2
  62. package/dist/core/Gestures.js +95 -3
  63. package/dist/core/Hover.d.ts +47 -0
  64. package/dist/core/Hover.js +134 -0
  65. package/dist/core/KeyboardManager.d.ts +13 -0
  66. package/dist/core/KeyboardManager.js +18 -0
  67. package/dist/core/ScrollAnimators.js +9 -0
  68. package/dist/core/SkiaControl.d.ts +216 -7
  69. package/dist/core/SkiaControl.js +516 -59
  70. package/dist/core/SkiaEffect.d.ts +17 -0
  71. package/dist/core/SkiaEffect.js +43 -8
  72. package/dist/core/SkiaImageManager.js +1 -1
  73. package/dist/core/Styles.d.ts +51 -0
  74. package/dist/core/Styles.js +92 -0
  75. package/dist/core/Super.d.ts +17 -1
  76. package/dist/core/Super.js +35 -4
  77. package/dist/core/ViewsAdapter.d.ts +7 -2
  78. package/dist/core/ViewsAdapter.js +10 -3
  79. package/dist/index.d.ts +4 -0
  80. package/dist/index.js +4 -0
  81. package/dist/react/SkiaShell.js +33 -14
  82. package/dist/react/index.d.ts +15 -4
  83. package/dist/react/index.js +102 -15
  84. package/dist/react/reconciler.js +34 -12
  85. package/dist/vite/index.d.ts +9 -0
  86. package/dist/vite/index.js +36 -3
  87. package/package.json +33 -3
@@ -9,7 +9,7 @@ export declare class SKPoint {
9
9
  }
10
10
  /** Raw platform action (subset of TouchActionType). */
11
11
  export type TouchActionType = "Pressed" | "Moved" | "Released" | "Cancelled" | "Wheel" | "Pointer";
12
- /** Recognized gesture (TouchActionResult). LongPressing/Pointer/Touch declared for parity, not produced yet. */
12
+ /** Recognized gesture (TouchActionResult). LongPressing after Canvas.LongPressTimeMs held still; Pointer / Touch declared for parity, not produced. */
13
13
  export type TouchActionResult = "Touch" | "Down" | "Up" | "Tapped" | "LongPressing" | "Panning" | "Wheel" | "Pointer";
14
14
  export type GesturesMode = "Disabled" | "Enabled" | "Lock";
15
15
  export type LockTouch = "Disabled" | "Enabled" | "PassNone" | "PassTap" | "PassTapAndLongPress";
@@ -21,9 +21,44 @@ export declare class DistanceInfo {
21
21
  Total: SKPoint;
22
22
  Velocity: SKPoint;
23
23
  }
24
+ /**
25
+ * The last positions of a press with their times (DrawnUi.Rust gestures `Trail`, 6d525f7). Measured only against the
26
+ * previous move, irregular delivery made the velocity worthless: moves handled in one burst (queued pointer events
27
+ * handed over back to back by a busy main thread, WSLg pairs 0.01 ms apart) gave millions of px/s and every fling at
28
+ * the limit. Moves under BurstMs apart are one position; a move's velocity is its displacement over the last WindowMs,
29
+ * the earlier position interpolated; when the previous move is already that old, its own velocity exactly (evenly
30
+ * spaced input at 60 Hz measures as before).
31
+ */
32
+ export declare class VelocityTrail {
33
+ /** A move's velocity is its displacement over this much time. */
34
+ static readonly WindowMs = 16;
35
+ /** Below this since the press no velocity is measured yet: the first moves of a burst. */
36
+ static readonly MinMs = 4;
37
+ /** Moves closer than this came in one burst: one position, at the burst's first time. */
38
+ static readonly BurstMs = 1;
39
+ private static readonly Capacity;
40
+ private readonly points;
41
+ Push(x: number, y: number, t: number): void;
42
+ /** The velocity at (x, y) at time t (ms), pixels per second; `fallback` right after the press. */
43
+ Velocity(x: number, y: number, t: number, fallback: SKPoint): SKPoint;
44
+ }
45
+ /** AppoMobi.Gestures MouseButton: which button pressed / released (DOM button 0..4). */
46
+ export type MouseButton = "Left" | "Middle" | "Right" | "XButton1" | "XButton2" | "Extended";
47
+ export type PointerDeviceType = "Mouse" | "Touch" | "Pen";
48
+ /** AppoMobi.Gestures PointerData: the device and button behind a Down / Up / Tapped (every button is delivered). */
49
+ export declare class PointerData {
50
+ Button: MouseButton;
51
+ /** 1 = Left, 2 = Right, 3 = Middle, 4+ = extended, like AppoMobi.Gestures. */
52
+ ButtonNumber: number;
53
+ DeviceType: PointerDeviceType;
54
+ /** DOM `buttons` bitmask of the buttons held (1 left, 2 right, 4 middle, 8 back, 16 forward). */
55
+ PressedButtons: number;
56
+ }
24
57
  /** AppoMobi.Gestures TouchActionEventArgs. */
25
58
  export declare class TouchActionEventArgs {
26
59
  Id: number;
60
+ /** Device and button of this event (mouse, pen, touch); undefined for wheel. */
61
+ Pointer?: PointerData;
27
62
  Type: TouchActionType;
28
63
  /** Pixels. */
29
64
  Location: SKPoint;
@@ -35,10 +70,21 @@ export declare class TouchActionEventArgs {
35
70
  Timestamp: number;
36
71
  /** ms since the previous event of the same pointer. */
37
72
  DeltaTimeMs: number;
38
- /** Mouse wheel: Delta > 0 = wheel down (browser deltaY sign). */
73
+ /** The press's recent positions the velocity is measured over, carried from event to event of one pointer. */
74
+ Trail?: VelocityTrail;
75
+ /**
76
+ * Mouse wheel: Delta > 0 = wheel down / right (browser sign). IsHorizontal: the event is along the X axis (a tilting
77
+ * wheel, the sideways part of a two-finger touchpad swipe), as C# WheelEventArgs.IsHorizontal.
78
+ */
39
79
  Wheel: {
40
80
  Delta: number;
81
+ IsHorizontal?: boolean;
41
82
  };
83
+ /**
84
+ * AppoMobi TouchActionEventArgs.Handled: set by a control that actually used the event. The canvas prevents the
85
+ * browser default (the page scrolling under the wheel) only for a Handled event in `Gestures="Enabled"`.
86
+ */
87
+ Handled: boolean;
42
88
  /** Same as the .NET helper: derives Start/End/Delta/Total from the previous event of the same pointer. */
43
89
  static FillDistanceInfo(current: TouchActionEventArgs, previous: TouchActionEventArgs | undefined): void;
44
90
  }
@@ -18,9 +18,81 @@ export class DistanceInfo {
18
18
  Total = SKPoint.Empty;
19
19
  Velocity = SKPoint.Empty;
20
20
  }
21
+ /**
22
+ * The last positions of a press with their times (DrawnUi.Rust gestures `Trail`, 6d525f7). Measured only against the
23
+ * previous move, irregular delivery made the velocity worthless: moves handled in one burst (queued pointer events
24
+ * handed over back to back by a busy main thread, WSLg pairs 0.01 ms apart) gave millions of px/s and every fling at
25
+ * the limit. Moves under BurstMs apart are one position; a move's velocity is its displacement over the last WindowMs,
26
+ * the earlier position interpolated; when the previous move is already that old, its own velocity exactly (evenly
27
+ * spaced input at 60 Hz measures as before).
28
+ */
29
+ export class VelocityTrail {
30
+ /** A move's velocity is its displacement over this much time. */
31
+ static WindowMs = 16;
32
+ /** Below this since the press no velocity is measured yet: the first moves of a burst. */
33
+ static MinMs = 4;
34
+ /** Moves closer than this came in one burst: one position, at the burst's first time. */
35
+ static BurstMs = 1;
36
+ static Capacity = 16;
37
+ points = [];
38
+ Push(x, y, t) {
39
+ const last = this.points[this.points.length - 1];
40
+ if (last && t - last.T < VelocityTrail.BurstMs) {
41
+ last.X = x;
42
+ last.Y = y;
43
+ return;
44
+ }
45
+ if (this.points.length === VelocityTrail.Capacity)
46
+ this.points.shift();
47
+ this.points.push({ X: x, Y: y, T: t });
48
+ }
49
+ /** The velocity at (x, y) at time t (ms), pixels per second; `fallback` right after the press. */
50
+ Velocity(x, y, t, fallback) {
51
+ let n = this.points.length;
52
+ // a move in the burst of the last position measures from the positions before it
53
+ if (n > 0 && t - this.points[n - 1].T < VelocityTrail.BurstMs)
54
+ n--;
55
+ if (n === 0)
56
+ return fallback;
57
+ const since = t - this.points[0].T;
58
+ if (since < VelocityTrail.MinMs)
59
+ return fallback;
60
+ const window = Math.min(VelocityTrail.WindowMs, since), at = t - window;
61
+ const last = this.points[n - 1];
62
+ if (last.T <= at) {
63
+ const secs = (t - last.T) / 1000;
64
+ return new SKPoint((x - last.X) / secs, (y - last.Y) / secs);
65
+ }
66
+ // the position at `at`: between the two positions around it (the current one included)
67
+ let fromX = this.points[0].X, fromY = this.points[0].Y, prev = this.points[0];
68
+ for (let i = 1; i <= n; i++) {
69
+ const p = i < n ? this.points[i] : { X: x, Y: y, T: t };
70
+ if (p.T >= at) {
71
+ const share = p.T - prev.T < 1e-6 ? 1 : (at - prev.T) / (p.T - prev.T);
72
+ fromX = prev.X + (p.X - prev.X) * share;
73
+ fromY = prev.Y + (p.Y - prev.Y) * share;
74
+ break;
75
+ }
76
+ prev = p;
77
+ }
78
+ const secs = window / 1000;
79
+ return new SKPoint((x - fromX) / secs, (y - fromY) / secs);
80
+ }
81
+ }
82
+ /** AppoMobi.Gestures PointerData: the device and button behind a Down / Up / Tapped (every button is delivered). */
83
+ export class PointerData {
84
+ Button = "Left";
85
+ /** 1 = Left, 2 = Right, 3 = Middle, 4+ = extended, like AppoMobi.Gestures. */
86
+ ButtonNumber = 1;
87
+ DeviceType = "Mouse";
88
+ /** DOM `buttons` bitmask of the buttons held (1 left, 2 right, 4 middle, 8 back, 16 forward). */
89
+ PressedButtons = 0;
90
+ }
21
91
  /** AppoMobi.Gestures TouchActionEventArgs. */
22
92
  export class TouchActionEventArgs {
23
93
  Id = 0;
94
+ /** Device and button of this event (mouse, pen, touch); undefined for wheel. */
95
+ Pointer;
24
96
  Type = "Pressed";
25
97
  /** Pixels. */
26
98
  Location = SKPoint.Empty;
@@ -32,8 +104,18 @@ export class TouchActionEventArgs {
32
104
  Timestamp = performance.now();
33
105
  /** ms since the previous event of the same pointer. */
34
106
  DeltaTimeMs = 0;
35
- /** Mouse wheel: Delta > 0 = wheel down (browser deltaY sign). */
107
+ /** The press's recent positions the velocity is measured over, carried from event to event of one pointer. */
108
+ Trail;
109
+ /**
110
+ * Mouse wheel: Delta > 0 = wheel down / right (browser sign). IsHorizontal: the event is along the X axis (a tilting
111
+ * wheel, the sideways part of a two-finger touchpad swipe), as C# WheelEventArgs.IsHorizontal.
112
+ */
36
113
  Wheel = { Delta: 0 };
114
+ /**
115
+ * AppoMobi TouchActionEventArgs.Handled: set by a control that actually used the event. The canvas prevents the
116
+ * browser default (the page scrolling under the wheel) only for a Handled event in `Gestures="Enabled"`.
117
+ */
118
+ Handled = false;
37
119
  /** Same as the .NET helper: derives Start/End/Delta/Total from the previous event of the same pointer. */
38
120
  static FillDistanceInfo(current, previous) {
39
121
  if (!previous) {
@@ -49,8 +131,18 @@ export class TouchActionEventArgs {
49
131
  d.End = released ? previous.Location : current.Location;
50
132
  d.Delta = released ? SKPoint.Empty : current.Location.Subtract(previous.Location);
51
133
  d.Total = previous.Distance.Total.Add(d.Delta);
52
- const secs = current.DeltaTimeMs / 1000;
53
- d.Velocity = secs > 0 && !released ? new SKPoint(d.Delta.X / secs, d.Delta.Y / secs) : previous.Distance.Velocity;
134
+ if (released)
135
+ d.Velocity = previous.Distance.Velocity;
136
+ else {
137
+ let trail = previous.Trail;
138
+ if (!trail) {
139
+ trail = new VelocityTrail();
140
+ trail.Push(previous.Location.X, previous.Location.Y, previous.Timestamp);
141
+ }
142
+ d.Velocity = trail.Velocity(current.Location.X, current.Location.Y, current.Timestamp, previous.Distance.Velocity);
143
+ trail.Push(current.Location.X, current.Location.Y, current.Timestamp);
144
+ current.Trail = trail;
145
+ }
54
146
  current.Distance = d;
55
147
  }
56
148
  }
@@ -0,0 +1,47 @@
1
+ import type { SkiaControl } from "./SkiaControl";
2
+ /**
3
+ * DrawnUI hover (drawnui-cross 6m, C# DrawnView ReportHover / CommitHover): every control under the mouse that takes
4
+ * hover (`ReceivesHover`) is hovered, a card and the button inside it alike, as CSS :hover. Decided from the hit path
5
+ * of one pointer position, by the routing rules of ProcessGestures (visible, not InputTransparent, inside, children
6
+ * not locked, nothing below a control that blocks gestures below).
7
+ * While content moves under the pointer (a scroll glide, fling or pan, a carousel or drawer transition) hover is not
8
+ * tracked and what was hovered stays; one check at the last pointer position follows when it stops, and after
9
+ * controls are added or removed, a cell is rebound, a hovered control is hidden or a scroll jumps. Leaving the canvas
10
+ * clears hover at once. Touch never hovers. No allocation on frames where nothing changes.
11
+ */
12
+ export declare class HoverManager {
13
+ /** The hovered controls, root first. */
14
+ readonly Hovered: SkiaControl[];
15
+ private readonly next;
16
+ /** The controls under the pointer at the last hit test, root first, and the pointer in each one's own space. */
17
+ readonly Path: SkiaControl[];
18
+ readonly PathX: number[];
19
+ readonly PathY: number[];
20
+ private readonly movers;
21
+ private hasPointer;
22
+ private x;
23
+ private y;
24
+ private paused;
25
+ private check;
26
+ /**
27
+ * The mouse (or a pen in range) moved to (x, y), canvas pixels, nothing pressed. The hit path is taken also while
28
+ * hover waits (the cursor follows it), hover changes only when nothing moves.
29
+ */
30
+ Move(root: SkiaControl | undefined, x: number, y: number): void;
31
+ /** Fills Path with the controls under the pointer by the routing rules of ProcessGestures. */
32
+ private HitTest;
33
+ /** The pointer left the canvas: nothing is hovered, also while content moves. */
34
+ Leave(): void;
35
+ /** The controls under a still pointer may have changed: checked once at the end of the frame. */
36
+ RequestCheck(): void;
37
+ /** A control that moves its content (MovesContent) while drawn: hover waits while any of them moves. */
38
+ RegisterMover(control: SkiaControl): void;
39
+ /** End of a frame: pause while content moves, else run a pending check (at most one a frame, none while pressed). */
40
+ AfterFrame(root: SkiaControl | undefined, pressed: boolean): void;
41
+ private static Shown;
42
+ /**
43
+ * The hovered set becomes the controls of the hit path that take hover (`fromPath`), or none: the ones that left get
44
+ * HoverChanged(false), then the new ones (true).
45
+ */
46
+ private Apply;
47
+ }
@@ -0,0 +1,134 @@
1
+ import { SKPoint } from "./Gestures";
2
+ /**
3
+ * DrawnUI hover (drawnui-cross 6m, C# DrawnView ReportHover / CommitHover): every control under the mouse that takes
4
+ * hover (`ReceivesHover`) is hovered, a card and the button inside it alike, as CSS :hover. Decided from the hit path
5
+ * of one pointer position, by the routing rules of ProcessGestures (visible, not InputTransparent, inside, children
6
+ * not locked, nothing below a control that blocks gestures below).
7
+ * While content moves under the pointer (a scroll glide, fling or pan, a carousel or drawer transition) hover is not
8
+ * tracked and what was hovered stays; one check at the last pointer position follows when it stops, and after
9
+ * controls are added or removed, a cell is rebound, a hovered control is hidden or a scroll jumps. Leaving the canvas
10
+ * clears hover at once. Touch never hovers. No allocation on frames where nothing changes.
11
+ */
12
+ export class HoverManager {
13
+ /** The hovered controls, root first. */
14
+ Hovered = [];
15
+ next = [];
16
+ /** The controls under the pointer at the last hit test, root first, and the pointer in each one's own space. */
17
+ Path = [];
18
+ PathX = [];
19
+ PathY = [];
20
+ movers = [];
21
+ hasPointer = false;
22
+ x = 0;
23
+ y = 0;
24
+ paused = false;
25
+ check = false;
26
+ /**
27
+ * The mouse (or a pen in range) moved to (x, y), canvas pixels, nothing pressed. The hit path is taken also while
28
+ * hover waits (the cursor follows it), hover changes only when nothing moves.
29
+ */
30
+ Move(root, x, y) {
31
+ this.hasPointer = true;
32
+ this.x = x;
33
+ this.y = y;
34
+ this.HitTest(root);
35
+ if (this.paused)
36
+ return; // content moves under the pointer: what was hovered stays, one check when it stops
37
+ this.check = false;
38
+ this.Apply(true);
39
+ }
40
+ /** Fills Path with the controls under the pointer by the routing rules of ProcessGestures. */
41
+ HitTest(root) {
42
+ const path = this.Path;
43
+ path.length = 0;
44
+ this.PathX.length = 0;
45
+ this.PathY.length = 0;
46
+ if (root && root.IsVisible && !root.InputTransparent) {
47
+ const point = root.TransformPointToLocalSpace(new SKPoint(this.x, this.y));
48
+ if (root.HitIsInside(point.X, point.Y))
49
+ root.CollectHovered(point, path, this.PathX, this.PathY);
50
+ }
51
+ }
52
+ /** The pointer left the canvas: nothing is hovered, also while content moves. */
53
+ Leave() {
54
+ this.hasPointer = false;
55
+ this.check = false;
56
+ this.Path.length = 0;
57
+ this.PathX.length = 0;
58
+ this.PathY.length = 0;
59
+ this.Apply(false);
60
+ }
61
+ /** The controls under a still pointer may have changed: checked once at the end of the frame. */
62
+ RequestCheck() { if (this.hasPointer)
63
+ this.check = true; }
64
+ /** A control that moves its content (MovesContent) while drawn: hover waits while any of them moves. */
65
+ RegisterMover(control) { if (this.movers.indexOf(control) < 0)
66
+ this.movers.push(control); }
67
+ /** End of a frame: pause while content moves, else run a pending check (at most one a frame, none while pressed). */
68
+ AfterFrame(root, pressed) {
69
+ let moving = false;
70
+ for (let i = this.movers.length - 1; i >= 0; i--) {
71
+ const m = this.movers[i];
72
+ if (m.IsDisposed || !m.Superview) {
73
+ this.movers.splice(i, 1);
74
+ continue;
75
+ }
76
+ if (!moving && m.IsVisible && m.MovesContent())
77
+ moving = true;
78
+ }
79
+ if (moving) {
80
+ this.paused = true;
81
+ return;
82
+ }
83
+ if (this.paused) {
84
+ this.paused = false;
85
+ this.check = true;
86
+ }
87
+ // a hovered control (or an ancestor) hidden or removed under a still pointer stops being hovered
88
+ for (let i = 0; i < this.Hovered.length && !this.check; i++)
89
+ if (!HoverManager.Shown(this.Hovered[i]))
90
+ this.check = true;
91
+ if (!this.check)
92
+ return;
93
+ this.check = false;
94
+ if (!this.hasPointer || pressed)
95
+ return;
96
+ this.HitTest(root);
97
+ this.Apply(true);
98
+ }
99
+ static Shown(control) {
100
+ if (control.IsDisposed || !control.Superview)
101
+ return false;
102
+ for (let c = control; c; c = c.Parent)
103
+ if (!c.IsVisible)
104
+ return false;
105
+ return true;
106
+ }
107
+ /**
108
+ * The hovered set becomes the controls of the hit path that take hover (`fromPath`), or none: the ones that left get
109
+ * HoverChanged(false), then the new ones (true).
110
+ */
111
+ Apply(fromPath) {
112
+ const now = this.Hovered, next = this.next;
113
+ next.length = 0;
114
+ if (fromPath)
115
+ for (const c of this.Path)
116
+ if (c.ReceivesHover)
117
+ next.push(c);
118
+ if (now.length === next.length) {
119
+ let same = true;
120
+ for (let i = 0; i < now.length && same; i++)
121
+ same = now[i] === next[i];
122
+ if (same)
123
+ return;
124
+ }
125
+ const was = now.splice(0);
126
+ now.push(...next);
127
+ for (const c of was)
128
+ if (now.indexOf(c) < 0)
129
+ c.ApplyHover(false);
130
+ for (const c of now)
131
+ if (was.indexOf(c) < 0)
132
+ c.ApplyHover(true);
133
+ }
134
+ }
@@ -20,6 +20,19 @@ export declare class KeyboardManager {
20
20
  static AttachToKeyboard(): void;
21
21
  static Subscribe(down: (key: InputKey, e: KeyboardEvent) => void, char: (ch: string, e: KeyboardEvent) => void, up?: (key: InputKey, e: KeyboardEvent) => void): void;
22
22
  static Unsubscribe(down: (key: InputKey, e: KeyboardEvent) => void, char: (ch: string, e: KeyboardEvent) => void, up?: (key: InputKey, e: KeyboardEvent) => void): void;
23
+ /**
24
+ * Hooks observe keys, they never take them from the element that owns them (C# 845b26e9, GitHub #231). True when a
25
+ * focused DOM element is the key's target: a page input, textarea, select, button, link or contenteditable, an
26
+ * accessibility overlay node, the drawn editor's hidden textarea. False when the key goes to the page itself (body,
27
+ * html) or the canvas. A handler that acts on keys (edits, moves, calls preventDefault) skips owned keys, except
28
+ * those of its own element.
29
+ */
30
+ static IsOwnedByElement(e: KeyboardEvent): boolean;
31
+ /**
32
+ * The key belongs to the input method (an IME composition, a soft keyboard): isComposing, keyCode 229, key "Process"
33
+ * or "Unidentified". Its result arrives as input events (TextInputProxy), never act on the key itself.
34
+ */
35
+ static IsInputMethodKey(e: KeyboardEvent): boolean;
23
36
  /** JS blurExternalTextInput: a page text input outside the canvas would keep receiving the keys. */
24
37
  static BlurExternalTextInput(): void;
25
38
  }
@@ -45,6 +45,24 @@ export class KeyboardManager {
45
45
  if (up)
46
46
  KeyboardManager.keyUp.delete(up);
47
47
  }
48
+ /**
49
+ * Hooks observe keys, they never take them from the element that owns them (C# 845b26e9, GitHub #231). True when a
50
+ * focused DOM element is the key's target: a page input, textarea, select, button, link or contenteditable, an
51
+ * accessibility overlay node, the drawn editor's hidden textarea. False when the key goes to the page itself (body,
52
+ * html) or the canvas. A handler that acts on keys (edits, moves, calls preventDefault) skips owned keys, except
53
+ * those of its own element.
54
+ */
55
+ static IsOwnedByElement(e) {
56
+ const tag = e.target?.tagName;
57
+ return !!tag && tag !== "BODY" && tag !== "HTML" && tag !== "CANVAS";
58
+ }
59
+ /**
60
+ * The key belongs to the input method (an IME composition, a soft keyboard): isComposing, keyCode 229, key "Process"
61
+ * or "Unidentified". Its result arrives as input events (TextInputProxy), never act on the key itself.
62
+ */
63
+ static IsInputMethodKey(e) {
64
+ return e.isComposing || e.keyCode === 229 || e.key === "Process" || e.key === "Unidentified";
65
+ }
48
66
  /** JS blurExternalTextInput: a page text input outside the canvas would keep receiving the keys. */
49
67
  static BlurExternalTextInput() {
50
68
  const a = typeof document !== "undefined" ? document.activeElement : null;
@@ -1,6 +1,7 @@
1
1
  // Mirrors DrawnUi scrolling physics: DecelerationTimingParameters + ScrollFlingAnimator (fling),
2
2
  // Spring + SpringTimingParameters + SpringWithVelocityAnimator (bounce), VelocityAccumulator, RubberBandUtils.
3
3
  import { SkiaValueAnimator } from "./Animators";
4
+ import { VelocityTrail } from "./Gestures";
4
5
  /** UIKit-style exponential deceleration: value(t) = v0 * (rate^(1000 t) - 1) / k, k = 1000 ln(rate). */
5
6
  export class DecelerationTimingParameters {
6
7
  InitialValue;
@@ -201,6 +202,14 @@ export class VelocityAccumulator {
201
202
  Clear() { this.samples.length = 0; }
202
203
  CaptureVelocity(x, y, arrivedTimeNanos = 0) {
203
204
  const time = arrivedTimeNanos > 0 ? arrivedTimeNanos / 1e6 : performance.now();
205
+ // a move in the same burst as the last one replaces it: the first move of a burst carries only part of the burst's
206
+ // distance (DrawnUi.Rust VelocityAccumulator, 6d525f7)
207
+ const last = this.samples[this.samples.length - 1];
208
+ if (last && time - last.time < VelocityTrail.BurstMs) {
209
+ last.X = x;
210
+ last.Y = y;
211
+ return;
212
+ }
204
213
  if (this.samples.length === VelocityAccumulator.MaxSampleSize)
205
214
  this.samples.shift();
206
215
  this.samples.push({ X: x, Y: y, time });