drawnui-react 0.1.0-preview.15 → 0.1.0-preview.17
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 +1 -1
- package/SKIPPED.md +9 -8
- package/dist/controls/AnimatedFramesRenderer.d.ts +7 -0
- package/dist/controls/AnimatedFramesRenderer.js +16 -1
- package/dist/controls/DrawnGame.d.ts +4 -1
- package/dist/controls/DrawnGame.js +7 -3
- package/dist/controls/GridStructure.d.ts +13 -1
- package/dist/controls/GridStructure.js +38 -2
- package/dist/controls/SkiaBackdrop.d.ts +2 -0
- package/dist/controls/SkiaBackdrop.js +2 -0
- package/dist/controls/SkiaButton.d.ts +2 -0
- package/dist/controls/SkiaButton.js +2 -0
- package/dist/controls/SkiaCarousel.d.ts +3 -1
- package/dist/controls/SkiaCarousel.js +5 -2
- package/dist/controls/SkiaDrawer.d.ts +2 -0
- package/dist/controls/SkiaDrawer.js +2 -0
- package/dist/controls/SkiaEditor.d.ts +5 -1
- package/dist/controls/SkiaEditor.js +29 -18
- package/dist/controls/SkiaGif.d.ts +3 -0
- package/dist/controls/SkiaGif.js +11 -1
- 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/SkiaLayout.d.ts +20 -0
- package/dist/controls/SkiaLayout.js +78 -6
- package/dist/controls/SkiaLottie.js +3 -1
- package/dist/controls/SkiaProgress.d.ts +2 -1
- package/dist/controls/SkiaProgress.js +6 -3
- package/dist/controls/SkiaScroll.d.ts +16 -0
- package/dist/controls/SkiaScroll.js +85 -8
- package/dist/controls/SkiaShape.d.ts +13 -2
- package/dist/controls/SkiaShape.js +50 -10
- package/dist/controls/SkiaSlider.d.ts +7 -1
- package/dist/controls/SkiaSlider.js +12 -4
- package/dist/controls/SkiaSprite.d.ts +1 -0
- package/dist/controls/SkiaSprite.js +4 -0
- package/dist/controls/SkiaToggle.d.ts +2 -0
- package/dist/controls/SkiaToggle.js +3 -1
- package/dist/controls/SnappingLayout.d.ts +7 -0
- package/dist/controls/SnappingLayout.js +11 -0
- package/dist/core/Accessibility.d.ts +21 -0
- package/dist/core/Accessibility.js +66 -9
- package/dist/core/Animators.d.ts +28 -1
- package/dist/core/Animators.js +76 -8
- package/dist/core/Canvas.d.ts +26 -4
- package/dist/core/Canvas.js +136 -23
- package/dist/core/Easing.d.ts +12 -1
- package/dist/core/Easing.js +25 -1
- package/dist/core/Gestures.d.ts +24 -1
- package/dist/core/Gestures.js +75 -2
- 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 +97 -1
- package/dist/core/SkiaControl.js +271 -38
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/react/SkiaShell.js +33 -14
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +30 -3
- package/dist/react/reconciler.js +29 -10
- package/package.json +18 -1
package/dist/core/SkiaControl.js
CHANGED
|
@@ -134,6 +134,7 @@ export class SkiaControl {
|
|
|
134
134
|
return;
|
|
135
135
|
this.bindingContext = value;
|
|
136
136
|
this.OnBindingContextChanged();
|
|
137
|
+
this.Superview?.Hover?.RequestCheck(); // a recycled cell under a still mouse now shows another item
|
|
137
138
|
}
|
|
138
139
|
/** Index of the bound item inside its ItemsSource, -1 when not templated. */
|
|
139
140
|
ContextIndex = -1;
|
|
@@ -166,15 +167,52 @@ export class SkiaControl {
|
|
|
166
167
|
compositeSurface;
|
|
167
168
|
/** Own content or structure changed: the next composite record is a full one. */
|
|
168
169
|
compositeFull = true;
|
|
170
|
+
/** Where the kept composite surface starts relative to DrawingRect (the effects margin it was laid out with). */
|
|
171
|
+
compositeInsetX = 0;
|
|
172
|
+
compositeInsetY = 0;
|
|
169
173
|
/** Canvas-pixel bounds this control covered when its parent last recorded it (composite erase region). */
|
|
170
174
|
lastCompositeBounds;
|
|
171
|
-
/**
|
|
172
|
-
|
|
175
|
+
/**
|
|
176
|
+
* What the last composite record did (diagnostics): "full" or "partial", the number of children re-recorded and which
|
|
177
|
+
* ones; Areas: the pixels erased and drawn again for changes deeper than a child (C# Areas), Changed: the controls
|
|
178
|
+
* those areas belong to (C# Changed).
|
|
179
|
+
*/
|
|
180
|
+
LastCompositeRecord = { Mode: "full", Children: 0, Dirty: [], Areas: [], Changed: [] };
|
|
181
|
+
/** C# MaxCompositionAreas: more areas than this in one composite record draw it whole instead. */
|
|
182
|
+
static MaxCompositionAreas = 16;
|
|
183
|
+
/** C# MaxCompositionShare: areas covering more than this share of a composite draw it whole instead. */
|
|
184
|
+
static MaxCompositionShare = 0.5;
|
|
185
|
+
/**
|
|
186
|
+
* Changes deeper than a direct child since the last composite record (drawnui-cross 6m): the control, the direct child
|
|
187
|
+
* it is in, where it was drawn before the change (its own drawn bounds, as drawn) and its rect then.
|
|
188
|
+
*/
|
|
189
|
+
compositeDeep;
|
|
190
|
+
/** Set while an UpdateDraw climbs: the ancestors re-measure but an ImageComposite among them keeps its partial record. */
|
|
191
|
+
static drawOrigin = null;
|
|
173
192
|
/** The children a composite record can re-record individually; layouts return their views. */
|
|
174
193
|
GetCompositeChildren() { return []; }
|
|
175
194
|
/** C# TrackChildAsDirty (DirtyChildrenTracker): the child changed without a remeasure of this control. */
|
|
176
195
|
TrackChildAsDirty(child) { if (this.IsCacheComposite)
|
|
177
196
|
this.DirtyChildrenInternal.add(child); }
|
|
197
|
+
static rejectRect = new Float32Array(4);
|
|
198
|
+
/** Its cache must be recorded before it can be drawn (none, dirty, another type, scale, size or GPU context). */
|
|
199
|
+
CacheStale(scale) {
|
|
200
|
+
const cacheType = this.UsingCacheType;
|
|
201
|
+
if (cacheType === "None")
|
|
202
|
+
return false;
|
|
203
|
+
const o = this.RenderObject, r = cacheType === "Operations" ? this.ExpandedCacheRect(scale) : this.AlignedCacheRect(scale);
|
|
204
|
+
return this.cacheDirty || !o || o.Type !== cacheType || o.Epoch !== (this.Superview?.GpuEpoch ?? 0) || o.Scale !== scale
|
|
205
|
+
|| Math.round(o.Bounds.Width) !== Math.round(r.Width) || Math.round(o.Bounds.Height) !== Math.round(r.Height);
|
|
206
|
+
}
|
|
207
|
+
/** Its rect grown by its effects margin, through the matrix it is drawn with, lies outside the canvas clip. */
|
|
208
|
+
OutsideClip(canvas, scale) {
|
|
209
|
+
const b = SkiaControl.MapRect(this.RenderTransformMatrix, this.ExpandedCacheRect(scale)), q = SkiaControl.rejectRect;
|
|
210
|
+
q[0] = b.Left;
|
|
211
|
+
q[1] = b.Top;
|
|
212
|
+
q[2] = b.Right;
|
|
213
|
+
q[3] = b.Bottom;
|
|
214
|
+
return canvas.quickReject(q);
|
|
215
|
+
}
|
|
178
216
|
/** Drawn bounds in canvas pixels including transforms and effects margins (C# GetTransformedDirtyBounds). */
|
|
179
217
|
GetTransformedDirtyBounds() {
|
|
180
218
|
const r = this.ExpandedCacheRect(this.RenderingScale), m = this.RenderTransformMatrix;
|
|
@@ -193,6 +231,57 @@ export class SkiaControl {
|
|
|
193
231
|
/** Consume every gesture that lands on this control so nothing below (z-order) receives it. */
|
|
194
232
|
BlockGesturesBelow = false;
|
|
195
233
|
LockChildrenGestures = "Disabled";
|
|
234
|
+
// ---- hover (drawnui-cross 6m, C# ReceivesHover / IsHovered / HoverChanged) ----
|
|
235
|
+
receivesHover;
|
|
236
|
+
/**
|
|
237
|
+
* Whether this control takes mouse hover: the canvas sets IsHovered while the mouse is over it. Opt-in: false by
|
|
238
|
+
* default, true by default only in the controls that always had hover (SkiaButton, SkiaSlider, the toggles,
|
|
239
|
+
* SkiaRadioButton, SkiaCarousel, SkiaDrawer), never because a control has a tap handler. A HoverChanged handler turns
|
|
240
|
+
* it on (C# fluent OnHovered) unless it is set.
|
|
241
|
+
*/
|
|
242
|
+
get ReceivesHover() { return this.receivesHover ?? (this.HoverChanged ? true : this.ReceivesHoverByDefault()); }
|
|
243
|
+
set ReceivesHover(value) { this.receivesHover = value; }
|
|
244
|
+
/** The value of ReceivesHover until it is set. */
|
|
245
|
+
ReceivesHoverByDefault() { return false; }
|
|
246
|
+
isHovered = false;
|
|
247
|
+
/**
|
|
248
|
+
* True while the mouse is over this control and it takes hover. Every such control under the pointer is hovered at
|
|
249
|
+
* once (a card and the button inside it, as CSS :hover); it stays as it is while content moves under the pointer and
|
|
250
|
+
* is checked again when that stops. Touch never hovers.
|
|
251
|
+
*/
|
|
252
|
+
get IsHovered() { return this.isHovered; }
|
|
253
|
+
/** IsHovered changed: true when the mouse came over this control, false when it left (C# HoverChanged / OnHovered). */
|
|
254
|
+
HoverChanged;
|
|
255
|
+
/** Set by the canvas's HoverManager. */
|
|
256
|
+
ApplyHover(on) {
|
|
257
|
+
if (this.isHovered === on)
|
|
258
|
+
return;
|
|
259
|
+
this.isHovered = on;
|
|
260
|
+
this.HoverChanged?.(this, on);
|
|
261
|
+
}
|
|
262
|
+
/** A scroll, carousel or drawer moving its content right now: hover waits until it stops (C# IsScrolling / InTransition). */
|
|
263
|
+
MovesContent() { return false; }
|
|
264
|
+
/**
|
|
265
|
+
* Hover hit path (HoverManager): this control is under the point (in its own space), its children under it follow,
|
|
266
|
+
* top-most first, by the routing rules of ProcessGestures. True when this subtree blocks gestures below it, so the
|
|
267
|
+
* controls under it get no hover (a popup over a list).
|
|
268
|
+
*/
|
|
269
|
+
CollectHovered(point, into, xs, ys) {
|
|
270
|
+
into.push(this);
|
|
271
|
+
xs?.push(point.X);
|
|
272
|
+
ys?.push(point.Y);
|
|
273
|
+
if (!this.CheckChildrenGesturesLocked("Pointer")) {
|
|
274
|
+
const listeners = this.GetGestureListeners();
|
|
275
|
+
for (let i = listeners.length - 1; i >= 0; i--) {
|
|
276
|
+
const child = listeners[i];
|
|
277
|
+
if (!child.IsVisible || child.InputTransparent || !this.IsGestureForChild(child, point))
|
|
278
|
+
continue;
|
|
279
|
+
if (child.CollectHovered(child.TransformPointToLocalSpace(point), into, xs, ys))
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return this.BlockGesturesBelow;
|
|
284
|
+
}
|
|
196
285
|
// ---- touch feedback (same names as DrawnUi) ----
|
|
197
286
|
TouchEffectColor = Colors.White;
|
|
198
287
|
/** Plays a ripple (from the tap point) when Tapped fires. */
|
|
@@ -277,6 +366,11 @@ export class SkiaControl {
|
|
|
277
366
|
if (!SkiaStyles.IsEmpty)
|
|
278
367
|
SkiaStyles.Apply(this, atConstruction);
|
|
279
368
|
}
|
|
369
|
+
/**
|
|
370
|
+
* C# IsChildrenItem: added by the app (JSX children or the `Children` list), not a part the control made itself. A new
|
|
371
|
+
* `Children` list drops only these, so a control's own parts keep their place.
|
|
372
|
+
*/
|
|
373
|
+
IsChildrenItem = false;
|
|
280
374
|
AddSubView(_control) { throw new Error(`DrawnUi: ${this.constructor.name} cannot host children`); }
|
|
281
375
|
InsertSubView(_index, control) { this.AddSubView(control); }
|
|
282
376
|
RemoveSubView(_control) { }
|
|
@@ -443,6 +537,8 @@ export class SkiaControl {
|
|
|
443
537
|
* host's pointer cursor. Default: the control as a whole (AccessibilityCanInteract); SkiaLabel adds its tappable spans.
|
|
444
538
|
*/
|
|
445
539
|
WantsPointerCursor(_x, _y) { return this.AccessibilityCanInteract; }
|
|
540
|
+
/** A range control's value for screen readers (slider, progress); undefined for everything else (drawnui-cross 6c). */
|
|
541
|
+
get AccessibilityValue() { return undefined; }
|
|
446
542
|
/** aria-pressed for toggles; undefined = not a toggle. */
|
|
447
543
|
get AccessibilityIsPressed() { return this.accessibilityIsPressed; }
|
|
448
544
|
set AccessibilityIsPressed(v) { if (this.accessibilityIsPressed !== v) {
|
|
@@ -606,6 +702,23 @@ export class SkiaControl {
|
|
|
606
702
|
const offsetOnly = (this.Left !== 0 || this.Top !== 0) && this.UsingCacheType !== "None" && this.TranslationX === 0 && this.TranslationY === 0
|
|
607
703
|
&& this.Rotation === 0 && this.ScaleX === 1 && this.ScaleY === 1 && this.SkewX === 0 && this.SkewY === 0;
|
|
608
704
|
const needTransform = this.HasTransform && !offsetOnly;
|
|
705
|
+
let dx = 0, dy = 0;
|
|
706
|
+
if (needTransform) {
|
|
707
|
+
this.RenderTransformMatrix = this.CreateRenderTransformMatrix(this.DrawingRect, ctx.Scale);
|
|
708
|
+
}
|
|
709
|
+
else if (offsetOnly) {
|
|
710
|
+
dx = this.Left * ctx.Scale;
|
|
711
|
+
dy = this.Top * ctx.Scale;
|
|
712
|
+
this.RenderTransformMatrix = Super.CK.Matrix.translated(dx, dy); // gestures / accessibility still map through it
|
|
713
|
+
}
|
|
714
|
+
else {
|
|
715
|
+
this.RenderTransformMatrix = undefined;
|
|
716
|
+
}
|
|
717
|
+
// drawn entirely outside the clip: skipped with its subtree (C# layout Virtualisation, DrawnUi.Rust); the matrix
|
|
718
|
+
// above stays current for hit tests. A cache that must be recorded is still recorded here, as before, so its cost
|
|
719
|
+
// does not move into the frame where the control scrolls in (DrawnUi.Rust records it then)
|
|
720
|
+
if (this.OutsideClip(canvas, ctx.Scale) && !this.CacheStale(ctx.Scale))
|
|
721
|
+
return;
|
|
609
722
|
let saved = false;
|
|
610
723
|
// same as DrawnUi: opacity = a layer with alpha, transforms = canvas matrix around the whole subtree (cache included)
|
|
611
724
|
if (applyOpacity) {
|
|
@@ -619,19 +732,8 @@ export class SkiaControl {
|
|
|
619
732
|
canvas.save();
|
|
620
733
|
saved = true;
|
|
621
734
|
}
|
|
622
|
-
|
|
623
|
-
if (needTransform) {
|
|
624
|
-
this.RenderTransformMatrix = this.CreateRenderTransformMatrix(this.DrawingRect, ctx.Scale);
|
|
735
|
+
if (needTransform)
|
|
625
736
|
canvas.concat(this.RenderTransformMatrix);
|
|
626
|
-
}
|
|
627
|
-
else if (offsetOnly) {
|
|
628
|
-
dx = this.Left * ctx.Scale;
|
|
629
|
-
dy = this.Top * ctx.Scale;
|
|
630
|
-
this.RenderTransformMatrix = Super.CK.Matrix.translated(dx, dy); // gestures / accessibility still map through it
|
|
631
|
-
}
|
|
632
|
-
else {
|
|
633
|
-
this.RenderTransformMatrix = undefined;
|
|
634
|
-
}
|
|
635
737
|
if (this.IsClippedToBounds) {
|
|
636
738
|
if (!saved) {
|
|
637
739
|
canvas.save();
|
|
@@ -696,10 +798,7 @@ export class SkiaControl {
|
|
|
696
798
|
const epoch = this.Superview?.GpuEpoch ?? 0;
|
|
697
799
|
if (this.RenderObjectPrevious && this.RenderObjectPrevious.Epoch !== epoch)
|
|
698
800
|
this.DisposePrevious(); // made on a lost GPU context
|
|
699
|
-
|
|
700
|
-
|| this.RenderObject.Scale !== ctx.Scale
|
|
701
|
-
|| Math.round(this.RenderObject.Bounds.Width) !== Math.round(r.Width) || Math.round(this.RenderObject.Bounds.Height) !== Math.round(r.Height);
|
|
702
|
-
if (stale)
|
|
801
|
+
if (this.CacheStale(ctx.Scale))
|
|
703
802
|
this.CreateRenderingObject({ ...ctx, Destination: this.DrawingRect }, cacheType);
|
|
704
803
|
if (this.RenderObject) {
|
|
705
804
|
// C# DrawRenderObject: with post renderers an Image cache is not blitted, the effects sample it (CachedImage)
|
|
@@ -813,14 +912,46 @@ export class SkiaControl {
|
|
|
813
912
|
}
|
|
814
913
|
const canvas = surface.getCanvas();
|
|
815
914
|
const children = this.GetCompositeChildren();
|
|
816
|
-
const
|
|
915
|
+
const deep = this.compositeDeep;
|
|
916
|
+
// the same size with another effects margin moves the content inside the surface: record whole (C# 97683f19)
|
|
917
|
+
const insetX = r.Left - this.DrawingRect.Left, insetY = r.Top - this.DrawingRect.Top;
|
|
918
|
+
const sameInset = insetX === this.compositeInsetX && insetY === this.compositeInsetY;
|
|
919
|
+
this.compositeInsetX = insetX;
|
|
920
|
+
this.compositeInsetY = insetY;
|
|
921
|
+
let partial = sameGeometry && sameInset && !this.compositeFull && children.length > 0 && (this.DirtyChildrenInternal.size > 0 || !!deep?.length);
|
|
922
|
+
// changes deeper than a child: only their areas, the child they are in drawn clipped to them (drawnui-cross 6m)
|
|
923
|
+
const areas = [], changed = [], inside = new Set();
|
|
924
|
+
if (partial && deep?.length) {
|
|
925
|
+
let covered = 0;
|
|
926
|
+
for (const d of deep) {
|
|
927
|
+
if (!children.includes(d.Child) || this.DirtyChildrenInternal.has(d.Child))
|
|
928
|
+
continue;
|
|
929
|
+
const area = SkiaControl.DeepArea(d, this, ctx.Scale);
|
|
930
|
+
if (area === null) {
|
|
931
|
+
partial = false;
|
|
932
|
+
break;
|
|
933
|
+
}
|
|
934
|
+
if (!area) {
|
|
935
|
+
this.DirtyChildrenInternal.add(d.Child);
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
areas.push(area);
|
|
939
|
+
changed.push(d.Origin);
|
|
940
|
+
inside.add(d.Child);
|
|
941
|
+
covered += area.Width * area.Height;
|
|
942
|
+
}
|
|
943
|
+
if (areas.length > SkiaControl.MaxCompositionAreas || covered > r.Width * r.Height * SkiaControl.MaxCompositionShare)
|
|
944
|
+
partial = false;
|
|
945
|
+
}
|
|
946
|
+
if (deep)
|
|
947
|
+
deep.length = 0;
|
|
817
948
|
const offset = prev && sameGeometry ? { X: r.Left - prev.Bounds.Left, Y: r.Top - prev.Bounds.Top } : { X: 0, Y: 0 };
|
|
818
949
|
const saved = canvas.save();
|
|
819
950
|
canvas.translate(-r.Left, -r.Top);
|
|
820
951
|
if (partial) {
|
|
821
952
|
// dirty = reported children + siblings intersecting their old or new bounds (C# makes intersecting children dirty too)
|
|
822
953
|
const dirty = new Set();
|
|
823
|
-
const rects = [];
|
|
954
|
+
const rects = [...areas];
|
|
824
955
|
const boundsOf = (c) => {
|
|
825
956
|
const out = [c.GetTransformedDirtyBounds()];
|
|
826
957
|
if (c.lastCompositeBounds)
|
|
@@ -836,7 +967,7 @@ export class SkiaControl {
|
|
|
836
967
|
while (grew) {
|
|
837
968
|
grew = false;
|
|
838
969
|
for (const c of children) {
|
|
839
|
-
if (dirty.has(c) || !c.IsVisible)
|
|
970
|
+
if (dirty.has(c) || inside.has(c) || !c.IsVisible)
|
|
840
971
|
continue;
|
|
841
972
|
const own = boundsOf(c);
|
|
842
973
|
if (rects.some((d) => own.some((o) => o.Left < d.Right && d.Left < o.Right && o.Top < d.Bottom && d.Top < o.Bottom))) {
|
|
@@ -863,14 +994,18 @@ export class SkiaControl {
|
|
|
863
994
|
this.DirtyChildrenInternal.clear();
|
|
864
995
|
for (const c of dirty)
|
|
865
996
|
this.DirtyChildrenInternal.add(c);
|
|
997
|
+
for (const c of inside)
|
|
998
|
+
this.DirtyChildrenInternal.add(c);
|
|
999
|
+
// children entirely outside the redrawn areas are skipped by their own clip check (Render)
|
|
866
1000
|
this.PaintContent({ ...ctx, Context: { Canvas: canvas, Surface: surface, Origin: { X: r.Left, Y: r.Top } } });
|
|
867
1001
|
this.IsRenderingWithComposition = false;
|
|
868
|
-
|
|
1002
|
+
const redrawn = children.filter((c) => this.DirtyChildrenInternal.has(c));
|
|
1003
|
+
this.LastCompositeRecord = { Mode: "partial", Children: redrawn.length, Dirty: redrawn, Areas: areas, Changed: changed };
|
|
869
1004
|
}
|
|
870
1005
|
else {
|
|
871
1006
|
canvas.clear(CK.TRANSPARENT);
|
|
872
1007
|
this.PaintContent({ ...ctx, Context: { Canvas: canvas, Surface: surface, Origin: { X: r.Left, Y: r.Top } } });
|
|
873
|
-
this.LastCompositeRecord = { Mode: "full", Children: children.length, Dirty: children };
|
|
1008
|
+
this.LastCompositeRecord = { Mode: "full", Children: children.length, Dirty: children, Areas: [], Changed: [] };
|
|
874
1009
|
}
|
|
875
1010
|
canvas.restoreToCount(saved);
|
|
876
1011
|
for (const c of children)
|
|
@@ -916,7 +1051,11 @@ export class SkiaControl {
|
|
|
916
1051
|
/** Marks the cache stale; re-recorded on the next frame (a composite records fully: its own content changed). */
|
|
917
1052
|
InvalidateCache() { this.cacheDirty = true; this.compositeFull = true; }
|
|
918
1053
|
/** Effects margin recomputed on the next use (C# InvalidateEffectsMargin). */
|
|
919
|
-
InvalidateEffectsMargin() {
|
|
1054
|
+
InvalidateEffectsMargin() {
|
|
1055
|
+
// a layout's margin aggregates what its children paint outside it: every ancestor recomputes too (C# 97683f19)
|
|
1056
|
+
for (let c = this; c; c = c.Parent)
|
|
1057
|
+
c.effectsMarginCache = undefined;
|
|
1058
|
+
}
|
|
920
1059
|
/** Draws PostAnimators above content; an effect returning true asks for another frame. */
|
|
921
1060
|
ExecutePostAnimators(ctx) {
|
|
922
1061
|
if (this.PostAnimators.length === 0)
|
|
@@ -1127,22 +1266,107 @@ export class SkiaControl {
|
|
|
1127
1266
|
}
|
|
1128
1267
|
/** Redraw without remeasure and WITHOUT dropping caches (position/overlay changes). */
|
|
1129
1268
|
Repaint() {
|
|
1130
|
-
this.
|
|
1269
|
+
if (!this.UnderHiddenParent())
|
|
1270
|
+
this.Superview?.Update();
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* A parent (not the control itself) is hidden: a change here asks for no frame and the latest state shows when the
|
|
1274
|
+
* parent is shown again (C# 329f6c44 HiddenUpdateTests, DrawnUi.Rust hidden_updates). The control's own visibility
|
|
1275
|
+
* change still draws: it is the one that hides or shows it.
|
|
1276
|
+
*/
|
|
1277
|
+
UnderHiddenParent() {
|
|
1278
|
+
for (let p = this.Parent; p; p = p.Parent)
|
|
1279
|
+
if (!p.IsVisible)
|
|
1280
|
+
return true;
|
|
1281
|
+
return false;
|
|
1131
1282
|
}
|
|
1283
|
+
/** The control or a parent is hidden: its animators pause until it shows again. */
|
|
1284
|
+
HiddenInTree() { return !this.IsVisible || this.UnderHiddenParent(); }
|
|
1285
|
+
/** The control an invalidation started at while it climbs (InvalidateMeasure): a hidden parent of it wakes no frame. */
|
|
1286
|
+
static invalidateOrigin = null;
|
|
1132
1287
|
/**
|
|
1133
1288
|
* Transform / Opacity changed: this control's own cache is still valid (content unchanged) but every ancestor
|
|
1134
1289
|
* cache holds its composited output, so those are staled before redrawing (DrawnUi RedrawCanvas + parent invalidation).
|
|
1135
1290
|
*/
|
|
1136
1291
|
RepaintComposition() {
|
|
1137
|
-
|
|
1138
|
-
|
|
1292
|
+
this.ReportToComposites();
|
|
1293
|
+
for (let p = this.Parent; p; p = p.Parent)
|
|
1139
1294
|
p.cacheDirty = true;
|
|
1140
|
-
p.TrackChildAsDirty(child);
|
|
1141
|
-
child = p;
|
|
1142
|
-
p = p.Parent;
|
|
1143
|
-
}
|
|
1144
1295
|
this.Repaint();
|
|
1145
1296
|
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Update() for a change of how this control looks, not of its size (a color set through props, C# NeedDraw): the same
|
|
1299
|
+
* re-measure and cache invalidation, but an ImageComposite above redraws only this control's area instead of
|
|
1300
|
+
* recording whole (drawnui-cross 6m). The record still goes whole when the control's rect changed after all.
|
|
1301
|
+
*/
|
|
1302
|
+
UpdateDraw() {
|
|
1303
|
+
this.ReportToComposites();
|
|
1304
|
+
const outer = SkiaControl.drawOrigin;
|
|
1305
|
+
SkiaControl.drawOrigin = this;
|
|
1306
|
+
try {
|
|
1307
|
+
this.InvalidateMeasure();
|
|
1308
|
+
}
|
|
1309
|
+
finally {
|
|
1310
|
+
SkiaControl.drawOrigin = outer;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* A change of this control that keeps its layout, to every ImageComposite above: a direct child is drawn again whole
|
|
1315
|
+
* (C# TrackChildAsDirty), a deeper control by its area, read now as it was drawn (its last matrix and effects margin).
|
|
1316
|
+
*/
|
|
1317
|
+
ReportToComposites() {
|
|
1318
|
+
let before;
|
|
1319
|
+
let child = this;
|
|
1320
|
+
for (let p = this.Parent; p; child = p, p = p.Parent) {
|
|
1321
|
+
if (!p.IsCacheComposite)
|
|
1322
|
+
continue;
|
|
1323
|
+
if (child === this) {
|
|
1324
|
+
p.TrackChildAsDirty(child);
|
|
1325
|
+
continue;
|
|
1326
|
+
}
|
|
1327
|
+
const deep = p.compositeDeep ??= [];
|
|
1328
|
+
if (deep.some((d) => d.Origin === this))
|
|
1329
|
+
continue;
|
|
1330
|
+
before ??= this.GetTransformedDirtyBounds();
|
|
1331
|
+
const r = this.DrawingRect;
|
|
1332
|
+
deep.push({ Origin: this, Child: child, Before: before, Rect: new SKRect(r.Left, r.Top, r.Right, r.Bottom) });
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
/** The matrix this control will be drawn with (Render), from its current properties; undefined when none. */
|
|
1336
|
+
CurrentMatrix(scale) {
|
|
1337
|
+
return this.HasTransform ? this.CreateRenderTransformMatrix(this.DrawingRect, scale) : undefined;
|
|
1338
|
+
}
|
|
1339
|
+
/** Where this control will be drawn: its rect grown by its effects margin, through its current matrix. */
|
|
1340
|
+
DrawnBoundsNow(scale) { return SkiaControl.MapRect(this.CurrentMatrix(scale), this.ExpandedCacheRect(scale)); }
|
|
1341
|
+
static MapRect(m, r) {
|
|
1342
|
+
if (!m)
|
|
1343
|
+
return r;
|
|
1344
|
+
const pts = Super.CK.Matrix.mapPoints(m, [r.Left, r.Top, r.Right, r.Top, r.Right, r.Bottom, r.Left, r.Bottom]);
|
|
1345
|
+
return new SKRect(Math.min(pts[0], pts[2], pts[4], pts[6]), Math.min(pts[1], pts[3], pts[5], pts[7]), Math.max(pts[0], pts[2], pts[4], pts[6]), Math.max(pts[1], pts[3], pts[5], pts[7]));
|
|
1346
|
+
}
|
|
1347
|
+
/** Its output depends on all of its content (a visual effect; a backdrop samples below): a change inside redraws it whole. */
|
|
1348
|
+
get SamplesContent() { return !this.DisableEffects && this.visualEffects.length > 0; }
|
|
1349
|
+
/**
|
|
1350
|
+
* Where a deep change shows inside the composite (drawnui-cross 6m rule b): the control as drawn before and as drawn
|
|
1351
|
+
* now, then through the transform of every ancestor up to the composite. undefined: draw the child whole (an effect
|
|
1352
|
+
* or a backdrop on the way, the control gone from the child); null: the layout changed, record whole.
|
|
1353
|
+
*/
|
|
1354
|
+
static DeepArea(d, composite, scale) {
|
|
1355
|
+
const o = d.Origin, r = o.DrawingRect;
|
|
1356
|
+
if (r.Left !== d.Rect.Left || r.Top !== d.Rect.Top || r.Right !== d.Rect.Right || r.Bottom !== d.Rect.Bottom)
|
|
1357
|
+
return null;
|
|
1358
|
+
if (!o.IsVisible)
|
|
1359
|
+
return undefined;
|
|
1360
|
+
const now = o.DrawnBoundsNow(scale);
|
|
1361
|
+
let area = new SKRect(Math.min(now.Left, d.Before.Left), Math.min(now.Top, d.Before.Top), Math.max(now.Right, d.Before.Right), Math.max(now.Bottom, d.Before.Bottom));
|
|
1362
|
+
let p = o.Parent, child = o;
|
|
1363
|
+
for (; p && p !== composite; child = p, p = p.Parent) {
|
|
1364
|
+
if (p.SamplesContent || !p.IsVisible)
|
|
1365
|
+
return undefined;
|
|
1366
|
+
area = SkiaControl.MapRect(p.CurrentMatrix(scale), area);
|
|
1367
|
+
}
|
|
1368
|
+
return p === composite && child === d.Child ? area : undefined;
|
|
1369
|
+
}
|
|
1146
1370
|
/**
|
|
1147
1371
|
* DrawnUi IsParentIndependent: this control's size never changes the layout around it, so its own invalidation
|
|
1148
1372
|
* stops here (ancestors composite again, nothing above re-measures). The parent re-measures it on draw when
|
|
@@ -1153,14 +1377,23 @@ export class SkiaControl {
|
|
|
1153
1377
|
InvalidateMeasure() {
|
|
1154
1378
|
this.NeedMeasure = true;
|
|
1155
1379
|
this.cacheDirty = true;
|
|
1156
|
-
|
|
1380
|
+
// structure may change: a composite cannot patch it; a draw-only change (UpdateDraw) reported its area instead
|
|
1381
|
+
if (SkiaControl.drawOrigin === null || SkiaControl.drawOrigin === this)
|
|
1382
|
+
this.compositeFull = true;
|
|
1157
1383
|
this.effectsMarginCache = undefined;
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
this.Parent.
|
|
1162
|
-
|
|
1163
|
-
this.
|
|
1384
|
+
const outer = SkiaControl.invalidateOrigin;
|
|
1385
|
+
SkiaControl.invalidateOrigin ??= this;
|
|
1386
|
+
try {
|
|
1387
|
+
if (this.Parent && this.IsParentIndependent)
|
|
1388
|
+
this.RepaintComposition();
|
|
1389
|
+
else if (this.Parent)
|
|
1390
|
+
this.Parent.InvalidateMeasure();
|
|
1391
|
+
else if (!SkiaControl.invalidateOrigin.UnderHiddenParent())
|
|
1392
|
+
this.Superview?.Update();
|
|
1393
|
+
}
|
|
1394
|
+
finally {
|
|
1395
|
+
SkiaControl.invalidateOrigin = outer;
|
|
1396
|
+
}
|
|
1164
1397
|
}
|
|
1165
1398
|
// ---- gestures (same names as DrawnUi) ----
|
|
1166
1399
|
/** Hit rect in pixels (no transforms in this port, so it is DrawingRect). */
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./core/SkiaControl";
|
|
|
9
9
|
export * from "./core/Canvas";
|
|
10
10
|
export * from "./controls/SkiaLayout";
|
|
11
11
|
export * from "./core/Accessibility";
|
|
12
|
+
export * from "./core/Hover";
|
|
12
13
|
export * from "./controls/SkiaLabel";
|
|
13
14
|
export * from "./controls/TextSpan";
|
|
14
15
|
export * from "./controls/SkiaRichLabel";
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./core/SkiaControl";
|
|
|
9
9
|
export * from "./core/Canvas";
|
|
10
10
|
export * from "./controls/SkiaLayout";
|
|
11
11
|
export * from "./core/Accessibility";
|
|
12
|
+
export * from "./core/Hover";
|
|
12
13
|
export * from "./controls/SkiaLabel";
|
|
13
14
|
export * from "./controls/TextSpan";
|
|
14
15
|
export * from "./controls/SkiaRichLabel";
|
package/dist/react/SkiaShell.js
CHANGED
|
@@ -389,6 +389,12 @@ export const SkiaShell = forwardRef(function SkiaShell({ Routes, children, NavBa
|
|
|
389
389
|
prev ? prev.TranslateToAsync(-dir * w, 0, TabsAnimationSpeed, TabsEasing) : Promise.resolve(),
|
|
390
390
|
]), TabsAnimationSpeed + 500);
|
|
391
391
|
}
|
|
392
|
+
// the tab left stays mounted (its state kept): hidden first, then back in place for its next turn
|
|
393
|
+
if (prev) {
|
|
394
|
+
prev.IsVisible = false;
|
|
395
|
+
prev.TranslationX = 0;
|
|
396
|
+
prev.TranslationY = 0;
|
|
397
|
+
}
|
|
392
398
|
setTabTransition(null);
|
|
393
399
|
}, [hasTabs, Tabs, AnimateTabs, TabsAnimationSpeed]);
|
|
394
400
|
// popstate: the browser popped OUR last entry (back) — or moved forward to a hash we can rebuild
|
|
@@ -405,7 +411,13 @@ export const SkiaShell = forwardRef(function SkiaShell({ Routes, children, NavBa
|
|
|
405
411
|
}
|
|
406
412
|
const onPop = () => {
|
|
407
413
|
const depth = window.history.state?.[HISTORY_KEY] ?? 0;
|
|
408
|
-
|
|
414
|
+
const target = parseHash();
|
|
415
|
+
// a Back lands on one of OUR entries: its hash is the stack it was pushed with (the first N pages, N = the page
|
|
416
|
+
// entries below it). A typed hash or a plain <a href="#/route"> makes a new entry with no state (depth 0):
|
|
417
|
+
// that is a navigation to the hash, not a Back to the root
|
|
418
|
+
const pagesBelow = history.current.slice(0, depth).filter((k) => k === "page").length;
|
|
419
|
+
const back = depth < history.current.length && target.join("/") === live.current.stack.slice(0, pagesBelow).join("/");
|
|
420
|
+
if (back) {
|
|
409
421
|
// back: unwind our entries down to depth
|
|
410
422
|
void (async () => {
|
|
411
423
|
while (history.current.length > depth) {
|
|
@@ -441,9 +453,8 @@ export const SkiaShell = forwardRef(function SkiaShell({ Routes, children, NavBa
|
|
|
441
453
|
})();
|
|
442
454
|
}
|
|
443
455
|
else {
|
|
444
|
-
// forward (depth grew), or
|
|
456
|
+
// forward (depth grew), or another hash = a plain <a href="#/route"> link or a typed hash:
|
|
445
457
|
// the hash names the pages, rebuild the stack without animation (overlays cannot be restored)
|
|
446
|
-
const target = parseHash();
|
|
447
458
|
if (depth === history.current.length && target.join("/") === live.current.stack.join("/"))
|
|
448
459
|
return;
|
|
449
460
|
history.current = target.map(() => "page");
|
|
@@ -466,28 +477,36 @@ export const SkiaShell = forwardRef(function SkiaShell({ Routes, children, NavBa
|
|
|
466
477
|
const transitioning = transitions > 0 || leaving !== null;
|
|
467
478
|
const rootBottom = hasTabs ? TabBarHeight + insets.Bottom : 0;
|
|
468
479
|
const navTop = NavBarHeight + insets.Top;
|
|
469
|
-
|
|
480
|
+
// every tab keeps its root and its pages mounted (state, scroll positions), only the selected one shows (C# tab
|
|
481
|
+
// views, DrawnUi.Rust "tabs keep their stacks")
|
|
482
|
+
const tabIndexes = hasTabs ? Tabs.map((_, i) => i) : [0];
|
|
470
483
|
const entering = tabTransition ? { TranslationX: tabTransition.dir * 0.75 * (typeof window !== "undefined" ? window.innerWidth : 0), Opacity: 0.001, ZIndex: 1 } : {};
|
|
471
|
-
return (_jsx(ShellContext.Provider, { value: nav, children: _jsxs(SkiaLayer, { VerticalOptions: "Fill", children: [
|
|
472
|
-
|
|
473
|
-
|
|
484
|
+
return (_jsx(ShellContext.Provider, { value: nav, children: _jsxs(SkiaLayer, { VerticalOptions: "Fill", children: [tabIndexes.map((t) => {
|
|
485
|
+
const selected = t === selectedTab;
|
|
486
|
+
return (_jsx(SkiaLayer, { VerticalOptions: "Fill", Margin: new Thickness(0, 0, 0, rootBottom), BackgroundColor: hasTabs ? PageBackgroundColor : undefined, IsVisible: selected ? visiblePages.length === 0 || transitioning : tabTransition?.from === t, ZIndex: 0, ...(selected ? entering : {}), ref: (c) => { if (c)
|
|
487
|
+
tabRoots.current.set(t, c); }, children: hasTabs ? RenderRoute(Tabs[t]?.route ?? "") : children }, hasTabs ? `tab-${t}` : "root"));
|
|
488
|
+
}), tabIndexes.flatMap((t) => (t === selectedTab ? visiblePages : stacks[t] ?? []).map((r, i) => (_jsxs(PageHost, { route: r, speed: PagesAnimationSpeed, active: t === selectedTab, visible: t === selectedTab && (r === topRoute || transitioning), background: PageBackgroundColor, bottom: rootBottom, register: (c) => { if (c)
|
|
474
489
|
pageCtrls.current.set(r, c);
|
|
475
490
|
else
|
|
476
|
-
pageCtrls.current.delete(r); }, children: [_jsx(SkiaLayer, { VerticalOptions: "Fill", Margin: new Thickness(0, navTop, 0, 0), children: RenderRoute(r) }), _jsxs(SkiaLayer, { HeightRequest: navTop, BackgroundColor: NavBarColor, Padding: new Thickness(0, insets.Top, 0, 0), children: [_jsx(SkiaButton, { Text: "\u2039 Back", BackgroundColor: "#00000000", TextColor: "#6EA8FE", FontSize: 16, VerticalOptions: "Center", Margin: new Thickness(8, 0), ApplyEffect: "Ripple", Tapped: () => void GoBackAsync(), AccessibilityRole: "button", AccessibilityLabel: "Back" }), _jsx(SkiaLabel, { Text: Titles?.[RouteName(r)] ?? RouteName(r), FontSize: 18, FontFamily: "FontTextBold", TextColor: Colors.White, HorizontalOptions: "Fill", HorizontalTextAlignment: "Center", VerticalOptions: "Center", MaxLines: 1, Margin: new Thickness(96, 0), AccessibilityRole: "heading" }), _jsx(SkiaButton, { Text: "Home", BackgroundColor: "#00000000", TextColor: "#6EA8FE", FontSize: 16, HorizontalOptions: "End", VerticalOptions: "Center", Margin: new Thickness(0, 0, 8, 0), ApplyEffect: "Ripple", Tapped: () => void PopToRootAsync(), AccessibilityRole: "button", AccessibilityLabel: "Home" }), _jsx(SkiaLayer, { HeightRequest: 1, VerticalOptions: "End", BackgroundColor: "#343A40" })] })] }, `${
|
|
491
|
+
pageCtrls.current.delete(r); }, children: [_jsx(SkiaLayer, { VerticalOptions: "Fill", Margin: new Thickness(0, navTop, 0, 0), children: RenderRoute(r) }), _jsxs(SkiaLayer, { HeightRequest: navTop, BackgroundColor: NavBarColor, Padding: new Thickness(0, insets.Top, 0, 0), children: [_jsx(SkiaButton, { Text: "\u2039 Back", BackgroundColor: "#00000000", TextColor: "#6EA8FE", FontSize: 16, VerticalOptions: "Center", Margin: new Thickness(8, 0), ApplyEffect: "Ripple", Tapped: () => void GoBackAsync(), AccessibilityRole: "button", AccessibilityLabel: "Back" }), _jsx(SkiaLabel, { Text: Titles?.[RouteName(r)] ?? RouteName(r), FontSize: 18, FontFamily: "FontTextBold", TextColor: Colors.White, HorizontalOptions: "Fill", HorizontalTextAlignment: "Center", VerticalOptions: "Center", MaxLines: 1, Margin: new Thickness(96, 0), AccessibilityRole: "heading" }), _jsx(SkiaButton, { Text: "Home", BackgroundColor: "#00000000", TextColor: "#6EA8FE", FontSize: 16, HorizontalOptions: "End", VerticalOptions: "Center", Margin: new Thickness(0, 0, 8, 0), ApplyEffect: "Ripple", Tapped: () => void PopToRootAsync(), AccessibilityRole: "button", AccessibilityLabel: "Home" }), _jsx(SkiaLayer, { HeightRequest: 1, VerticalOptions: "End", BackgroundColor: "#343A40" })] })] }, `${t}:${r}#${i}`)))), hasTabs && (_jsxs(SkiaLayer, { HeightRequest: TabBarHeight + insets.Bottom, VerticalOptions: "End", BackgroundColor: TabBarColor, ZIndex: ShellDefaults.ZIndexModals - 1, BlockGesturesBelow: true, Padding: new Thickness(0, 0, 0, insets.Bottom), children: [_jsx(SkiaLayer, { HeightRequest: 1, BackgroundColor: "#343A40" }), _jsx(SkiaGrid, { ColumnDefinitions: Tabs.map(() => "*").join(","), HorizontalOptions: "Fill", VerticalOptions: "Fill", children: Tabs.map((t, i) => (_jsxs(SkiaLayer, { Column: i, HorizontalOptions: "Fill", VerticalOptions: "Fill", AnimationTapped: "Ripple", Tapped: () => void SelectTabAsync(i), AccessibilityRole: "button", AccessibilityLabel: t.title, children: [_jsx(SkiaLabel, { Text: t.title, FontSize: 13, FontFamily: i === selectedTab ? "FontTextBold" : undefined, TextColor: i === selectedTab ? ShellDefaults.TabSelectedColor : ShellDefaults.TabColor, HorizontalOptions: "Center", VerticalOptions: "Center", AccessibilityRole: "presentation" }), i === selectedTab && _jsx(SkiaShape, { Type: "Rectangle", HeightRequest: 3, WidthRequest: 36, CornerRadius: 2, BackgroundColor: ShellDefaults.TabSelectedColor, HorizontalOptions: "Center", VerticalOptions: "End" })] }, t.route))) })] })), modals.map((m) => m.node), popups.map((p) => p.node), toasts.map((t) => t.node)] }) }));
|
|
477
492
|
});
|
|
478
493
|
/** A pushed page: slides in from the right on mount (C# SkiaViewSwitcher PushView), covers what is below. */
|
|
479
|
-
function PageHost({ route, speed, visible, background, bottom, register, children }) {
|
|
494
|
+
function PageHost({ route, speed, active, visible, background, bottom, register, children }) {
|
|
480
495
|
const ctrl = useRef(null);
|
|
496
|
+
// slides in once, when pushed; a page of another tab stays mounted and is registered only while its tab is selected
|
|
481
497
|
useEffect(() => {
|
|
482
498
|
const c = ctrl.current;
|
|
483
|
-
if (
|
|
484
|
-
return;
|
|
485
|
-
register(c);
|
|
486
|
-
if (speed > 0) {
|
|
499
|
+
if (c && speed > 0) {
|
|
487
500
|
c.TranslationX = CanvasWidthPts(c);
|
|
488
501
|
void c.TranslateToAsync(0, 0, speed);
|
|
489
502
|
}
|
|
490
|
-
return () => register(null);
|
|
491
503
|
}, [route]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
504
|
+
useEffect(() => {
|
|
505
|
+
const c = ctrl.current;
|
|
506
|
+
if (!c || !active)
|
|
507
|
+
return;
|
|
508
|
+
register(c);
|
|
509
|
+
return () => register(null);
|
|
510
|
+
}, [route, active]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
492
511
|
return (_jsx(SkiaLayer, { ref: (c) => { ctrl.current = c; }, VerticalOptions: "Fill", HorizontalOptions: "Fill", Margin: new Thickness(0, 0, 0, bottom), BackgroundColor: background, IsVisible: visible, BlockGesturesBelow: true, children: children }));
|
|
493
512
|
}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ import type { GesturesMode } from "../core/Gestures";
|
|
|
42
42
|
* and never appeared in completion.
|
|
43
43
|
*/
|
|
44
44
|
type PropsOf<T> = Partial<{
|
|
45
|
-
[K in keyof T as T[K] extends Function ? (undefined extends T[K] ? K : never) : K extends "Children" | "Views" | "Parent" | "Spans" | "GridStructure" | "AccessibilityId" | "IsAccessibilityElement" | "HasTransform" | "RenderTransformMatrix" | "RenderObjectPrevious" | "LastMeasuredIndex" | "ItemsInsertedAtStart" | "UsingControlStyle" | "Track" | "Thumb" | "FrameOn" | "FrameOff" | "ViewCheckOn" | "ViewOn" | "ViewText" | "Ratio" | "StartThumbX" | "EndThumbX" | "SnapPoints" | "CurrentPosition" | "CurrentSnap" | "ContentOffsetBounds" | "InTransition" | "CanAnimate" | "MaxIndex" | "ChildrenTotal" | "IsAtStart" | "IsAtEnd" | "ScrollProgress" | "ScrollAmount" | "TransitionProgress" | "LastIndex" | "ChildrenCount" | "Horizontal" | "Animator" | "Animation" | "IsPlaying" | "PlayWhenAvailable" | "TotalFrames" | "HasEffects" | "IsDisposed" | "Label" | "IsMultiline" | "HasSelection" | "LayoutVersion" | "LinesCount" | "MeasuredLineHeight" | "Focused" | "SpriteSheet" | "FrameWidth" | "FrameHeight" | "DurationMs" | "FrameDurationMs" | "CurrentSprite" | "Rects" | "HasTapHandler" | "HasDecorations" | "LinesCount" | "Superview" | "DrawingRect" | "MeasuredSize" | "RenderingScale" | "NeedMeasure" | "_superview" | "HitBoxAuto" | "TotalDown" | "TotalTapped" | "TouchDown" | "PostAnimators" | "LoadedSource" | "IsLoading" | "DisplayRect" | "AspectScale" | "Content" | "ContentSize" | "ContentOffsetBounds" | "OverscrollDistance" | "OverScrolled" | "IsUserPanning" | "IsUserFocused" | "IsScrolling" | "IsTemplated" | "FirstVisibleIndex" | "LastVisibleIndex" | "DebugString" | "ChildrenFactory" | "ContextIndex" | "RenderObject" | "UsingCacheType" | "TransitionEffect" | "TransitionFromIndex" | "TransitionToIndex" | "EffectPostRenderers" | "CachedImage" | "ImageBitmap" | "ShouldSubmitOnEnter" | "Header" | "Footer" | "RefreshIndicator" | "ScrollBar" | "ScrollBarHorizontal" | "IsRunning" | "VisibleRatio" | "CurrentIndex" | "LastCompositeRecord" | "DirtyChildrenInternal" | "IsRenderingWithComposition" ? never : K]: T[K];
|
|
45
|
+
[K in keyof T as T[K] extends Function ? (undefined extends T[K] ? K : never) : K extends "Children" | "Views" | "Parent" | "Spans" | "GridStructure" | "AccessibilityId" | "IsAccessibilityElement" | "HasTransform" | "RenderTransformMatrix" | "RenderObjectPrevious" | "LastMeasuredIndex" | "ItemsInsertedAtStart" | "UsingControlStyle" | "Track" | "Thumb" | "FrameOn" | "FrameOff" | "ViewCheckOn" | "ViewOn" | "ViewText" | "Ratio" | "StartThumbX" | "EndThumbX" | "SnapPoints" | "CurrentPosition" | "CurrentSnap" | "ContentOffsetBounds" | "InTransition" | "CanAnimate" | "MaxIndex" | "ChildrenTotal" | "IsAtStart" | "IsAtEnd" | "ScrollProgress" | "ScrollAmount" | "TransitionProgress" | "LastIndex" | "ChildrenCount" | "Horizontal" | "Animator" | "Animation" | "IsPlaying" | "PlayWhenAvailable" | "TotalFrames" | "HasEffects" | "IsDisposed" | "Label" | "IsMultiline" | "HasSelection" | "LayoutVersion" | "LinesCount" | "MeasuredLineHeight" | "Focused" | "SpriteSheet" | "FrameWidth" | "FrameHeight" | "DurationMs" | "FrameDurationMs" | "CurrentSprite" | "Rects" | "HasTapHandler" | "HasDecorations" | "LinesCount" | "Superview" | "DrawingRect" | "MeasuredSize" | "RenderingScale" | "NeedMeasure" | "_superview" | "HitBoxAuto" | "TotalDown" | "TotalTapped" | "TouchDown" | "PostAnimators" | "LoadedSource" | "IsLoading" | "DisplayRect" | "AspectScale" | "Content" | "ContentSize" | "ContentOffsetBounds" | "OverscrollDistance" | "OverScrolled" | "IsUserPanning" | "IsUserFocused" | "IsScrolling" | "IsTemplated" | "FirstVisibleIndex" | "LastVisibleIndex" | "DebugString" | "ChildrenFactory" | "ContextIndex" | "RenderObject" | "UsingCacheType" | "TransitionEffect" | "TransitionFromIndex" | "TransitionToIndex" | "EffectPostRenderers" | "CachedImage" | "ImageBitmap" | "ShouldSubmitOnEnter" | "Header" | "Footer" | "RefreshIndicator" | "ScrollBar" | "ScrollBarHorizontal" | "IsRunning" | "VisibleRatio" | "CurrentIndex" | "LastCompositeRecord" | "DirtyChildrenInternal" | "IsRenderingWithComposition" | "IsHovered" ? never : K]: T[K];
|
|
46
46
|
}>;
|
|
47
47
|
/** `ref` receives the engine control instance (react-reconciler getPublicInstance). */
|
|
48
48
|
type LeafProps<T> = PropsOf<T> & {
|