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.
- package/README.md +17 -4
- package/SKIPPED.md +33 -27
- package/dist/controls/AnimatedFramesRenderer.d.ts +7 -0
- package/dist/controls/AnimatedFramesRenderer.js +16 -1
- package/dist/controls/DrawnGame.d.ts +40 -0
- package/dist/controls/DrawnGame.js +64 -0
- package/dist/controls/GridStructure.d.ts +16 -2
- package/dist/controls/GridStructure.js +40 -4
- package/dist/controls/SkiaBackdrop.d.ts +8 -2
- package/dist/controls/SkiaBackdrop.js +48 -8
- package/dist/controls/SkiaButton.d.ts +19 -2
- package/dist/controls/SkiaButton.js +41 -11
- package/dist/controls/SkiaCarousel.d.ts +9 -1
- package/dist/controls/SkiaCarousel.js +16 -3
- package/dist/controls/SkiaCheckbox.d.ts +0 -1
- package/dist/controls/SkiaCheckbox.js +0 -6
- package/dist/controls/SkiaDrawer.d.ts +2 -0
- package/dist/controls/SkiaDrawer.js +2 -0
- package/dist/controls/SkiaEditor.d.ts +12 -1
- package/dist/controls/SkiaEditor.js +46 -19
- package/dist/controls/SkiaGif.d.ts +3 -0
- package/dist/controls/SkiaGif.js +12 -2
- package/dist/controls/SkiaImage.js +15 -10
- package/dist/controls/SkiaLabel.d.ts +54 -3
- package/dist/controls/SkiaLabel.js +235 -52
- package/dist/controls/SkiaLabelFps.d.ts +16 -0
- package/dist/controls/SkiaLabelFps.js +41 -0
- package/dist/controls/SkiaLayout.d.ts +64 -7
- package/dist/controls/SkiaLayout.js +316 -19
- package/dist/controls/SkiaLottie.js +4 -2
- package/dist/controls/SkiaProgress.d.ts +2 -1
- package/dist/controls/SkiaProgress.js +6 -3
- package/dist/controls/SkiaRadioButton.js +4 -3
- package/dist/controls/SkiaScroll.d.ts +35 -5
- package/dist/controls/SkiaScroll.js +233 -23
- package/dist/controls/SkiaScrollBar.d.ts +27 -0
- package/dist/controls/SkiaScrollBar.js +51 -2
- package/dist/controls/SkiaShaderCarousel.d.ts +5 -0
- package/dist/controls/SkiaShaderCarousel.js +5 -0
- package/dist/controls/SkiaShape.d.ts +13 -2
- package/dist/controls/SkiaShape.js +50 -10
- package/dist/controls/SkiaSlider.d.ts +20 -2
- package/dist/controls/SkiaSlider.js +59 -5
- package/dist/controls/SkiaSprite.d.ts +1 -0
- package/dist/controls/SkiaSprite.js +4 -0
- package/dist/controls/SkiaSvg.js +1 -1
- package/dist/controls/SkiaSwitch.d.ts +0 -1
- package/dist/controls/SkiaSwitch.js +0 -6
- package/dist/controls/SkiaToggle.d.ts +25 -5
- package/dist/controls/SkiaToggle.js +58 -5
- package/dist/controls/SnappingLayout.d.ts +12 -1
- package/dist/controls/SnappingLayout.js +19 -1
- package/dist/core/Accessibility.d.ts +80 -0
- package/dist/core/Accessibility.js +365 -8
- package/dist/core/Animators.d.ts +37 -1
- package/dist/core/Animators.js +102 -9
- package/dist/core/Canvas.d.ts +108 -7
- package/dist/core/Canvas.js +363 -35
- package/dist/core/Easing.d.ts +12 -1
- package/dist/core/Easing.js +25 -1
- package/dist/core/Gestures.d.ts +48 -2
- package/dist/core/Gestures.js +95 -3
- package/dist/core/Hover.d.ts +47 -0
- package/dist/core/Hover.js +134 -0
- package/dist/core/KeyboardManager.d.ts +13 -0
- package/dist/core/KeyboardManager.js +18 -0
- package/dist/core/ScrollAnimators.js +9 -0
- package/dist/core/SkiaControl.d.ts +216 -7
- package/dist/core/SkiaControl.js +516 -59
- package/dist/core/SkiaEffect.d.ts +17 -0
- package/dist/core/SkiaEffect.js +43 -8
- package/dist/core/SkiaImageManager.js +1 -1
- package/dist/core/Styles.d.ts +51 -0
- package/dist/core/Styles.js +92 -0
- package/dist/core/Super.d.ts +17 -1
- package/dist/core/Super.js +35 -4
- package/dist/core/ViewsAdapter.d.ts +7 -2
- package/dist/core/ViewsAdapter.js +10 -3
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/react/SkiaShell.js +33 -14
- package/dist/react/index.d.ts +15 -4
- package/dist/react/index.js +102 -15
- package/dist/react/reconciler.js +34 -12
- package/dist/vite/index.d.ts +9 -0
- package/dist/vite/index.js +36 -3
- package/package.json +33 -3
|
@@ -20,6 +20,10 @@ export declare class SkiaShape extends SkiaLayout {
|
|
|
20
20
|
StrokeCap: StrokeCap;
|
|
21
21
|
/** Gradient painted along the stroke instead of StrokeColor (C# StrokeGradient). */
|
|
22
22
|
StrokeGradient?: SkiaGradient;
|
|
23
|
+
/** Dashes in points: on, off, on, off... (C# StrokePath, each rounded to pixels). Empty = a solid line. */
|
|
24
|
+
StrokePath?: number[];
|
|
25
|
+
/** Blend mode of the stroke, an SKBlendMode name (C# StrokeBlendMode). */
|
|
26
|
+
StrokeBlendMode: string;
|
|
23
27
|
/** Raised (Bevel) or pressed-in (Emboss) edges, drawn with Bevel after the background (C# BevelType). */
|
|
24
28
|
BevelType: BevelType;
|
|
25
29
|
private bevel?;
|
|
@@ -43,11 +47,18 @@ export declare class SkiaShape extends SkiaLayout {
|
|
|
43
47
|
static ShadowFilter(shadow: SkiaShadow, scale: number): import("canvaskit-wasm").ImageFilter;
|
|
44
48
|
constructor();
|
|
45
49
|
private StrokePixels;
|
|
46
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* The outline the stroke runs on (C# CalculateShapeSizeForStroke): the bounds inset by half the stroke rounded up, at
|
|
52
|
+
* least a pixel, then on whole pixels inward, so a 1 px stroke on a plain rectangle sits on the line between two
|
|
53
|
+
* pixels as upstream draws it.
|
|
54
|
+
*/
|
|
47
55
|
private StrokeAwareRect;
|
|
56
|
+
/** The fill (C# MeasuredStrokeAwareClipSize): a third of the stroke inside the outline, so it does not show under the stroke's edge. */
|
|
57
|
+
private BackgroundRect;
|
|
48
58
|
private UsesStroke;
|
|
49
59
|
/** Builds the outline path for rect (canvas pixels). Caller deletes it. */
|
|
50
|
-
|
|
60
|
+
/** `radiusReduce`: pixels taken off every corner radius (the fill inside a thick stroke, C# GetCorrectedBackgroundRadii). */
|
|
61
|
+
protected CreateShapePath(rect: SKRect, scale: number, radiusReduce?: number): Path;
|
|
51
62
|
/** Children live inside the stroke (full width) + Padding. */
|
|
52
63
|
private ChildrenRect;
|
|
53
64
|
/** Clip for overlay effects (ripple) and children: the shape itself, inset by the stroke. */
|
|
@@ -17,6 +17,10 @@ export class SkiaShape extends SkiaLayout {
|
|
|
17
17
|
StrokeCap = "Round";
|
|
18
18
|
/** Gradient painted along the stroke instead of StrokeColor (C# StrokeGradient). */
|
|
19
19
|
StrokeGradient;
|
|
20
|
+
/** Dashes in points: on, off, on, off... (C# StrokePath, each rounded to pixels). Empty = a solid line. */
|
|
21
|
+
StrokePath;
|
|
22
|
+
/** Blend mode of the stroke, an SKBlendMode name (C# StrokeBlendMode). */
|
|
23
|
+
StrokeBlendMode = "SrcOver";
|
|
20
24
|
/** Raised (Bevel) or pressed-in (Emboss) edges, drawn with Bevel after the background (C# BevelType). */
|
|
21
25
|
BevelType = "None";
|
|
22
26
|
bevel;
|
|
@@ -66,14 +70,28 @@ export class SkiaShape extends SkiaLayout {
|
|
|
66
70
|
return 0;
|
|
67
71
|
return this.StrokeWidth > 0 ? this.StrokeWidth * scale : -this.StrokeWidth;
|
|
68
72
|
}
|
|
69
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* The outline the stroke runs on (C# CalculateShapeSizeForStroke): the bounds inset by half the stroke rounded up, at
|
|
75
|
+
* least a pixel, then on whole pixels inward, so a 1 px stroke on a plain rectangle sits on the line between two
|
|
76
|
+
* pixels as upstream draws it.
|
|
77
|
+
*/
|
|
70
78
|
StrokeAwareRect(r, scale) {
|
|
71
|
-
|
|
72
|
-
|
|
79
|
+
if (!this.UsesStroke())
|
|
80
|
+
return r;
|
|
81
|
+
const half = this.StrokePixels(scale) / 2, inset = half < 0.5 ? 1 : Math.ceil(half);
|
|
82
|
+
return new SKRect(Math.ceil(r.Left + inset), Math.ceil(r.Top + inset), Math.floor(r.Right - inset), Math.floor(r.Bottom - inset));
|
|
83
|
+
}
|
|
84
|
+
/** The fill (C# MeasuredStrokeAwareClipSize): a third of the stroke inside the outline, so it does not show under the stroke's edge. */
|
|
85
|
+
BackgroundRect(r, scale) {
|
|
86
|
+
if (!this.UsesStroke())
|
|
87
|
+
return r;
|
|
88
|
+
const o = this.StrokeAwareRect(r, scale), k = this.StrokePixels(scale) / 3;
|
|
89
|
+
return new SKRect(o.Left + k, o.Top + k, o.Right - k, o.Bottom - k);
|
|
73
90
|
}
|
|
74
91
|
UsesStroke() { return this.StrokeWidth !== 0 && this.StrokeColor !== Colors.Transparent; }
|
|
75
92
|
/** Builds the outline path for rect (canvas pixels). Caller deletes it. */
|
|
76
|
-
|
|
93
|
+
/** `radiusReduce`: pixels taken off every corner radius (the fill inside a thick stroke, C# GetCorrectedBackgroundRadii). */
|
|
94
|
+
CreateShapePath(rect, scale, radiusReduce = 0) {
|
|
77
95
|
const CK = Super.CK;
|
|
78
96
|
const b = new CK.PathBuilder();
|
|
79
97
|
const type = this.Type;
|
|
@@ -114,7 +132,8 @@ export class SkiaShape extends SkiaLayout {
|
|
|
114
132
|
else {
|
|
115
133
|
const c = this.cornerRadius;
|
|
116
134
|
if (c.TopLeft || c.TopRight || c.BottomLeft || c.BottomRight) {
|
|
117
|
-
const
|
|
135
|
+
const k = (v) => Math.max(0, v * scale - radiusReduce);
|
|
136
|
+
const tl = k(c.TopLeft), tr = k(c.TopRight), br = k(c.BottomRight), bl = k(c.BottomLeft);
|
|
118
137
|
b.addRRect(Float32Array.of(rect.Left, rect.Top, rect.Right, rect.Bottom, tl, tl, tr, tr, br, br, bl, bl));
|
|
119
138
|
}
|
|
120
139
|
else {
|
|
@@ -151,8 +170,11 @@ export class SkiaShape extends SkiaLayout {
|
|
|
151
170
|
return; // open shapes: stroke only
|
|
152
171
|
const CK = Super.CK;
|
|
153
172
|
const canvas = ctx.Context.Canvas;
|
|
154
|
-
const rect = this.
|
|
155
|
-
|
|
173
|
+
const rect = this.BackgroundRect(ctx.Destination, ctx.Scale);
|
|
174
|
+
// inside a stroke wider than a point the corners of the fill are rounded less (C# GetCorrectedBackgroundRadii)
|
|
175
|
+
const outline = this.StrokeAwareRect(ctx.Destination, ctx.Scale);
|
|
176
|
+
const reduce = this.UsesStroke() && this.StrokeWidth > 1 ? Math.min((outline.Width - rect.Width) / 2, (outline.Height - rect.Height) / 2) : 0;
|
|
177
|
+
const path = this.CreateShapePath(rect, ctx.Scale, reduce);
|
|
156
178
|
// C# PaintWithShadowsInternal: the fill is drawn once per shadow with a drop-shadow filter; a hollow shape
|
|
157
179
|
// (ClipBackgroundColor) clips its own outline away so only the shadow outside remains
|
|
158
180
|
for (const shadow of this.shadows) {
|
|
@@ -295,18 +317,36 @@ export class SkiaShape extends SkiaLayout {
|
|
|
295
317
|
clip.delete();
|
|
296
318
|
}
|
|
297
319
|
if (this.UsesStroke()) {
|
|
298
|
-
const
|
|
299
|
-
|
|
320
|
+
const scale = ctx.Scale, px = this.StrokePixels(scale), type = this.Type;
|
|
321
|
+
let rect = this.StrokeAwareRect(ctx.Destination, scale);
|
|
322
|
+
const c = this.cornerRadius;
|
|
323
|
+
const rounded = type === "Rectangle" && !!(c.TopLeft || c.TopRight || c.BottomLeft || c.BottomRight);
|
|
324
|
+
// C# PaintStroke: on curves a stroke of a point or less is thinned (its antialiasing makes it look heavier than
|
|
325
|
+
// the straight parts), a thin rounded rectangle runs through pixel centers, a line is at least a pixel wide
|
|
326
|
+
const curved = rounded || type === "Circle" || type === "Ellipse" || type === "Arc" || type === "Path";
|
|
327
|
+
const width = type === "Line" ? Math.max(1, px) : curved && px <= scale ? px * 0.55 : px;
|
|
328
|
+
if (rounded && px <= 2 * scale) {
|
|
329
|
+
const even = (v) => { const r = Math.round(v); return Math.abs(v % 1) === 0.5 && r % 2 !== 0 ? r - 1 : r; }; // C# Math.Round: ties to even
|
|
330
|
+
rect = new SKRect(even(rect.Left) + 0.5, even(rect.Top) + 0.5, even(rect.Right) - 0.5, even(rect.Bottom) - 0.5);
|
|
331
|
+
}
|
|
332
|
+
const path = this.CreateShapePath(rect, scale);
|
|
300
333
|
const paint = new CK.Paint();
|
|
301
334
|
paint.setAntiAlias(true);
|
|
302
335
|
paint.setStyle(CK.PaintStyle.Stroke);
|
|
303
|
-
paint.setStrokeWidth(
|
|
336
|
+
paint.setStrokeWidth(width);
|
|
304
337
|
paint.setColor(Super.ParseColor(this.StrokeColor));
|
|
305
338
|
if (this.StrokeGradient)
|
|
306
339
|
this.SetupGradient(paint, this.StrokeGradient, rect);
|
|
340
|
+
const blend = CK.BlendMode[this.StrokeBlendMode];
|
|
341
|
+
if (blend && this.StrokeBlendMode !== "SrcOver")
|
|
342
|
+
paint.setBlendMode(blend);
|
|
307
343
|
paint.setStrokeCap(this.StrokeCap === "Butt" ? CK.StrokeCap.Butt : this.StrokeCap === "Square" ? CK.StrokeCap.Square : CK.StrokeCap.Round);
|
|
308
344
|
paint.setStrokeJoin(this.StrokeCap === "Round" ? CK.StrokeJoin.Round : CK.StrokeJoin.Miter);
|
|
345
|
+
const dash = this.StrokePath?.length ? CK.PathEffect.MakeDash(this.StrokePath.map((v) => Math.round(v * scale)), 0) : null;
|
|
346
|
+
if (dash)
|
|
347
|
+
paint.setPathEffect(dash);
|
|
309
348
|
canvas.drawPath(path, paint);
|
|
349
|
+
dash?.delete();
|
|
310
350
|
paint.delete();
|
|
311
351
|
path.delete();
|
|
312
352
|
}
|
|
@@ -8,6 +8,8 @@ import { type Color, ScaledSize, Thickness } from "../core/Types";
|
|
|
8
8
|
* Track, selected trail and thumbs are painted directly; the C# thumb-position math (SliderHeight = thumb box) is kept.
|
|
9
9
|
*/
|
|
10
10
|
export declare class SkiaSlider extends SkiaControl {
|
|
11
|
+
/** Takes mouse hover unless ReceivesHover is set false (C# ReceivesHoverByDefault). */
|
|
12
|
+
protected ReceivesHoverByDefault(): boolean;
|
|
11
13
|
static DefaultAccessibilityRole?: string;
|
|
12
14
|
ControlStyle: PrebuiltControlStyle;
|
|
13
15
|
Min: number;
|
|
@@ -17,7 +19,11 @@ export declare class SkiaSlider extends SkiaControl {
|
|
|
17
19
|
/** Minimum distance between the two thumbs, in value units. */
|
|
18
20
|
RangeMin: number;
|
|
19
21
|
ClickOnTrailEnabled: boolean;
|
|
20
|
-
|
|
22
|
+
private respondsToGestures;
|
|
23
|
+
/** False: neither a drag nor the arrow keys move it (code still can), and it is reported disabled (drawnui-cross 6w). */
|
|
24
|
+
get RespondsToGestures(): boolean;
|
|
25
|
+
set RespondsToGestures(v: boolean);
|
|
26
|
+
protected AcceptsInput(): boolean;
|
|
21
27
|
IsPressed: boolean;
|
|
22
28
|
IsUserPanning: boolean;
|
|
23
29
|
/** Extra points around a thumb that still count as grabbing it (C# moreHotspotSize). */
|
|
@@ -70,5 +76,17 @@ export declare class SkiaSlider extends SkiaControl {
|
|
|
70
76
|
protected Paint(ctx: DrawingContext): void;
|
|
71
77
|
ProcessGestures(args: SkiaGesturesParameters, apply: GestureEventProcessingInfo): SkiaControl | null;
|
|
72
78
|
protected DefaultAccessibilityCanInteract(): boolean;
|
|
73
|
-
|
|
79
|
+
/** Enter / Space do nothing: the base taps the center, which would jump the value to the middle. */
|
|
80
|
+
OnAccessibilityActivated(): void;
|
|
81
|
+
/**
|
|
82
|
+
* Keyboard adjustment while the slider holds keyboard focus (C# SkiaSlider.OnAccessibilityKey): Right / Up increase and
|
|
83
|
+
* Left / Down decrease by Step (a hundredth of the range when Step is 0), PageUp / PageDown move a tenth of the range,
|
|
84
|
+
* Home / End go to Min / Max. A ranged slider moves its End thumb, which stops at Start.
|
|
85
|
+
*/
|
|
86
|
+
OnAccessibilityKey(key: string): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* End between Min and Max, read as the number, or "20 – 80" in range mode; the step is the arrow key's. The name is
|
|
89
|
+
* the app's AccessibilityLabel only (C# used to make the value the name, drawnui-cross 6c).
|
|
90
|
+
*/
|
|
91
|
+
get AccessibilityValue(): import("../core/Accessibility").AccessibilityValue;
|
|
74
92
|
}
|
|
@@ -9,6 +9,8 @@ import { SkiaShape } from "./SkiaShape";
|
|
|
9
9
|
* Track, selected trail and thumbs are painted directly; the C# thumb-position math (SliderHeight = thumb box) is kept.
|
|
10
10
|
*/
|
|
11
11
|
export class SkiaSlider extends SkiaControl {
|
|
12
|
+
/** Takes mouse hover unless ReceivesHover is set false (C# ReceivesHoverByDefault). */
|
|
13
|
+
ReceivesHoverByDefault() { return true; }
|
|
12
14
|
static DefaultAccessibilityRole = "slider";
|
|
13
15
|
ControlStyle = "Unset";
|
|
14
16
|
Min = 0;
|
|
@@ -18,7 +20,14 @@ export class SkiaSlider extends SkiaControl {
|
|
|
18
20
|
/** Minimum distance between the two thumbs, in value units. */
|
|
19
21
|
RangeMin = 0;
|
|
20
22
|
ClickOnTrailEnabled = true;
|
|
21
|
-
|
|
23
|
+
respondsToGestures = true;
|
|
24
|
+
/** False: neither a drag nor the arrow keys move it (code still can), and it is reported disabled (drawnui-cross 6w). */
|
|
25
|
+
get RespondsToGestures() { return this.respondsToGestures; }
|
|
26
|
+
set RespondsToGestures(v) { if (this.respondsToGestures !== v) {
|
|
27
|
+
this.respondsToGestures = v;
|
|
28
|
+
this.NotifyAccessibility();
|
|
29
|
+
} }
|
|
30
|
+
AcceptsInput() { return this.respondsToGestures; }
|
|
22
31
|
IsPressed = false;
|
|
23
32
|
IsUserPanning = false;
|
|
24
33
|
/** Extra points around a thumb that still count as grabbing it (C# moreHotspotSize). */
|
|
@@ -60,11 +69,11 @@ export class SkiaSlider extends SkiaControl {
|
|
|
60
69
|
get SliderHeight() { return this.sliderHeight ?? this.Look().thumb + (this.EnableRange && this.UsingControlStyle !== "Unset" ? 8 : 0); }
|
|
61
70
|
set SliderHeight(v) { this.sliderHeight = v; this.Update(); }
|
|
62
71
|
get ThumbColor() { return this.thumbColor ?? this.Look().thumbColor; }
|
|
63
|
-
set ThumbColor(v) { this.thumbColor = v; this.
|
|
72
|
+
set ThumbColor(v) { this.thumbColor = v; this.UpdateDraw(); }
|
|
64
73
|
get TrackColor() { return this.trackColor ?? this.Look().track; }
|
|
65
|
-
set TrackColor(v) { this.trackColor = v; this.
|
|
74
|
+
set TrackColor(v) { this.trackColor = v; this.UpdateDraw(); }
|
|
66
75
|
get TrackSelectedColor() { return this.trackSelectedColor ?? this.Look().selected; }
|
|
67
|
-
set TrackSelectedColor(v) { this.trackSelectedColor = v; this.
|
|
76
|
+
set TrackSelectedColor(v) { this.trackSelectedColor = v; this.UpdateDraw(); }
|
|
68
77
|
/** C# style builders: thumb diameter, track height, palette, thumb shadow. */
|
|
69
78
|
Look() {
|
|
70
79
|
switch (this.UsingControlStyle) {
|
|
@@ -239,5 +248,50 @@ export class SkiaSlider extends SkiaControl {
|
|
|
239
248
|
return super.ProcessGestures(args, apply);
|
|
240
249
|
}
|
|
241
250
|
DefaultAccessibilityCanInteract() { return this.RespondsToGestures; }
|
|
242
|
-
|
|
251
|
+
/** Enter / Space do nothing: the base taps the center, which would jump the value to the middle. */
|
|
252
|
+
OnAccessibilityActivated() { }
|
|
253
|
+
/**
|
|
254
|
+
* Keyboard adjustment while the slider holds keyboard focus (C# SkiaSlider.OnAccessibilityKey): Right / Up increase and
|
|
255
|
+
* Left / Down decrease by Step (a hundredth of the range when Step is 0), PageUp / PageDown move a tenth of the range,
|
|
256
|
+
* Home / End go to Min / Max. A ranged slider moves its End thumb, which stops at Start.
|
|
257
|
+
*/
|
|
258
|
+
OnAccessibilityKey(key) {
|
|
259
|
+
if (!this.RespondsToGestures)
|
|
260
|
+
return false;
|
|
261
|
+
const range = this.Max - this.Min, small = this.Step > 0 ? this.Step : range / 100, large = Math.max(small, range / 10);
|
|
262
|
+
const low = this.EnableRange ? this.Start : this.Min;
|
|
263
|
+
let value;
|
|
264
|
+
switch (key) {
|
|
265
|
+
case "ArrowRight":
|
|
266
|
+
case "ArrowUp":
|
|
267
|
+
value = this.End + small;
|
|
268
|
+
break;
|
|
269
|
+
case "ArrowLeft":
|
|
270
|
+
case "ArrowDown":
|
|
271
|
+
value = this.End - small;
|
|
272
|
+
break;
|
|
273
|
+
case "PageUp":
|
|
274
|
+
value = this.End + large;
|
|
275
|
+
break;
|
|
276
|
+
case "PageDown":
|
|
277
|
+
value = this.End - large;
|
|
278
|
+
break;
|
|
279
|
+
case "Home":
|
|
280
|
+
value = low;
|
|
281
|
+
break;
|
|
282
|
+
case "End":
|
|
283
|
+
value = this.Max;
|
|
284
|
+
break;
|
|
285
|
+
default: return false;
|
|
286
|
+
}
|
|
287
|
+
this.End = Math.min(this.Max, Math.max(low, value));
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* End between Min and Max, read as the number, or "20 – 80" in range mode; the step is the arrow key's. The name is
|
|
292
|
+
* the app's AccessibilityLabel only (C# used to make the value the name, drawnui-cross 6c).
|
|
293
|
+
*/
|
|
294
|
+
get AccessibilityValue() {
|
|
295
|
+
return { Now: this.end, Min: this.Min, Max: this.Max, Step: this.Step > 0 ? this.Step : (this.Max - this.Min) / 100, Text: this.EnableRange ? `${this.start} – ${this.end}` : "" };
|
|
296
|
+
}
|
|
243
297
|
}
|
|
@@ -86,6 +86,7 @@ export declare class SkiaSprite extends AnimatedFramesRenderer {
|
|
|
86
86
|
/** Transparent borders of a frame, scanned once (C# ScanFrameContentBounds over the bitmap alpha). */
|
|
87
87
|
private GetFrameContentBounds;
|
|
88
88
|
protected OnAnimatorUpdated(value: number): void;
|
|
89
|
+
protected NextChangeMs(value: number): number | undefined;
|
|
89
90
|
protected OnAnimatorSeeking(time: number): void;
|
|
90
91
|
protected OnAnimatorInitializing(): void;
|
|
91
92
|
protected ApplySpeed(): void;
|
|
@@ -227,6 +227,10 @@ export class SkiaSprite extends AnimatedFramesRenderer {
|
|
|
227
227
|
}
|
|
228
228
|
// ---- animator ----
|
|
229
229
|
OnAnimatorUpdated(value) { this.Seek(value); }
|
|
230
|
+
NextChangeMs(value) {
|
|
231
|
+
const fd = this.FrameDurationMs;
|
|
232
|
+
return fd > 0 ? (Math.floor(value / fd) + 1) * fd : undefined;
|
|
233
|
+
}
|
|
230
234
|
OnAnimatorSeeking(time) {
|
|
231
235
|
if (!this.SpriteSheet)
|
|
232
236
|
return;
|
package/dist/controls/SkiaSvg.js
CHANGED
|
@@ -42,7 +42,7 @@ export class SkiaSvg extends SkiaControl {
|
|
|
42
42
|
}
|
|
43
43
|
const generation = ++this.loadGeneration;
|
|
44
44
|
this.IsLoading = true;
|
|
45
|
-
fetch(value)
|
|
45
|
+
fetch(Super.ResolveAssetUrl(value))
|
|
46
46
|
.then((r) => { if (!r.ok)
|
|
47
47
|
throw new Error(`DrawnUi: ${r.status} loading svg '${value}'`); return r.text(); })
|
|
48
48
|
.then((text) => { if (generation === this.loadGeneration)
|
|
@@ -14,7 +14,6 @@ export declare class SkiaSwitch extends SkiaToggle {
|
|
|
14
14
|
private static readonly DefaultTrack;
|
|
15
15
|
private static readonly MaterialOutline;
|
|
16
16
|
protected StyleDefault(name: "ColorThumbOn" | "ColorFrameOn" | "ColorThumbOff" | "ColorFrameOff"): Color | undefined;
|
|
17
|
-
private SetContentSize;
|
|
18
17
|
protected CreateDefaultContent(): void;
|
|
19
18
|
/** C# GetThumbPosForOn: track width minus thumb width and margins, in points. */
|
|
20
19
|
private ThumbPosForOn;
|
|
@@ -25,12 +25,6 @@ export class SkiaSwitch extends SkiaToggle {
|
|
|
25
25
|
};
|
|
26
26
|
return table[s]?.[name];
|
|
27
27
|
}
|
|
28
|
-
SetContentSize(w, h) {
|
|
29
|
-
if (this.WidthRequest < 0)
|
|
30
|
-
this.WidthRequest = w;
|
|
31
|
-
if (this.HeightRequest < 0)
|
|
32
|
-
this.HeightRequest = h;
|
|
33
|
-
}
|
|
34
28
|
CreateDefaultContent() {
|
|
35
29
|
const frame = new SkiaShape();
|
|
36
30
|
frame.Tag = "Frame";
|
|
@@ -5,16 +5,25 @@ import { type Color, type ScaledSize } from "../core/Types";
|
|
|
5
5
|
import { SkiaLayout } from "./SkiaLayout";
|
|
6
6
|
/**
|
|
7
7
|
* Mirrors DrawnUi SkiaToggle: base of SkiaSwitch / SkiaCheckbox / SkiaRadioButton. Tapped flips IsToggled,
|
|
8
|
-
* `Toggled` reports user changes, `ControlStyle` selects the look the default content is built with (
|
|
9
|
-
* measure, like C# CreateDefaultContent
|
|
8
|
+
* `Toggled` reports user changes, `ControlStyle` selects the look the default content is built with (at first
|
|
9
|
+
* measure, like C# CreateDefaultContent; a later change rebuilds it, C# RebuildDefaultContent). Colors left unset
|
|
10
|
+
* take the style defaults (C# SetStyleDefault).
|
|
10
11
|
*/
|
|
11
12
|
export declare abstract class SkiaToggle extends SkiaLayout {
|
|
12
|
-
|
|
13
|
+
/** Takes mouse hover unless ReceivesHover is set false (C# ReceivesHoverByDefault): switch, checkbox, radio button. */
|
|
14
|
+
protected ReceivesHoverByDefault(): boolean;
|
|
15
|
+
private controlStyle;
|
|
16
|
+
get ControlStyle(): PrebuiltControlStyle;
|
|
17
|
+
set ControlStyle(v: PrebuiltControlStyle);
|
|
13
18
|
/** Fires on every IsToggled change; C# fires only on user/external changes, not on DefaultValue application. */
|
|
14
19
|
Toggled?: (sender: SkiaToggle, value: boolean) => void;
|
|
15
20
|
DefaultValue: boolean;
|
|
16
21
|
IsAnimated: boolean;
|
|
17
|
-
|
|
22
|
+
private respondsToGestures;
|
|
23
|
+
/** False: a tap or the keyboard does not toggle it (code still can), a tap on it stops there, and it is reported disabled (drawnui-cross 6w). */
|
|
24
|
+
get RespondsToGestures(): boolean;
|
|
25
|
+
set RespondsToGestures(v: boolean);
|
|
26
|
+
protected AcceptsInput(): boolean;
|
|
18
27
|
private isToggled;
|
|
19
28
|
private colorThumbOn?;
|
|
20
29
|
private colorFrameOn?;
|
|
@@ -37,10 +46,21 @@ export declare abstract class SkiaToggle extends SkiaLayout {
|
|
|
37
46
|
/** Per-style color defaults (C# SetStyleDefault); undefined = the SkiaToggle default. */
|
|
38
47
|
protected StyleDefault(_name: "ColorThumbOn" | "ColorFrameOn" | "ColorThumbOff" | "ColorFrameOff"): Color | undefined;
|
|
39
48
|
protected OnToggledChanged(): void;
|
|
40
|
-
/** Builds the look
|
|
49
|
+
/** Builds the look (C# CreateDefaultContent) — subclasses add their views here; called again after a rebuild. */
|
|
41
50
|
protected abstract CreateDefaultContent(): void;
|
|
42
51
|
/** Pushes IsToggled + colors into the views (C# ApplyProperties). */
|
|
43
52
|
abstract ApplyProperties(): void;
|
|
53
|
+
/** Size requests the style pinned (C# SetDefaultContentSize / SetDefaultMinimumContentSize); a rebuild releases only these. */
|
|
54
|
+
private stylePinned;
|
|
55
|
+
/** C# SetDefaultContentSize: the style's size, only where the app left the request unset. */
|
|
56
|
+
protected SetContentSize(w: number, h: number): void;
|
|
57
|
+
/** C# SetDefaultMinimumContentSize (height only): the style's minimum, only where the app left it unset. */
|
|
58
|
+
protected SetMinimumContentHeight(h: number): void;
|
|
59
|
+
/**
|
|
60
|
+
* C# RebuildDefaultContent: drops the content built for the previous style and the sizes it pinned (the app's own
|
|
61
|
+
* requests stay), so the next Measure builds the current style. No-op before the first build.
|
|
62
|
+
*/
|
|
63
|
+
RebuildDefaultContent(): void;
|
|
44
64
|
/** Content (and the size requests the style sets) must exist before Measure reads WidthRequest/HeightRequest. */
|
|
45
65
|
Measure(widthConstraint: number, heightConstraint: number, scale: number): ScaledSize;
|
|
46
66
|
protected DefaultAccessibilityCanInteract(): boolean;
|
|
@@ -3,16 +3,33 @@ import { Colors } from "../core/Types";
|
|
|
3
3
|
import { SkiaLayout } from "./SkiaLayout";
|
|
4
4
|
/**
|
|
5
5
|
* Mirrors DrawnUi SkiaToggle: base of SkiaSwitch / SkiaCheckbox / SkiaRadioButton. Tapped flips IsToggled,
|
|
6
|
-
* `Toggled` reports user changes, `ControlStyle` selects the look the default content is built with (
|
|
7
|
-
* measure, like C# CreateDefaultContent
|
|
6
|
+
* `Toggled` reports user changes, `ControlStyle` selects the look the default content is built with (at first
|
|
7
|
+
* measure, like C# CreateDefaultContent; a later change rebuilds it, C# RebuildDefaultContent). Colors left unset
|
|
8
|
+
* take the style defaults (C# SetStyleDefault).
|
|
8
9
|
*/
|
|
9
10
|
export class SkiaToggle extends SkiaLayout {
|
|
10
|
-
|
|
11
|
+
/** Takes mouse hover unless ReceivesHover is set false (C# ReceivesHoverByDefault): switch, checkbox, radio button. */
|
|
12
|
+
ReceivesHoverByDefault() { return true; }
|
|
13
|
+
controlStyle = "Unset";
|
|
14
|
+
get ControlStyle() { return this.controlStyle; }
|
|
15
|
+
set ControlStyle(v) {
|
|
16
|
+
if (this.controlStyle === v)
|
|
17
|
+
return;
|
|
18
|
+
this.controlStyle = v;
|
|
19
|
+
this.RebuildDefaultContent();
|
|
20
|
+
}
|
|
11
21
|
/** Fires on every IsToggled change; C# fires only on user/external changes, not on DefaultValue application. */
|
|
12
22
|
Toggled;
|
|
13
23
|
DefaultValue = false;
|
|
14
24
|
IsAnimated = true;
|
|
15
|
-
|
|
25
|
+
respondsToGestures = true;
|
|
26
|
+
/** False: a tap or the keyboard does not toggle it (code still can), a tap on it stops there, and it is reported disabled (drawnui-cross 6w). */
|
|
27
|
+
get RespondsToGestures() { return this.respondsToGestures; }
|
|
28
|
+
set RespondsToGestures(v) { if (this.respondsToGestures !== v) {
|
|
29
|
+
this.respondsToGestures = v;
|
|
30
|
+
this.NotifyAccessibility();
|
|
31
|
+
} }
|
|
32
|
+
AcceptsInput() { return this.respondsToGestures; }
|
|
16
33
|
isToggled = false;
|
|
17
34
|
colorThumbOn;
|
|
18
35
|
colorFrameOn;
|
|
@@ -48,6 +65,40 @@ export class SkiaToggle extends SkiaLayout {
|
|
|
48
65
|
this.Toggled?.(this, this.isToggled);
|
|
49
66
|
this.NotifyAccessibility();
|
|
50
67
|
}
|
|
68
|
+
/** Size requests the style pinned (C# SetDefaultContentSize / SetDefaultMinimumContentSize); a rebuild releases only these. */
|
|
69
|
+
stylePinned = [];
|
|
70
|
+
/** C# SetDefaultContentSize: the style's size, only where the app left the request unset. */
|
|
71
|
+
SetContentSize(w, h) {
|
|
72
|
+
if (this.WidthRequest < 0) {
|
|
73
|
+
this.WidthRequest = w;
|
|
74
|
+
this.stylePinned.push("WidthRequest");
|
|
75
|
+
}
|
|
76
|
+
if (this.HeightRequest < 0) {
|
|
77
|
+
this.HeightRequest = h;
|
|
78
|
+
this.stylePinned.push("HeightRequest");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** C# SetDefaultMinimumContentSize (height only): the style's minimum, only where the app left it unset. */
|
|
82
|
+
SetMinimumContentHeight(h) {
|
|
83
|
+
if (this.MinimumHeightRequest < 0) {
|
|
84
|
+
this.MinimumHeightRequest = h;
|
|
85
|
+
this.stylePinned.push("MinimumHeightRequest");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* C# RebuildDefaultContent: drops the content built for the previous style and the sizes it pinned (the app's own
|
|
90
|
+
* requests stay), so the next Measure builds the current style. No-op before the first build.
|
|
91
|
+
*/
|
|
92
|
+
RebuildDefaultContent() {
|
|
93
|
+
if (!this.contentCreated)
|
|
94
|
+
return;
|
|
95
|
+
this.ClearChildren();
|
|
96
|
+
for (const p of this.stylePinned)
|
|
97
|
+
this[p] = -1;
|
|
98
|
+
this.stylePinned = [];
|
|
99
|
+
this.contentCreated = false;
|
|
100
|
+
this.Update();
|
|
101
|
+
}
|
|
51
102
|
/** Content (and the size requests the style sets) must exist before Measure reads WidthRequest/HeightRequest. */
|
|
52
103
|
Measure(widthConstraint, heightConstraint, scale) {
|
|
53
104
|
if (!this.contentCreated) {
|
|
@@ -62,7 +113,9 @@ export class SkiaToggle extends SkiaLayout {
|
|
|
62
113
|
get AccessibilityIsPressed() { return this.isToggled; }
|
|
63
114
|
set AccessibilityIsPressed(_v) { }
|
|
64
115
|
ProcessGestures(args, apply) {
|
|
65
|
-
if (
|
|
116
|
+
if (!this.respondsToGestures)
|
|
117
|
+
return this.BlockedGestureResult(args); // a tap does not toggle it, nor reach controls under it
|
|
118
|
+
if (args.Type === "Tapped") {
|
|
66
119
|
this.IsToggled = !this.IsToggled;
|
|
67
120
|
return this;
|
|
68
121
|
}
|
|
@@ -11,7 +11,11 @@ import { SkiaLayout } from "./SkiaLayout";
|
|
|
11
11
|
*/
|
|
12
12
|
export declare abstract class SnappingLayout extends SkiaLayout {
|
|
13
13
|
Animated: boolean;
|
|
14
|
-
|
|
14
|
+
private respondsToGestures;
|
|
15
|
+
/** False: neither a swipe nor the keyboard moves it (code still can), and it is reported disabled; its content keeps its own taps (drawnui-cross 6w). */
|
|
16
|
+
get RespondsToGestures(): boolean;
|
|
17
|
+
set RespondsToGestures(v: boolean);
|
|
18
|
+
protected AcceptsInput(): boolean;
|
|
15
19
|
IgnoreWrongDirection: boolean;
|
|
16
20
|
Bounces: boolean;
|
|
17
21
|
RubberDamping: number;
|
|
@@ -50,6 +54,8 @@ export declare abstract class SnappingLayout extends SkiaLayout {
|
|
|
50
54
|
protected StopSnapAnimators(): void;
|
|
51
55
|
/** C# SnappingLayout.Render: the transition state follows the position every frame. */
|
|
52
56
|
Render(ctx: DrawingContext): void;
|
|
57
|
+
/** Hover waits while the content slides (C# InTransition) or follows a pan. */
|
|
58
|
+
MovesContent(): boolean;
|
|
53
59
|
ClampOffset(x: number, y: number, rubber: boolean): SKPoint;
|
|
54
60
|
/** C# GetContentOffsetBounds: the extent covered by the snap points. */
|
|
55
61
|
protected BoundsFromSnapPoints(): SKRect;
|
|
@@ -59,6 +65,11 @@ export declare abstract class SnappingLayout extends SkiaLayout {
|
|
|
59
65
|
protected FindNearestAnchorInternal(current: SKPoint, _velocity: SKPoint): SKPoint;
|
|
60
66
|
/** The closest anchor lying in the direction of the velocity (dot product), else the origin. */
|
|
61
67
|
SelectNextAnchor(origin: SKPoint, velocity: SKPoint): SKPoint;
|
|
68
|
+
/**
|
|
69
|
+
* Release speed, points per second along an axis, under which the snap ignores the velocity and goes to the nearest
|
|
70
|
+
* anchor by distance (C# SnapVelocityThreshold, drawnui-cross 6o): a finger held still before lifting is not a flick.
|
|
71
|
+
*/
|
|
72
|
+
SnapVelocityThreshold: number;
|
|
62
73
|
ScrollToNearestAnchor(location: SKPoint, velocity: SKPoint): void;
|
|
63
74
|
get CanAnimate(): boolean;
|
|
64
75
|
protected GetAutoVelocity(displacement: SKPoint): SKPoint;
|
|
@@ -11,7 +11,14 @@ import { SkiaLayout } from "./SkiaLayout";
|
|
|
11
11
|
*/
|
|
12
12
|
export class SnappingLayout extends SkiaLayout {
|
|
13
13
|
Animated = true;
|
|
14
|
-
|
|
14
|
+
respondsToGestures = true;
|
|
15
|
+
/** False: neither a swipe nor the keyboard moves it (code still can), and it is reported disabled; its content keeps its own taps (drawnui-cross 6w). */
|
|
16
|
+
get RespondsToGestures() { return this.respondsToGestures; }
|
|
17
|
+
set RespondsToGestures(v) { if (this.respondsToGestures !== v) {
|
|
18
|
+
this.respondsToGestures = v;
|
|
19
|
+
this.NotifyAccessibility();
|
|
20
|
+
} }
|
|
21
|
+
AcceptsInput() { return this.respondsToGestures; }
|
|
15
22
|
IgnoreWrongDirection = false;
|
|
16
23
|
Bounces = false;
|
|
17
24
|
RubberDamping = 0.7;
|
|
@@ -80,9 +87,12 @@ export class SnappingLayout extends SkiaLayout {
|
|
|
80
87
|
}
|
|
81
88
|
/** C# SnappingLayout.Render: the transition state follows the position every frame. */
|
|
82
89
|
Render(ctx) {
|
|
90
|
+
this.Superview?.Hover?.RegisterMover(this);
|
|
83
91
|
super.Render(ctx);
|
|
84
92
|
this.InTransition = !this.CheckTransitionEnded();
|
|
85
93
|
}
|
|
94
|
+
/** Hover waits while the content slides (C# InTransition) or follows a pan. */
|
|
95
|
+
MovesContent() { return this.InTransition || this.IsUserPanning; }
|
|
86
96
|
// ---- clamping ----
|
|
87
97
|
ClampOffset(x, y, rubber) {
|
|
88
98
|
const b = this.ContentOffsetBounds;
|
|
@@ -135,9 +145,17 @@ export class SnappingLayout extends SkiaLayout {
|
|
|
135
145
|
}
|
|
136
146
|
return origin;
|
|
137
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Release speed, points per second along an axis, under which the snap ignores the velocity and goes to the nearest
|
|
150
|
+
* anchor by distance (C# SnapVelocityThreshold, drawnui-cross 6o): a finger held still before lifting is not a flick.
|
|
151
|
+
*/
|
|
152
|
+
SnapVelocityThreshold = 100;
|
|
138
153
|
ScrollToNearestAnchor(location, velocity) {
|
|
139
154
|
if (this.SnapPoints.length === 0)
|
|
140
155
|
return;
|
|
156
|
+
if (Math.abs(velocity.X) < this.SnapVelocityThreshold || Math.abs(velocity.Y) < this.SnapVelocityThreshold) {
|
|
157
|
+
velocity = new SKPoint(Math.abs(velocity.X) < this.SnapVelocityThreshold ? 0 : velocity.X, Math.abs(velocity.Y) < this.SnapVelocityThreshold ? 0 : velocity.Y);
|
|
158
|
+
}
|
|
141
159
|
const origin = this.FindNearestAnchorInternal(location, velocity);
|
|
142
160
|
const target = this.SelectNextAnchor(origin, velocity);
|
|
143
161
|
if (SnappingLayout.Dist(location, target) >= 0.5)
|