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
|
@@ -88,7 +88,7 @@ export class SkiaGridStructure {
|
|
|
88
88
|
spans = new Map();
|
|
89
89
|
explicitWidth;
|
|
90
90
|
explicitHeight;
|
|
91
|
-
constructor(grid, widthConstraint, heightConstraint, scale) {
|
|
91
|
+
constructor(grid, widthConstraint, heightConstraint, scale, children = grid.Views) {
|
|
92
92
|
this.grid = grid;
|
|
93
93
|
this.widthConstraint = widthConstraint;
|
|
94
94
|
this.heightConstraint = heightConstraint;
|
|
@@ -99,7 +99,7 @@ export class SkiaGridStructure {
|
|
|
99
99
|
this.explicitHeight = grid.HeightRequest;
|
|
100
100
|
this.Rows = this.InitializeTracks(grid.RowDefinitions, grid.DefaultRowDefinition);
|
|
101
101
|
this.Columns = this.InitializeTracks(grid.ColumnDefinitions, grid.DefaultColumnDefinition);
|
|
102
|
-
this.children =
|
|
102
|
+
this.children = children.filter((v) => v.IsVisible);
|
|
103
103
|
this.InitializeCells();
|
|
104
104
|
this.MeasureCells();
|
|
105
105
|
}
|
|
@@ -294,7 +294,20 @@ export class SkiaGridStructure {
|
|
|
294
294
|
ResolveStarRows(heightConstraint) {
|
|
295
295
|
this.ResolveStars(this.Rows, heightConstraint - this.GridHeight(), (c) => c.IsRowSpanStar, (v) => v.MeasuredSize.Units.Height);
|
|
296
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* C# SizesAutoRow: the child decides the height of its single Auto row, so it is measured with the height that row
|
|
299
|
+
* could still grow to, not with the row's current size (drawnui-cross 6p).
|
|
300
|
+
*/
|
|
301
|
+
SizesAutoRow(cell) { return cell.RowSpan === 1 && this.Rows[cell.Row].IsAuto; }
|
|
302
|
+
static NeedFillY(child) { return child.VerticalOptions === "Fill" && child.HeightRequest < 0; }
|
|
303
|
+
/**
|
|
304
|
+
* C# HeightOnAutoRow: the height the single Auto row could still grow to, unbounded for a vertical Fill child (which
|
|
305
|
+
* would take the whole constraint). The row's size so far came from a first pass at the grid's whole width, so a
|
|
306
|
+
* label wrapping in a star column was measured against its one-line height.
|
|
307
|
+
*/
|
|
308
|
+
HeightOnAutoRow(cell, child) { return SkiaGridStructure.NeedFillY(child) ? Infinity : this.AvailableHeight(cell); }
|
|
297
309
|
MeasureKnownCells() {
|
|
310
|
+
let autoRowGrew = false;
|
|
298
311
|
for (const cell of this.cells) {
|
|
299
312
|
if (!cell.NeedsKnownMeasurePass)
|
|
300
313
|
continue;
|
|
@@ -307,12 +320,30 @@ export class SkiaGridStructure {
|
|
|
307
320
|
continue;
|
|
308
321
|
const child = this.children[cell.ViewIndex];
|
|
309
322
|
const rect = this.GetCellBoundsFor(child);
|
|
310
|
-
|
|
323
|
+
if (child.IsVisible && this.SizesAutoRow(cell)) {
|
|
324
|
+
// the row grows to the child at its real width, within the room the grid has left
|
|
325
|
+
let grown = this.MeasurePx(child, rect.Width, this.HeightOnAutoRow(cell, child)).Height;
|
|
326
|
+
const available = this.AvailableHeight(cell);
|
|
327
|
+
if (isFinite(available) && grown > available)
|
|
328
|
+
grown = available;
|
|
329
|
+
if (grown > this.Rows[cell.Row].Size) {
|
|
330
|
+
this.Rows[cell.Row].Update(grown);
|
|
331
|
+
autoRowGrew = true;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
else
|
|
335
|
+
this.MeasurePx(child, rect.Width, rect.Height);
|
|
311
336
|
if (cell.IsColumnSpanStar && cell.ColumnSpan > 1)
|
|
312
337
|
this.TrackSpan({ Key: `c${cell.Column}:${cell.ColumnSpan}`, Start: cell.Column, Length: cell.ColumnSpan, IsColumn: true, Requested: rect.Width });
|
|
313
338
|
if (cell.IsRowSpanStar && cell.RowSpan > 1)
|
|
314
339
|
this.TrackSpan({ Key: `r${cell.Row}:${cell.RowSpan}`, Start: cell.Row, Length: cell.RowSpan, IsColumn: false, Requested: rect.Height });
|
|
315
340
|
}
|
|
341
|
+
if (autoRowGrew) {
|
|
342
|
+
// star rows were resolved against the smaller Auto rows: they get what is left now, nothing when Auto took it all
|
|
343
|
+
SkiaGridStructure.ZeroOutStars(this.Rows);
|
|
344
|
+
if (this.heightConstraint - this.GridHeight() > 0)
|
|
345
|
+
this.ResolveStarRows(this.heightConstraint);
|
|
346
|
+
}
|
|
316
347
|
}
|
|
317
348
|
AvailableWidth(cell) {
|
|
318
349
|
let w = 0, absolute = true;
|
|
@@ -390,10 +421,15 @@ export class SkiaGridStructure {
|
|
|
390
421
|
RemeasureChildrenAtFinalCells() {
|
|
391
422
|
for (const cell of this.cells) {
|
|
392
423
|
const child = this.children[cell.ViewIndex];
|
|
424
|
+
if (!child.IsVisible)
|
|
425
|
+
continue;
|
|
393
426
|
const rect = this.GetCellBoundsFor(child);
|
|
394
427
|
if (rect.Width <= 0 || rect.Height <= 0)
|
|
395
428
|
continue;
|
|
396
|
-
|
|
429
|
+
// on an Auto row a child that is not Fill vertically keeps the height the row can grow to, as in
|
|
430
|
+
// MeasureKnownCells: the same request returns at once, and wrapped text is not cut to the row
|
|
431
|
+
const height = this.SizesAutoRow(cell) && !SkiaGridStructure.NeedFillY(child) ? Math.max(rect.Height, this.AvailableHeight(cell)) : rect.Height;
|
|
432
|
+
const m = this.MeasurePx(child, rect.Width, height);
|
|
397
433
|
if (cell.ColumnSpan === 1 && this.Columns[cell.Column].IsAuto && m.Width > this.Columns[cell.Column].Size)
|
|
398
434
|
this.Columns[cell.Column].Update(m.Width);
|
|
399
435
|
if (cell.RowSpan === 1 && this.Rows[cell.Row].IsAuto && m.Height > this.Rows[cell.Row].Size)
|
|
@@ -4,10 +4,14 @@ import { SkiaLayout } from "./SkiaLayout";
|
|
|
4
4
|
/**
|
|
5
5
|
* Mirrors DrawnUi SkiaBackdrop: draws what is already painted beneath its box through `Blur` (points, default 5)
|
|
6
6
|
* and `Brightness` (gamma, 1 = unchanged), tinted by `BackgroundColor`; children draw first and are blurred too.
|
|
7
|
-
* The snapshot comes from the surface being drawn (`UseContext
|
|
8
|
-
*
|
|
7
|
+
* The snapshot comes from the surface being drawn (`UseContext`, the area it covers there through every ancestor
|
|
8
|
+
* transform) or the on-screen surface. Not cached (re-blurs on every frame it is drawn in); picture-cached ancestors
|
|
9
|
+
* draw live once it is inside them (HostsBackdrop); inside an Image-cached ancestor it copies that cache's surface
|
|
10
|
+
* (right when what shows through the glass is inside that cache, wrong when it should show content outside it).
|
|
9
11
|
*/
|
|
10
12
|
export declare class SkiaBackdrop extends SkiaLayout {
|
|
13
|
+
/** Samples what is drawn below: a composite above redraws it whole when something inside changes (drawnui-cross 6m). */
|
|
14
|
+
protected get SamplesContent(): boolean;
|
|
11
15
|
Blur: number;
|
|
12
16
|
Brightness: number;
|
|
13
17
|
/** Snapshot the surface currently drawn into (true) or the on-screen surface (false). */
|
|
@@ -21,5 +25,7 @@ export declare class SkiaBackdrop extends SkiaLayout {
|
|
|
21
25
|
protected PaintBackground(_ctx: DrawingContext): void;
|
|
22
26
|
protected PaintsBackgroundWithoutColor(): boolean;
|
|
23
27
|
protected Paint(ctx: DrawingContext): void;
|
|
28
|
+
/** C# StopPictureCachesAbove: the picture-cached ancestors draw live from the next frame, which this asks for. */
|
|
29
|
+
protected StopPictureCachesAbove(): void;
|
|
24
30
|
protected OnDisposing(): void;
|
|
25
31
|
}
|
|
@@ -4,10 +4,14 @@ import { SkiaLayout } from "./SkiaLayout";
|
|
|
4
4
|
/**
|
|
5
5
|
* Mirrors DrawnUi SkiaBackdrop: draws what is already painted beneath its box through `Blur` (points, default 5)
|
|
6
6
|
* and `Brightness` (gamma, 1 = unchanged), tinted by `BackgroundColor`; children draw first and are blurred too.
|
|
7
|
-
* The snapshot comes from the surface being drawn (`UseContext
|
|
8
|
-
*
|
|
7
|
+
* The snapshot comes from the surface being drawn (`UseContext`, the area it covers there through every ancestor
|
|
8
|
+
* transform) or the on-screen surface. Not cached (re-blurs on every frame it is drawn in); picture-cached ancestors
|
|
9
|
+
* draw live once it is inside them (HostsBackdrop); inside an Image-cached ancestor it copies that cache's surface
|
|
10
|
+
* (right when what shows through the glass is inside that cache, wrong when it should show content outside it).
|
|
9
11
|
*/
|
|
10
12
|
export class SkiaBackdrop extends SkiaLayout {
|
|
13
|
+
/** Samples what is drawn below: a composite above redraws it whole when something inside changes (drawnui-cross 6m). */
|
|
14
|
+
get SamplesContent() { return true; }
|
|
11
15
|
Blur = 5;
|
|
12
16
|
Brightness = 1;
|
|
13
17
|
/** Snapshot the surface currently drawn into (true) or the on-screen surface (false). */
|
|
@@ -38,16 +42,36 @@ export class SkiaBackdrop extends SkiaLayout {
|
|
|
38
42
|
}
|
|
39
43
|
if (!this.HasEffects)
|
|
40
44
|
return;
|
|
45
|
+
// C# StopPictureCachesAbove: a screen copy recorded into an ancestor's picture would be replayed frozen; this frame
|
|
46
|
+
// draws no copy, the picture-cached ancestors draw live from the next frame on (HostsBackdrop)
|
|
47
|
+
if (ctx.Context.Recording) {
|
|
48
|
+
this.StopPictureCachesAbove();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
41
51
|
// C# UseContext: snapshot the surface the pixels end up on — the on-screen surface or the nearest Image cache
|
|
42
|
-
// surface
|
|
43
|
-
//
|
|
52
|
+
// surface. Copied where the backdrop really is on that surface: its rect through the canvas matrix (ancestor
|
|
53
|
+
// scale, move, rotation, 3D, the cache origin), rounded out, clamped to the surface, and drawn back 1:1 with the
|
|
54
|
+
// matrix undone, clipped to the backdrop's own (maybe turned) rect: the blur stays the same on screen while an
|
|
55
|
+
// ancestor scales (C# 6fd3bae3). The global surface (UseContext false) is copied at the layout rect.
|
|
44
56
|
const surface = this.UseContext ? ctx.Context.Surface : this.Superview?.Surface;
|
|
45
57
|
if (!surface)
|
|
46
58
|
return;
|
|
47
59
|
surface.flush();
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
let l = Math.floor(d.Left), t = Math.floor(d.Top), r = Math.ceil(d.Right), b = Math.ceil(d.Bottom);
|
|
61
|
+
let device = null;
|
|
62
|
+
if (this.UseContext) {
|
|
63
|
+
const m = canvas.getTotalMatrix();
|
|
64
|
+
const p = CK.Matrix.mapPoints(m, [d.Left, d.Top, d.Right, d.Top, d.Right, d.Bottom, d.Left, d.Bottom]);
|
|
65
|
+
l = Math.max(0, Math.floor(Math.min(p[0], p[2], p[4], p[6])));
|
|
66
|
+
t = Math.max(0, Math.floor(Math.min(p[1], p[3], p[5], p[7])));
|
|
67
|
+
r = Math.min(surface.width(), Math.ceil(Math.max(p[0], p[2], p[4], p[6])));
|
|
68
|
+
b = Math.min(surface.height(), Math.ceil(Math.max(p[1], p[3], p[5], p[7])));
|
|
69
|
+
if (r <= l || b <= t)
|
|
70
|
+
return;
|
|
71
|
+
device = CK.Matrix.invert(m);
|
|
72
|
+
if (!device)
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
51
75
|
// whole-surface snapshot, sub-rect on draw: a bounded snapshot of a GPU surface is not origin-safe in CanvasKit
|
|
52
76
|
const image = surface.makeImageSnapshot();
|
|
53
77
|
if (!image)
|
|
@@ -61,7 +85,13 @@ export class SkiaBackdrop extends SkiaLayout {
|
|
|
61
85
|
paint.setColorFilter(gamma);
|
|
62
86
|
const saved = canvas.save();
|
|
63
87
|
canvas.clipRect(CK.LTRBRect(d.Left, d.Top, d.Right, d.Bottom), CK.ClipOp.Intersect, true);
|
|
64
|
-
|
|
88
|
+
if (device) {
|
|
89
|
+
canvas.concat(device);
|
|
90
|
+
canvas.drawImageRectOptions(image, CK.LTRBRect(l, t, r, b), CK.LTRBRect(l, t, r, b), CK.FilterMode.Nearest, CK.MipmapMode.None, paint);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
canvas.drawImageRectOptions(image, CK.LTRBRect(l, t, r, b), CK.LTRBRect(d.Left, d.Top, d.Right, d.Bottom), CK.FilterMode.Linear, CK.MipmapMode.None, paint);
|
|
94
|
+
}
|
|
65
95
|
canvas.restoreToCount(saved);
|
|
66
96
|
blur?.delete();
|
|
67
97
|
gamma?.delete();
|
|
@@ -76,5 +106,15 @@ export class SkiaBackdrop extends SkiaLayout {
|
|
|
76
106
|
p = p.Parent;
|
|
77
107
|
} });
|
|
78
108
|
}
|
|
109
|
+
/** C# StopPictureCachesAbove: the picture-cached ancestors draw live from the next frame, which this asks for. */
|
|
110
|
+
StopPictureCachesAbove() {
|
|
111
|
+
for (let p = this.Parent; p; p = p.Parent) {
|
|
112
|
+
if (p.UsingCacheType === "Operations") {
|
|
113
|
+
p.HostsBackdrop = true;
|
|
114
|
+
p.InvalidateCache();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
queueMicrotask(() => this.Update());
|
|
118
|
+
}
|
|
79
119
|
OnDisposing() { this.snapshot?.delete(); this.snapshot = undefined; }
|
|
80
120
|
}
|
|
@@ -10,6 +10,8 @@ import { type PrebuiltControlStyle, type ResolvedControlStyle } from "../core/Co
|
|
|
10
10
|
* PanThreshold. Press feedback = ApplyEffect (ripple), no darkening.
|
|
11
11
|
*/
|
|
12
12
|
export declare class SkiaButton extends SkiaLayout {
|
|
13
|
+
/** Takes mouse hover unless ReceivesHover is set false (C# ReceivesHoverByDefault). */
|
|
14
|
+
protected ReceivesHoverByDefault(): boolean;
|
|
13
15
|
static PanThreshold: number;
|
|
14
16
|
Text: string;
|
|
15
17
|
/** Look of the default content (C# SkiaButton.ControlStyle): background, corners, font, minimum size when left unset. */
|
|
@@ -24,7 +26,17 @@ export declare class SkiaButton extends SkiaLayout {
|
|
|
24
26
|
StrokeColor: Color;
|
|
25
27
|
StrokeWidth: number;
|
|
26
28
|
IsPressed: boolean;
|
|
27
|
-
|
|
29
|
+
private respondsToGestures;
|
|
30
|
+
/**
|
|
31
|
+
* False switches the button off for the user: taps, presses and keyboard activation do nothing (no Tapped, Down, Up),
|
|
32
|
+
* a tap on it does not reach controls under it, and screen readers report it disabled. Code can still change it.
|
|
33
|
+
* Same property as on toggles, sliders, scrolls and drawers (drawnui-cross 6w). Default true.
|
|
34
|
+
*/
|
|
35
|
+
get RespondsToGestures(): boolean;
|
|
36
|
+
set RespondsToGestures(v: boolean);
|
|
37
|
+
/** @deprecated The opposite of RespondsToGestures, which every control uses: set `RespondsToGestures = false`. */
|
|
38
|
+
get IsDisabled(): boolean;
|
|
39
|
+
set IsDisabled(v: boolean);
|
|
28
40
|
LockPanning: boolean;
|
|
29
41
|
/** Touch feedback played on Down (DrawnUi SkiaButton.ApplyEffect). */
|
|
30
42
|
ApplyEffect: SkiaTouchAnimation;
|
|
@@ -41,10 +53,15 @@ export declare class SkiaButton extends SkiaLayout {
|
|
|
41
53
|
static DefaultAccessibilityRole?: string;
|
|
42
54
|
protected DefaultAccessibilityLabel(): string | undefined;
|
|
43
55
|
protected DefaultAccessibilityCanInteract(): boolean;
|
|
56
|
+
/** With RespondsToGestures = false the button ignores taps, keyboard navigation skips it and it is reported disabled. */
|
|
57
|
+
protected AcceptsInput(): boolean;
|
|
44
58
|
/** The button's own BackgroundColor/CornerRadius/Stroke are the frame's; the button itself paints nothing. */
|
|
45
59
|
protected PaintBackground(): void;
|
|
46
60
|
get UsingControlStyle(): ResolvedControlStyle;
|
|
47
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* C# Create*StyleContent defaults: accent, corner radius, font size/weight, minimum content size.
|
|
63
|
+
* Every C# style calls SetDefaultMinimumContentSize(100, minH), so the width floor is 100 everywhere.
|
|
64
|
+
*/
|
|
48
65
|
private static Look;
|
|
49
66
|
protected MeasureAbsolute(w: number, h: number, scale: number): ScaledSize;
|
|
50
67
|
/** The frame sits under the padding box: arrange children in the full rect, the label centered inside padding. */
|
|
@@ -10,6 +10,8 @@ import { ResolveControlStyle } from "../core/ControlStyle";
|
|
|
10
10
|
* PanThreshold. Press feedback = ApplyEffect (ripple), no darkening.
|
|
11
11
|
*/
|
|
12
12
|
export class SkiaButton extends SkiaLayout {
|
|
13
|
+
/** Takes mouse hover unless ReceivesHover is set false (C# ReceivesHoverByDefault). */
|
|
14
|
+
ReceivesHoverByDefault() { return true; }
|
|
13
15
|
static PanThreshold = 5;
|
|
14
16
|
Text = "";
|
|
15
17
|
/** Look of the default content (C# SkiaButton.ControlStyle): background, corners, font, minimum size when left unset. */
|
|
@@ -24,7 +26,20 @@ export class SkiaButton extends SkiaLayout {
|
|
|
24
26
|
StrokeColor = Colors.Transparent;
|
|
25
27
|
StrokeWidth = 0;
|
|
26
28
|
IsPressed = false;
|
|
27
|
-
|
|
29
|
+
respondsToGestures = true;
|
|
30
|
+
/**
|
|
31
|
+
* False switches the button off for the user: taps, presses and keyboard activation do nothing (no Tapped, Down, Up),
|
|
32
|
+
* a tap on it does not reach controls under it, and screen readers report it disabled. Code can still change it.
|
|
33
|
+
* Same property as on toggles, sliders, scrolls and drawers (drawnui-cross 6w). Default true.
|
|
34
|
+
*/
|
|
35
|
+
get RespondsToGestures() { return this.respondsToGestures; }
|
|
36
|
+
set RespondsToGestures(v) { if (this.respondsToGestures !== v) {
|
|
37
|
+
this.respondsToGestures = v;
|
|
38
|
+
this.NotifyAccessibility();
|
|
39
|
+
} }
|
|
40
|
+
/** @deprecated The opposite of RespondsToGestures, which every control uses: set `RespondsToGestures = false`. */
|
|
41
|
+
get IsDisabled() { return !this.respondsToGestures; }
|
|
42
|
+
set IsDisabled(v) { this.RespondsToGestures = !v; }
|
|
28
43
|
LockPanning = false;
|
|
29
44
|
/** Touch feedback played on Down (DrawnUi SkiaButton.ApplyEffect). */
|
|
30
45
|
ApplyEffect = "Ripple"; // C# default
|
|
@@ -49,22 +64,32 @@ export class SkiaButton extends SkiaLayout {
|
|
|
49
64
|
this.label.VerticalOptions = "Center";
|
|
50
65
|
this.AddSubView(this.frame);
|
|
51
66
|
this.AddSubView(this.label);
|
|
67
|
+
// C# ButtonLabel is styled when the button builds its content, and ApplyProperties then overwrites FontFamily with
|
|
68
|
+
// the button's own (empty by default) value, which the style no longer touches. Same order here: style the caption
|
|
69
|
+
// now, MeasureAbsolute below pushes SkiaButton.FontFamily over it every measure.
|
|
70
|
+
this.frame.ApplyInitialStyles(true);
|
|
71
|
+
this.label.ApplyInitialStyles(true);
|
|
52
72
|
}
|
|
53
73
|
/** Set to `Aria.RoleButton` to expose every button (React extension). */
|
|
54
74
|
static DefaultAccessibilityRole;
|
|
55
75
|
DefaultAccessibilityLabel() { return this.Text || undefined; }
|
|
56
|
-
DefaultAccessibilityCanInteract() { return
|
|
76
|
+
DefaultAccessibilityCanInteract() { return this.respondsToGestures; }
|
|
77
|
+
/** With RespondsToGestures = false the button ignores taps, keyboard navigation skips it and it is reported disabled. */
|
|
78
|
+
AcceptsInput() { return this.respondsToGestures; }
|
|
57
79
|
/** The button's own BackgroundColor/CornerRadius/Stroke are the frame's; the button itself paints nothing. */
|
|
58
80
|
PaintBackground() { }
|
|
59
81
|
get UsingControlStyle() { return ResolveControlStyle(this.ControlStyle); }
|
|
60
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* C# Create*StyleContent defaults: accent, corner radius, font size/weight, minimum content size.
|
|
84
|
+
* Every C# style calls SetDefaultMinimumContentSize(100, minH), so the width floor is 100 everywhere.
|
|
85
|
+
*/
|
|
61
86
|
static Look(s) {
|
|
62
87
|
switch (s) {
|
|
63
|
-
case "Cupertino": return { bg: "#007AFF", radius: 8, font: 17, weight: 600, minH: 36, shadow: new SkiaShadow({ X: 0, Y: 1, Blur: 2, Opacity: 0.2, Color: Colors.Black }) };
|
|
64
|
-
case "Material": return { bg: "#2196F3", radius: 4, font: 14, weight: 0, minH: 40, shadow: new SkiaShadow({ X: 0, Y: 2, Blur: 4, Opacity: 0.3, Color: Colors.Black }) };
|
|
65
|
-
case "Material3": return { bg: "#6750A4", radius: 20, font: 14, weight: 0, minH: 40 };
|
|
66
|
-
case "Windows": return { bg: "#0078D7", radius: 4, font: 15, weight: 500, minH: 32, shadow: new SkiaShadow({ X: 0, Y: 1, Blur: 1, Opacity: 0.2, Color: Colors.Black }) };
|
|
67
|
-
default: return { bg: "#DC143C", radius: 8, font: 15, weight: 0, minH: 41 };
|
|
88
|
+
case "Cupertino": return { bg: "#007AFF", radius: 8, font: 17, weight: 600, minW: 100, minH: 36, shadow: new SkiaShadow({ X: 0, Y: 1, Blur: 2, Opacity: 0.2, Color: Colors.Black }) };
|
|
89
|
+
case "Material": return { bg: "#2196F3", radius: 4, font: 14, weight: 0, minW: 100, minH: 40, shadow: new SkiaShadow({ X: 0, Y: 2, Blur: 4, Opacity: 0.3, Color: Colors.Black }) };
|
|
90
|
+
case "Material3": return { bg: "#6750A4", radius: 20, font: 14, weight: 0, minW: 100, minH: 40 };
|
|
91
|
+
case "Windows": return { bg: "#0078D7", radius: 4, font: 15, weight: 500, minW: 100, minH: 32, shadow: new SkiaShadow({ X: 0, Y: 1, Blur: 1, Opacity: 0.2, Color: Colors.Black }) };
|
|
92
|
+
default: return { bg: "#DC143C", radius: 8, font: 15, weight: 0, minW: 100, minH: 41 };
|
|
68
93
|
}
|
|
69
94
|
}
|
|
70
95
|
MeasureAbsolute(w, h, scale) {
|
|
@@ -75,7 +100,12 @@ export class SkiaButton extends SkiaLayout {
|
|
|
75
100
|
this.frame.StrokeWidth = this.StrokeWidth;
|
|
76
101
|
if (this.frame.Shadows[0] !== look.shadow)
|
|
77
102
|
this.frame.Shadows = look.shadow ? [look.shadow] : [];
|
|
78
|
-
|
|
103
|
+
// C# SetDefaultMinimumContentSize(100, minH) from the style builder: a style default only where the app left the
|
|
104
|
+
// size unset, skipped on a Fill axis. C# adds Margins because it compares the minimum against a margin-inclusive
|
|
105
|
+
// size; here Measure applies it to the margin-excluded size and adds the margin after, so the plain value is used.
|
|
106
|
+
if (this.WidthRequest < 0 && this.MinimumWidthRequest < 0 && this.HorizontalOptions !== "Fill")
|
|
107
|
+
this.MinimumWidthRequest = look.minW;
|
|
108
|
+
if (this.HeightRequest < 0 && this.MinimumHeightRequest < 0 && this.VerticalOptions !== "Fill")
|
|
79
109
|
this.MinimumHeightRequest = look.minH;
|
|
80
110
|
this.label.Text = this.Text;
|
|
81
111
|
this.label.TextColor = this.TextColor;
|
|
@@ -113,8 +143,8 @@ export class SkiaButton extends SkiaLayout {
|
|
|
113
143
|
this.Repaint();
|
|
114
144
|
}
|
|
115
145
|
ProcessGestures(args, apply) {
|
|
116
|
-
if (this.
|
|
117
|
-
return
|
|
146
|
+
if (!this.respondsToGestures)
|
|
147
|
+
return this.BlockedGestureResult(args); // no press, no Down / Up / Tapped; a tap on it stops here
|
|
118
148
|
const point = args.Event.Location;
|
|
119
149
|
if (args.Type === "Down") {
|
|
120
150
|
this.IsPressed = true;
|
|
@@ -15,6 +15,8 @@ export interface SnapPoint {
|
|
|
15
15
|
* `DynamicSize` sizes an auto-sized carousel from the selected slide, `SwipeSpeed` / `LinearSpeedMs` tune the snap.
|
|
16
16
|
*/
|
|
17
17
|
export declare class SkiaCarousel extends SnappingLayout {
|
|
18
|
+
/** Takes mouse hover unless ReceivesHover is set false (C# ReceivesHoverByDefault). */
|
|
19
|
+
protected ReceivesHoverByDefault(): boolean;
|
|
18
20
|
IsVertical: boolean;
|
|
19
21
|
/** Side padding in points so the previous/next slides peek in. */
|
|
20
22
|
SidesOffset: number;
|
|
@@ -99,7 +101,7 @@ export declare class SkiaCarousel extends SnappingLayout {
|
|
|
99
101
|
private GetVirtualSnapPoints;
|
|
100
102
|
protected GetVirtualAnchor(current: SKPoint): SnapPoint;
|
|
101
103
|
protected FindNearestAnchorInternal(current: SKPoint, velocity: SKPoint): SKPoint;
|
|
102
|
-
/** C# SkiaCarousel.ScrollToNearestAnchor: velocity below
|
|
104
|
+
/** C# SkiaCarousel.ScrollToNearestAnchor: velocity below SnapVelocityThreshold counts as none; looped snaps around virtual anchors. */
|
|
103
105
|
ScrollToNearestAnchor(location: SKPoint, velocity: SKPoint): void;
|
|
104
106
|
/** C# SkiaCarousel.SelectNextAnchor: base choice, plus a wrap to the virtual anchor at the strip borders when looped. */
|
|
105
107
|
SelectNextAnchor(origin: SKPoint, velocity: SKPoint): SKPoint;
|
|
@@ -127,6 +129,12 @@ export declare class SkiaCarousel extends SnappingLayout {
|
|
|
127
129
|
/** C# RenderViewsList: position every slide, draw the visible ones (+ neighbours when PreloadNeighboors), release the rest. */
|
|
128
130
|
protected Paint(ctx: DrawingContext): void;
|
|
129
131
|
private SlideRect;
|
|
132
|
+
/**
|
|
133
|
+
* Whether the slide at `index` takes part in hit-testing for the frame being rendered (C# IsSlideHitTestable).
|
|
134
|
+
* Every slide drawn on screen does by default; a carousel whose slides share one rect (SkiaShaderCarousel) narrows
|
|
135
|
+
* this to the slide the user actually sees.
|
|
136
|
+
*/
|
|
137
|
+
protected IsSlideHitTestable(_index: number): boolean;
|
|
130
138
|
protected GetGestureListeners(): readonly SkiaControl[];
|
|
131
139
|
ProcessGestures(args: SkiaGesturesParameters, apply: GestureEventProcessingInfo): SkiaControl | null;
|
|
132
140
|
protected DefaultAccessibilityLabel(): string | undefined;
|
|
@@ -11,6 +11,8 @@ import { SnappingLayout } from "./SnappingLayout";
|
|
|
11
11
|
* `DynamicSize` sizes an auto-sized carousel from the selected slide, `SwipeSpeed` / `LinearSpeedMs` tune the snap.
|
|
12
12
|
*/
|
|
13
13
|
export class SkiaCarousel extends SnappingLayout {
|
|
14
|
+
/** Takes mouse hover unless ReceivesHover is set false (C# ReceivesHoverByDefault). */
|
|
15
|
+
ReceivesHoverByDefault() { return true; }
|
|
14
16
|
IsVertical = false;
|
|
15
17
|
/** Side padding in points so the previous/next slides peek in. */
|
|
16
18
|
SidesOffset = 0;
|
|
@@ -230,9 +232,10 @@ export class SkiaCarousel extends SnappingLayout {
|
|
|
230
232
|
}
|
|
231
233
|
return super.FindNearestAnchorInternal(current, velocity);
|
|
232
234
|
}
|
|
233
|
-
/** C# SkiaCarousel.ScrollToNearestAnchor: velocity below
|
|
235
|
+
/** C# SkiaCarousel.ScrollToNearestAnchor: velocity below SnapVelocityThreshold counts as none; looped snaps around virtual anchors. */
|
|
234
236
|
ScrollToNearestAnchor(location, velocity) {
|
|
235
|
-
|
|
237
|
+
const t = this.SnapVelocityThreshold;
|
|
238
|
+
velocity = new SKPoint(Math.abs(velocity.X) < t ? 0 : velocity.X, Math.abs(velocity.Y) < t ? 0 : velocity.Y);
|
|
236
239
|
if (this.ApplyLoopedLogic) {
|
|
237
240
|
const origin = this.FindNearestAnchorInternal(location, velocity);
|
|
238
241
|
const target = this.SelectNextAnchor(origin, velocity);
|
|
@@ -504,7 +507,7 @@ export class SkiaCarousel extends SnappingLayout {
|
|
|
504
507
|
view.Measure(inner.Width, inner.Height, scale); // recycled cells carry new content
|
|
505
508
|
view.Arrange(this.SlideRect(inner, this.SlideOffset(cell.offset), scale), view.WidthRequest, view.HeightRequest, scale);
|
|
506
509
|
view.Render(ctx);
|
|
507
|
-
if (cell.visible)
|
|
510
|
+
if (cell.visible && this.IsSlideHitTestable(cell.index))
|
|
508
511
|
visible.push(view);
|
|
509
512
|
}
|
|
510
513
|
this.visibleViews = visible;
|
|
@@ -514,6 +517,12 @@ export class SkiaCarousel extends SnappingLayout {
|
|
|
514
517
|
? SKRect.Create(inner.Left, inner.Top + offset.Y * scale, inner.Width, inner.Height)
|
|
515
518
|
: SKRect.Create(inner.Left + offset.X * scale, inner.Top, inner.Width, inner.Height);
|
|
516
519
|
}
|
|
520
|
+
/**
|
|
521
|
+
* Whether the slide at `index` takes part in hit-testing for the frame being rendered (C# IsSlideHitTestable).
|
|
522
|
+
* Every slide drawn on screen does by default; a carousel whose slides share one rect (SkiaShaderCarousel) narrows
|
|
523
|
+
* this to the slide the user actually sees.
|
|
524
|
+
*/
|
|
525
|
+
IsSlideHitTestable(_index) { return true; }
|
|
517
526
|
GetGestureListeners() { return this.visibleViews; }
|
|
518
527
|
// ---- gestures (port of C# SkiaCarousel.ProcessGestures) ----
|
|
519
528
|
ProcessGestures(args, apply) {
|
|
@@ -555,6 +564,10 @@ export class SkiaCarousel extends SnappingLayout {
|
|
|
555
564
|
if (!this.IsUserPanning) {
|
|
556
565
|
const movex = Math.abs(e.Distance.Total.X), movey = Math.abs(e.Distance.Total.Y);
|
|
557
566
|
const along = this.IsVertical ? movey : movex, across = this.IsVertical ? movex : movey;
|
|
567
|
+
// a first move too short to tell the direction decides nothing yet (a 1 CSS px move at devicePixelRatio 2):
|
|
568
|
+
// marking it wrong kept the whole drag dead until the next press (DrawnUi.Rust c6b22e3; C# decides on every move)
|
|
569
|
+
if (along < scale * 2 && across < scale * 2)
|
|
570
|
+
return consumedDefault;
|
|
558
571
|
if (along < scale * 2 || across > along) {
|
|
559
572
|
this.wrongDirection = true;
|
|
560
573
|
return consumedDefault;
|
|
@@ -15,7 +15,6 @@ export declare class SkiaCheckbox extends SkiaToggle {
|
|
|
15
15
|
get ColorCheckOn(): Color;
|
|
16
16
|
set ColorCheckOn(v: Color);
|
|
17
17
|
protected StyleDefault(name: "ColorThumbOn" | "ColorFrameOn" | "ColorThumbOff" | "ColorFrameOff"): Color | undefined;
|
|
18
|
-
private SetContentSize;
|
|
19
18
|
protected CreateDefaultContent(): void;
|
|
20
19
|
ApplyProperties(): void;
|
|
21
20
|
}
|
|
@@ -27,12 +27,6 @@ export class SkiaCheckbox extends SkiaToggle {
|
|
|
27
27
|
};
|
|
28
28
|
return table[this.UsingControlStyle]?.[name];
|
|
29
29
|
}
|
|
30
|
-
SetContentSize(w, h) {
|
|
31
|
-
if (this.WidthRequest < 0)
|
|
32
|
-
this.WidthRequest = w;
|
|
33
|
-
if (this.HeightRequest < 0)
|
|
34
|
-
this.HeightRequest = h;
|
|
35
|
-
}
|
|
36
30
|
CreateDefaultContent() {
|
|
37
31
|
const off = new SkiaShape();
|
|
38
32
|
off.Tag = "FrameOff";
|
|
@@ -9,6 +9,8 @@ import { SnappingLayout } from "./SnappingLayout";
|
|
|
9
9
|
* so it must be aligned to its edge by the parent (VerticalOptions="End" for FromBottom, etc.).
|
|
10
10
|
*/
|
|
11
11
|
export declare class SkiaDrawer extends SnappingLayout {
|
|
12
|
+
/** Takes mouse hover unless ReceivesHover is set false (C# ReceivesHoverByDefault). */
|
|
13
|
+
protected ReceivesHoverByDefault(): boolean;
|
|
12
14
|
Direction: DrawerDirection;
|
|
13
15
|
/** Points that stay visible when closed. */
|
|
14
16
|
HeaderSize: number;
|
|
@@ -7,6 +7,8 @@ import { SnappingLayout } from "./SnappingLayout";
|
|
|
7
7
|
* so it must be aligned to its edge by the parent (VerticalOptions="End" for FromBottom, etc.).
|
|
8
8
|
*/
|
|
9
9
|
export class SkiaDrawer extends SnappingLayout {
|
|
10
|
+
/** Takes mouse hover unless ReceivesHover is set false (C# ReceivesHoverByDefault). */
|
|
11
|
+
ReceivesHoverByDefault() { return true; }
|
|
10
12
|
Direction = "FromBottom";
|
|
11
13
|
/** Points that stay visible when closed. */
|
|
12
14
|
HeaderSize = 0;
|
|
@@ -58,7 +58,8 @@ export declare class SkiaEditor extends SkiaShape {
|
|
|
58
58
|
private readonly placeholder;
|
|
59
59
|
private scrollX;
|
|
60
60
|
private scrollY;
|
|
61
|
-
|
|
61
|
+
/** The caret blinks on a timer, asleep between blinks: no frame runs while it waits (DrawnUi.Rust editor_rules). */
|
|
62
|
+
private blinkTimer;
|
|
62
63
|
private caretVisible;
|
|
63
64
|
private subscribed;
|
|
64
65
|
private stubSelectionStop;
|
|
@@ -102,6 +103,9 @@ export declare class SkiaEditor extends SkiaShape {
|
|
|
102
103
|
set CursorPosition(v: number);
|
|
103
104
|
get SelectionLength(): number;
|
|
104
105
|
set SelectionLength(v: number);
|
|
106
|
+
/** Shows the caret for a full half second, then blinks every 500 ms. */
|
|
107
|
+
private StartBlink;
|
|
108
|
+
private StopBlink;
|
|
105
109
|
private OnCursorMoved;
|
|
106
110
|
get IsFocused(): boolean;
|
|
107
111
|
set IsFocused(v: boolean);
|
|
@@ -128,7 +132,14 @@ export declare class SkiaEditor extends SkiaShape {
|
|
|
128
132
|
private LabelPoint;
|
|
129
133
|
protected Paint(ctx: DrawingContext): void;
|
|
130
134
|
ProcessGestures(args: SkiaGesturesParameters, apply: GestureEventProcessingInfo): SkiaControl | null;
|
|
135
|
+
/** A text field is a Tab stop by default, like a native text box, with no Tapped handler needed. */
|
|
136
|
+
protected DefaultAccessibilityCanInteract(): boolean;
|
|
137
|
+
/** Keyboard navigation reached the field (Tab) or left it: take the caret and the input sink like a native text box, or release them. */
|
|
138
|
+
OnAccessibilityFocused(focused: boolean): void;
|
|
139
|
+
/** Enter / Space on the field while it has no caret start editing; while editing they are text. */
|
|
131
140
|
OnAccessibilityActivated(): void;
|
|
141
|
+
/** While editing, arrows, Home / End and PageUp / PageDown belong to the text: they never move a surrounding group. */
|
|
142
|
+
OnAccessibilityKey(_key: string): boolean;
|
|
132
143
|
protected DefaultAccessibilityLabel(): string | undefined;
|
|
133
144
|
Submit(): void;
|
|
134
145
|
private ExecuteSubmit;
|