drawnui-react 0.1.0-preview.14 → 0.1.0-preview.16
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/SKIPPED.md +5 -4
- 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 +2 -0
- package/dist/controls/SkiaCarousel.js +2 -0
- package/dist/controls/SkiaDrawer.d.ts +2 -0
- package/dist/controls/SkiaDrawer.js +2 -0
- package/dist/controls/SkiaEditor.js +10 -1
- package/dist/controls/SkiaLabel.d.ts +2 -0
- package/dist/controls/SkiaLabel.js +13 -2
- package/dist/controls/SkiaLayout.d.ts +15 -0
- package/dist/controls/SkiaLayout.js +75 -6
- package/dist/controls/SkiaProgress.js +2 -2
- package/dist/controls/SkiaScroll.d.ts +4 -0
- package/dist/controls/SkiaScroll.js +9 -0
- package/dist/controls/SkiaSlider.d.ts +2 -0
- package/dist/controls/SkiaSlider.js +5 -3
- package/dist/controls/SkiaToggle.d.ts +2 -0
- package/dist/controls/SkiaToggle.js +3 -1
- package/dist/controls/SnappingLayout.d.ts +2 -0
- package/dist/controls/SnappingLayout.js +3 -0
- package/dist/core/Accessibility.d.ts +8 -0
- package/dist/core/Accessibility.js +56 -5
- package/dist/core/Canvas.d.ts +23 -0
- package/dist/core/Canvas.js +65 -3
- package/dist/core/Hover.d.ts +37 -0
- package/dist/core/Hover.js +118 -0
- package/dist/core/KeyboardManager.d.ts +13 -0
- package/dist/core/KeyboardManager.js +18 -0
- package/dist/core/SkiaControl.d.ts +87 -1
- package/dist/core/SkiaControl.js +229 -18
- package/dist/core/SkiaEffect.d.ts +3 -0
- package/dist/core/SkiaEffect.js +8 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +2 -2
- package/dist/react/reconciler.js +29 -10
- package/package.json +4 -1
package/SKIPPED.md
CHANGED
|
@@ -10,14 +10,15 @@ DrawnUi name and semantics; nothing here is a redesign, only an omission.
|
|
|
10
10
|
| `UseCache` | partial | `None`, `Operations` / `OperationsFull` (SkPicture replay), `Image` (offscreen surface snapshot, GPU-backed on WebGL, nearest-sampled blit), `ImageDoubleBuffered` (`RenderObjectPrevious` kept and drawn when a new cache cannot be produced yet, `DrawPlaceholder(ctx)` hook when there is none; recording itself is synchronous — no background thread, same as DrawnUi.Blazor with `CanUseCacheDoubleBuffering = false`), `ImageComposite` / `ImageCompositeGPU` (per-child composite, see the `UseCache="ImageComposite"` row below). `GPU` resolves to `Image` and `ImageCompositeGPU` to `ImageComposite`: no separate GPU-only path. `Super.CacheEnabled`, `RenderObject`/`CachedObject`, `InvalidateCache`, `DestroyRenderingObject`, `Canvas.DisposeObject` (deleted after flush). Defaults per C#: `SkiaLabel`/`SkiaSvg` Operations, everything else None. Not ported: `CacheSharing`, `AllowCaching`, offscreen bake threads, cache validity by GRContext. |
|
|
11
11
|
| Incremental invalidation | partial | `Measure` returns the previous size when `!NeedMeasure` and constraints+scale are unchanged; `Update()`/`InvalidateMeasure()` bubble up and stale every ancestor cache; `Repaint()` keeps caches. Arrange still runs per frame for the whole tree; no dirty regions, no `DirtyChildrenTracker`. Post-animators on a control nested INSIDE a cached ancestor are drawn only when that ancestor re-records. |
|
|
12
12
|
| Gestures core: `Canvas.Gestures` (Disabled/Enabled/Lock), `ProcessGestures(args, apply)`, `ConsumeGestures`, `Tapped`/`ChildTapped`, `InputTransparent`, `BlockGesturesBelow`, `LockChildrenGestures`, `HitBoxAuto`/`HitIsInside`/`IsGestureForChild`, `SkiaGesturesParameters`/`GestureEventProcessingInfo`/`SkiaGesturesInfo`/`ControlTappedEventArgs` | ported | Pointer events -> per-pointer `OnTouchAction` state machine (port of DrawnUi.Blazor) -> Down/Panning/Tapped/Up, queued and processed at frame start. Tap slop = 16pt like AppoMobi TouchEffect. The control that consumes a gesture keeps receiving it until it lets go (DrawnUi `Canvas.HadInput`, one pointer here instead of C#'s set): without it a drag handle, a slider thumb or a held button stops getting `Panning` the moment the finger leaves its box, and a control that stops consuming hands routing straight back to the tree (a button whose press turns into a scroll pan). Every mouse button is delivered (`Event.Pointer`: `Button`, `ButtonNumber`, `DeviceType`, `PressedButtons`, the AppoMobi.Gestures PointerData): a right click is Down / Up / Tapped with `Button` "Right" plus a `ContextMenu`; handlers that must be primary-only check `e.Parameters.Event.Pointer?.Button`. |
|
|
13
|
-
| Gestures: `LongPressing`, `Pointer` (hover), multi-touch pinch, `SoftLock`, `AddGestures` attached props, `OnGestures` delegate | skipped | The enum members exist for parity and are never produced; there is no pinch member at all. Ported instead of skipped: velocity (`Gestures.ts` fills `Distance.Velocity` per move, the scroll / snapping physics run on it) and transform-aware mapping — a child receives the point through the inverse `RenderTransformMatrix` (`TransformPointToLocalSpace`), so rotated / scaled / translated controls hit-test correctly. `ChildOffset` stays zero because that inverse matrix replaces the C# cache-offset math (`TranslateInputCoords`), not because transforms are missing. `Wheel` IS produced (`Canvas.onWheel`). |
|
|
13
|
+
| Gestures: `LongPressing`, `Pointer` (routed as a gesture; hover itself is ported, see `ReceivesHover`), multi-touch pinch, `SoftLock`, `AddGestures` attached props, `OnGestures` delegate | skipped | The enum members exist for parity and are never produced; there is no pinch member at all. Ported instead of skipped: velocity (`Gestures.ts` fills `Distance.Velocity` per move, the scroll / snapping physics run on it) and transform-aware mapping — a child receives the point through the inverse `RenderTransformMatrix` (`TransformPointToLocalSpace`), so rotated / scaled / translated controls hit-test correctly. `ChildOffset` stays zero because that inverse matrix replaces the C# cache-offset math (`TranslateInputCoords`), not because transforms are missing. `Wheel` IS produced (`Canvas.onWheel`). |
|
|
14
14
|
| C# multi-subscriber `event`s | changed shape | One callback per event prop (`Tapped={fn}`), same names. `Command*` (ICommand) variants not ported. |
|
|
15
15
|
| `Opacity`, `TranslationX/Y`, `Rotation`, `ScaleX/Y` (+`Scale`), `SkewX/Y`, `AnchorX/Y`, `HasTransform`, `RenderTransformMatrix`, `TransformPointToLocalSpace` | ported | Same matrix order as C# `ApplyTransforms` (pivot → rotation → scale/skew → translation), opacity = `saveLayer` alpha over the subtree, gestures mapped through the inverse matrix (`IsGestureForChild`, child receives the local point), accessibility rect = transformed bounds. Animations: `AnimateAsync`, `AnimateRangeAsync`, `FadeToAsync`, `ScaleToAsync`, `TranslateToAsync`, `RotateToAsync` as Promises with `AbortSignal` (one running per kind, like the C# per-property cancellation). Not ported: `RotationX/Y/Z`, `TranslationZ`, `Perspective1/2`, `CameraAngle`, `LinkTransforms`, `AddTranslationX/Y`, `UseTranslation*` overrides, `CustomizeLayerPaint`, `SkipRenderingOutOfBounds`. |
|
|
16
16
|
| `Left` / `Top` | ported (as a translate) | Same paint-time offset in points, but folded into the render matrix (a `canvas.translate`, hit-testing follows) for every control, cached or not; C# applies them only when blitting a cache. |
|
|
17
17
|
| `AnimatedFramesRenderer` / `SkiaLottie` / `SkiaGif` | ported | `AutoPlay`, `Repeat`, `SpeedRatio`, `DefaultFrame` (-1 = last), `Start(delay)`/`Stop`/`Seek`, `IsPlaying`, `Started`/`Finished`, `PlayWhenAvailable`, `GetFrameAt`. Lottie on CanvasKit Skottie (needs the `full` build, +0.9 MB raw wasm): `Source` (URL / app path, JSON cached per source, in-flight dedup), `ColorTint` / `Colors` (C# `ApplyTint` JSON rewrite), `IsOn` + `DefaultFrameWhenOn` + `ApplyIsOnWhenNotPlaying`, `StopAtCurrentFrame`, `ProcessJson`, `GoToStart`/`GoToEnd`, `SetAnimation`, `LoadSource`, `CreateAnimation`, `TotalFrames`; `UseCache=ImageDoubleBuffered` re-recorded per frame. GIF on CanvasKit AnimatedImage: every frame decoded once (`GifAnimation`: `SeekFrame`, `GetFrameNumber`, `DurationMs`, `TotalFrames`), `Aspect` (AspectFitFill), alignment, `Success`/`Error`. Not ported: `SkiaGif.Display` inner SkiaImage (frames are drawn directly), custom-control ctor `SkiaGif(display)`, `SkiaLottie` Skottie property/slot editing, sound, `ReloadSource` sync file paths, `Dispose` wiring on unmount (no engine hook from the reconciler yet). |
|
|
18
18
|
| `SkiaBackdrop` | ported | `Blur` (5), `Brightness` (gamma), `UseContext`, `BackgroundColor` tint, `HasEffects`, `GetImage`; children drawn first and blurred with the snapshot (C# order); `UseContext` snapshots the surface the pixels end up on (on-screen, or the nearest Image cache surface through its `Origin`), also while an Operations picture is being recorded, since caches record inline; after each paint the ancestors' caches are staled for the next frame (no frame requested) so content that changes beneath without invalidating this branch (an image loading later) is picked up by the next frame. Used by the shell popups/modals (`ShellDefaults.PopupsBackgroundBlur`). Not ported: `CacheSource`, the ghost mode. |
|
|
19
19
|
| Disposal (`Dispose` / `OnDisposing`) | ported | The React renderer calls `Dispose()` when an element leaves the tree (`detachDeletedInstance`): caches, gradient shaders, running `*ToAsync` animations, overlay effects, the accessibility node, then the children (static and templated pool); `SkiaScroll`/`SnappingLayout` stop their animators, `SkiaSvg` drops its raster, `SkiaLottie`/`SkiaGif` free the Skottie animation / frames; `Canvas.Dispose` disposes the content. Not ported: `IsDisposing` staged disposal, `Disposing` event, `OnWillDisposeWithChildren`. |
|
|
20
|
-
| `
|
|
20
|
+
| Hover: `ReceivesHover` (opt-in; on by default in SkiaButton, SkiaSlider, the toggles, SkiaRadioButton, SkiaCarousel, SkiaDrawer), `IsHovered`, `HoverChanged` (turns `ReceivesHover` on, as C# `OnHovered`) | ported | drawnui-cross 6m: every opted-in control under the mouse is hovered; nothing changes while a scroll, carousel or drawer moves its content, one check at the last pointer position when it stops and after an add / remove, a rebind, a hidden hovered control or an instant scroll jump; pointerleave clears at once; touch never hovers (`Canvas.Hover`, a `HoverManager`). Not ported: C# `SetHover`, `CheckHovered` / `OnHover` / `IsPointerOver` (no Pointer gestures are routed), SkiaSpinner / wheel picker (no such controls). |
|
|
21
|
+
| `KeyboardManager` | ported | Window-level keydown / keyup capture listeners like `drawnui-keyboard.js`: `KeyDown` / `KeyUp` with the DOM `event.code` ("KeyA", "ArrowLeft"; C# maps the same codes to `InputKey`), `KeyChar` for printable keys without Ctrl / Alt / Meta, `IsShiftPressed` / `IsControlPressed` / `IsAltPressed`, `AttachToKeyboard` (idempotent, pointerdown pulls window focus inside iframes), `BlurExternalTextInput`, `IsOwnedByElement` / `IsInputMethodKey` (React additions: a handler acting on keys skips keys a focused DOM element or the input method owns, C# 845b26e9); `Subscribe` / `Unsubscribe` instead of C# events. Not ported: `InputKey` enum (codes are strings). |
|
|
21
22
|
| `SkiaEditor` | partial | SkiaShape host with padding, content `SkiaLabel` (single-line: `HorizontalTextAlignment` Center / End get the field as the label's minimum width, so short text centers and long text still scrolls), placeholder label, caret (blinking 1 s, `CursorColor`, `CanShowCursor`) and selection overlay (`SelectionColor`) painted by the editor, single line (`MaxLines` 1, scrolls horizontally to the caret) / multiline (`MaxLines` != 1, `AutoHeight`) with own scroll offsets instead of the C# inner `SkiaScroll`; `Text` (line breaks normalized, spaces for single line), `PlaceholderText/Color/HorizontalAlignment`, `FontSize` (12) / `FontFamily` / `FontWeight` / `TextColor` / `TextGradient` / `LineHeight` / `Horizontal|VerticalTextAlignment`, `IsPassword` (bullets), `ReturnType` (Send submits multiline), `KeyboardType` / `IsSpellCheckEnabled` / `UseMarkdown` / `UseUnicode` accepted, `CursorPosition`, `SelectionLength`, `IsFocused` (one focused editor at a time), `ControlStyle` (C# `ApplyControlStyleVisuals` palettes over unset values), events `TextChanged` / `FocusChanged` / `CursorMoved` / `TextSubmitted`, `Submit` / `SelectAll` / `SetSelection` / `SelectWord` / `GetSelectedText` / `DeleteSelection` / `InsertAtCursor` / `CutSelection` / `CopySelection` / `PasteFromClipboard` (navigator.clipboard), the Blazor head stubs `StubTypeText` / `StubPressEnter` / `StubBackspace` / `StubDelete` (grapheme clusters via Intl.Segmenter) / `StubMoveCursor` / `StubSelectRange` / `StubSelectAll` / `HandleVerticalArrow`, Ctrl+A / C / X / V, Home / End, Escape blurs, tap places the caret, drag selects, long press selects a word, shift + tap extends. Keys come from `KeyboardManager` like the Blazor / Wasm heads; on top of that (not in DrawnUi.Blazor) a focused editor is mirrored by a hidden DOM textarea (`TextInputProxy`, `inputmode` from `KeyboardType`, `enterkeyhint` from `ReturnType`) so IME composition, the mobile soft keyboard, autocorrect and native paste / cut / undo reach the editor: every input event is diffed against the editor text and replayed through `StubSelectRange` + `StubTypeText` / `StubBackspace` / `StubPressEnter`; while the proxy holds DOM focus, Backspace / Delete / Enter / Ctrl+C / X / V key events are left to it. iOS may need a second tap for the keyboard (focus() runs on the next frame, outside the touch handler). Not ported: `SkiaCursor` control + `CursorGradient`, `SkiaEditorSelection` handles, `Superview.FocusedChild`, `CommandOn*`, `SkiaEditorDocument` (rich formatting), single-line Enter clearing focus (Blazor keeps it). |
|
|
22
23
|
| `SkiaSprite` / `SkiaSpriteSet` | ported | Sprite: `Source` (sheets cached per source, `CachedSpriteSheets`), `Columns` / `Rows` / `MaxFrames`, `FramesPerSecond` (24), `FrameSequence` / `AnimationName` + `CreateAnimationSequence` / `RegisteredAnimations`, `CurrentFrame`, `TotalFrames` / `DurationMs` / `FrameDurationMs` / `FrameWidth` / `FrameHeight`, `ApplyPlacementConfig` (units-per-pixel, size, anchor, offset = C# SpriteFrameImage ResolveFrameMetrics), transparent frame borders trimmed per frame (pixels read once), nearest sampling, AspectFit of the frame, `DisplayRect`, `Success` / `Error`, the AnimatedFramesRenderer playback. Set: `State`, `Define(state, source, columns, rows, fps, repeat, autoPlay, placement)`, `CurrentSprite`, `OnChangeState` hook, hit box from the drawn frame. Not ported: `Display` as a separate SkiaImage child (`SpriteFrameImage`) and the standalone `SkiaSprite(display)` ctor, `RescalingQuality` other than None, `InflateAmount`, `ClearCache` / `RemoveFromCache` (SkiaImageManager owns the images), `SkiaSpriteSet.InvalidateInternal`. Code-behind subclasses (FastRepro `WarriorSprite`) mount through a ref + `AddSubView` and are disposed by the mounting effect. |
|
|
23
24
|
| `IsClippedToBounds`, `ClipEffects` | ported | Clip to `DrawingRect` around content, children and post-animators; `ClipEffects=false` grows the clip by the aggregated effects margin so a child's shadow/glow can escape. Layouts aggregate their children's effects margins (C# `AggregatedEffectsMarginPixels`), cached per control and reset by `InvalidateMeasure`. Not ported: `Clipping` callback, `ClipWith`, `ClipFrom`, platform clips; C# currently ignores `ClipEffects` in its render path (always expands). |
|
|
@@ -61,7 +62,7 @@ DrawnUi name and semantics; nothing here is a redesign, only an omission.
|
|
|
61
62
|
|
|
62
63
|
| Control | Status | Notes |
|
|
63
64
|
|---|---|---|
|
|
64
|
-
| `SkiaLayout` Absolute / Column / Row (+ `SkiaStack`, `SkiaRow`, `SkiaLayer`) | ported | `Spacing`, `Padding`, `Children`/`Views`, `AddSubView`/`RemoveSubView`/`InsertSubView`. |
|
|
65
|
+
| `SkiaLayout` Absolute / Column / Row (+ `SkiaStack`, `SkiaRow`, `SkiaLayer`) | ported | `Spacing`, `Padding`, `Children`/`Views`, `AddSubView`/`RemoveSubView`/`InsertSubView`, `ClearChildren`, `IsChildrenItem`. Children changes follow C# `ChildrenCollectionSync` (insert / move / reset in collection order inside the slots app children occupy); React has no `ObservableCollection`: JSX children diffs and a new `Children` list are the collection actions. |
|
|
65
66
|
| `SkiaLayout` templated (Column only): `ItemsSource` (array), `ItemTemplate` (factory `() => SkiaControl`), `RecyclingTemplate` Enabled/Disabled, `MeasureItemsStrategy` `MeasureAll` (default, per-item size) / `MeasureFirst` (explicit uniform, O(1) for 100k items), `ChildrenFactory` (ViewsAdapter pool: `GetOrCreateViewForIndex`, `ReleaseViewAt`), `FirstVisibleIndex`/`LastVisibleIndex`, `DebugString`, `ApplyItemsSource`, `VirtualisationInflated`, `BindingContext`/`ContextIndex` on every control, `SkiaDynamicDrawnCell.SetContent` | ported | Cells realized/bound/arranged/drawn only for the visible viewport (+inflation) each frame. |
|
|
66
67
|
| `LoadMoreCommand` / `LoadMoreTopCommand` / `LoadMoreOffset` / `LoadMoreTopOffset` | ported | Callbacks instead of ICommand. Bottom fires once per content extent when within the offset of the end (or when the content underfills the viewport), re-arms when the content grows or the user moves away (>offset+100 pt, >2 s) — C# CheckNeedToLoadMore. Top arms only after the offset left the top edge, fires when it comes back. Not ported: opposite-direction cooldown (`IsOppositeLoadMoreBlocked`), `IInsideViewport.ShouldTriggerLoadMore`. |
|
|
67
68
|
| Structure-preserving `ItemsSource` updates | ported (array diff) | C# listens to `ObservableCollection` Add/Insert; React apps replace the array, so the setter detects **append** (old is a prefix: measured heights kept, tail measured lazily) and **prepend** (old is a suffix: view indices shifted without rebinding, new head measured synchronously up to 200 items, `ItemsInsertedAtStart(px)` lets the `SkiaScroll` move its offset so visible rows stay put — C# head-insert rebase). and **reorder** (same items permuted, the C# `ObservableCollection.Move` case: contexts rebound, the index-keyed measured heights permuted with their items, nothing re-measured, extent and scroll offset unchanged). Any other change rebuilds the structure (heights re-measured, offset kept). Not ported: removals in place, `Replace`. |
|
|
@@ -81,7 +82,7 @@ DrawnUi name and semantics; nothing here is a redesign, only an omission.
|
|
|
81
82
|
| `SkiaShape` (+ `SkiaFrame`) | partial | `BevelType` Bevel / Emboss + `Bevel` (`Depth`, `LightColor`, `ShadowColor`, `Opacity`; C# `PaintBevelEffect` edge paths for Rectangle, rounded Rectangle, Circle/Ellipse, Polygon, Path via ContourMeasure halves). `Shadows` (`SkiaShadow` X/Y/Blur/Opacity/Color/ShadowOnly, plain literals accepted): fill painted once per shadow with a DropShadow image filter (C# `PaintWithShadowsInternal`), hollow shapes clip their outline away so only the outside shadow remains; caches record the effects margin (`3·Blur·scale ± offset`, C# `MergeShadowMargin`) via `ComputeEffectsMargin`/`ExpandedCacheRect`. Not ported: `PlatformShadow` (MAUI `Shadow` brush), `ExpandDirtyRegion`, bevel/emboss. `Type` Rectangle/Circle/Ellipse/Arc (`Value1` start, `Value2` sweep)/Polygon+Line (`Points` ratios)/Path (`PathData`, fitted + centered), `CornerRadius` (uniform or per-corner), `StrokeWidth` (points, negative = px) drawn inside the bounds, `StrokeColor`, `StrokeCap`, `ClipBackgroundColor`, children laid out inside the stroke and clipped to the shape, `CreateClip` = shape, `FillGradient` fill, `UseCache=Operations` default. Not ported: `Squricle`, `Custom`, `SmoothPoints`, `StrokePath` (dashes), `StrokeGradient`, `StrokeBlendMode`, sub-1px stroke compensation, `MeasuredStrokeAware*` exposure, `LayoutChildren` override hook. |
|
|
82
83
|
| `SkiaButton` | partial | Now a `SkiaLayout` hosting a `SkiaShape` frame (`Tag="BtnShape"`) + `SkiaLabel` (`Tag="BtnText"`), sized from the label + Padding; `CornerRadius` (default 8), `StrokeColor`/`StrokeWidth`, `BackgroundColor`, text props, `ApplyEffect`, `Tapped`/`Down`/`Up`. Still no `ButtonStyle` looks, icons, `TextCase`, elevation, shimmer, custom templating through tags. |
|
|
83
84
|
| `SnappingLayout` | ported | `Animated`, `RespondsToGestures`, `IgnoreWrongDirection`, `Bounces`, `RubberDamping`, `RubberEffect`, `AutoVelocityMultiplyPts`, `SnapDistanceRatio`, `SnapPoints`, `CurrentPosition`, `CurrentSnap`, `ContentOffsetBounds`, `IsUserPanning`/`IsUserFocused`, `InTransition`, `Scrolled`/`TransitionChanged`/`Stopped`, `ClampOffset` (rubber), `FindNearestAnchor`, `SelectNextAnchor` (dot product), `ScrollToNearestAnchor`, `ScrollToOffset` (spring `Spring(1+RubberDamping, 200, 0.5·(1+RubberDamping))` when `Bounces`, else 0.1–0.8 s from velocity). The settled state is reported when the snap animation stops (C# relies on `ApplyPosition` ticks). Not ported: `AnimatorSpring` reuse, `SnapDistanceRatio` for non-carousel layouts, hover. |
|
|
84
|
-
| `UseCache="ImageComposite"` / `ImageCompositeGPU`, `Left` / `Top` | ported | ImageComposite keeps its offscreen surface across records; a child that calls `RepaintComposition` (transform / own repaint) is tracked as dirty by its composite ancestors (`TrackChildAsDirty`, `DirtyChildrenInternal`), the next record erases the dirty children's old + new transformed bounds plus the siblings they overlap and paints only those (`IsRenderingWithComposition`, layouts skip the rest); own content, a remeasure, a size / scale change record fully (`LastCompositeRecord` diagnostics). `Left` / `Top` on a cached control blit the cache at the offset without a matrix or save/restore (the translation is still set as `RenderTransformMatrix` so gestures / accessibility map through it); uncached controls keep the matrix path. Not ported: `ExpandDirtyRegion`, C# `CachedObject` surface reuse for plain `Image`. |
|
|
85
|
+
| `UseCache="ImageComposite"` / `ImageCompositeGPU`, `Left` / `Top` | ported | ImageComposite keeps its offscreen surface across records; a change deeper than a direct child (a color through props or a color setter = `UpdateDraw`, or `RepaintComposition`) is erased and redrawn by its area (drawnui-cross 6m: the control as drawn before and now, through every ancestor's transform; an effect or a backdrop on the way draws the child whole; a rect change, more than `MaxCompositionAreas` 16 areas or more than `MaxCompositionShare` 0.5 of the composite records whole; `LastCompositeRecord.Areas` / `Changed`; children outside the areas are not traversed); a child that calls `RepaintComposition` (transform / own repaint) is tracked as dirty by its composite ancestors (`TrackChildAsDirty`, `DirtyChildrenInternal`), the next record erases the dirty children's old + new transformed bounds plus the siblings they overlap and paints only those (`IsRenderingWithComposition`, layouts skip the rest); own content, a remeasure, a size / scale change record fully (`LastCompositeRecord` diagnostics). `Left` / `Top` on a cached control blit the cache at the offset without a matrix or save/restore (the translation is still set as `RenderTransformMatrix` so gestures / accessibility map through it); uncached controls keep the matrix path. Not ported: `ExpandDirtyRegion`, C# `CachedObject` surface reuse for plain `Image`. |
|
|
85
86
|
| `SkiaCarousel` | ported | Static `Children` or recycled `ItemsSource` + `ItemTemplate` cells (only visible + neighbour slides realized, `ReleaseExcept` for the looped non-contiguous set); `IsVertical`, `SidesOffset`, `Spacing`, `IsLooped` (virtual anchors -1/-2, wrap by `GoNext`/`GoPrev`/swipe at the borders, unbounded offset along the axis, edge slides drawn on the far side, `FixIndex` teleport after the wrap, `InterruptSnapping` strip shift), `PreloadNeighboors` (default true), `DynamicSize` (auto-sized axis follows the selected slide, re-measured on index change), `SwipeSpeed` (velocity ×SwipeSpeed/2, spring stiffness 200·SwipeSpeed, linear cap 0.25/speedK s, `SinOut`), `LinearSpeedMs` (one slide = LinearSpeedMs), `SelectedIndex` + `SelectedIndexChanged`, `LastIndex`, `MaxIndex`, `ChildrenTotal`/`ChildrenCount`, `IsAtStart`/`IsAtEnd`, `ScrollProgress`, `ScrollAmount`, `TransitionProgress`, `ItemAppearing`/`ItemDisappearing`, `Stopped`, `GoNext`/`GoPrev`/`ScrollTo(index, animate)`, C# gesture rules (velocity dead zone 100, clamp 500, snap-if-no-pan on Up, only the slides drawn on screen receive gestures). `IsRightToLeft` accepted (C# TODO). Not ported: `TransitionDirection`, `SafeIndex`, `ScrolledCommand`/`SelectedIndexChangedCommand`, `SelectedItem`, `CheckConstraints` exception, `OnTemplatesAvailable` background init, hover, pinch. |
|
|
86
87
|
| Templated `Row` / `Wrap` / `Grid`, `Split`, `SkiaDecoratedGrid` | ported | `ItemsSource` + `ItemTemplate` on any layout type: Column stays the virtualized list. Row / Wrap / Grid / a split Column follow `RecyclingTemplate` (drawnui-cross 6b): Enabled measures every item on pooled views (C# measures the structure on one template instance) and binds views only to the slots drawn, their views go back to the pool when the slot leaves the viewport (`VirtualisationInflated` widens it), every slot draws under a cache (C# `GetOnScreenVisibleArea`); Disabled keeps one view per item, never rebound. Changing `RecyclingTemplate` rebuilds the cells. `Split` (column count for Wrap and for a Column, which then lays out like a wrap with fixed slots; a templated Grid places item i at (i % Split, i / Split), `Invert` column-major), `SplitAlign` (true: fixed slot width), `DynamicColumns` (a short last row spreads over the width). `SkiaDecoratedGrid`: `HorizontalLine` / `VerticalLine` gradients painted in `RowSpacing` / `ColumnSpacing` under the children (C# defaults). Not ported: `SplitSpace` (C# TODO), `SplitMax`, `MeasureItemsStrategy` for non-Column templated layouts (every item is measured once per structure). |
|
|
87
88
|
| `AccessibilityTextSelectable` (React extension) | ported | Opt-in per control, off by default: a `SkiaLabel` with it renders its laid-out lines as real, transparent DOM text in the accessibility overlay (one span per line at the drawn position, the registered font aliases are also installed as CSS `FontFace`s so glyphs line up), `pointer-events` on, so the browser selects, copies and reads it like HTML; the wheel over such text is re-dispatched to the canvas. Pointer input over the text never reaches the drawn control, hence opt-in. Not in DrawnUi.Net. |
|
|
@@ -8,6 +8,8 @@ import { SkiaLayout } from "./SkiaLayout";
|
|
|
8
8
|
* on every frame it is drawn in; inside a cached parent it is re-recorded with that parent).
|
|
9
9
|
*/
|
|
10
10
|
export declare class SkiaBackdrop extends SkiaLayout {
|
|
11
|
+
/** Samples what is drawn below: a composite above redraws it whole when something inside changes (drawnui-cross 6m). */
|
|
12
|
+
protected get SamplesContent(): boolean;
|
|
11
13
|
Blur: number;
|
|
12
14
|
Brightness: number;
|
|
13
15
|
/** Snapshot the surface currently drawn into (true) or the on-screen surface (false). */
|
|
@@ -8,6 +8,8 @@ import { SkiaLayout } from "./SkiaLayout";
|
|
|
8
8
|
* on every frame it is drawn in; inside a cached parent it is re-recorded with that parent).
|
|
9
9
|
*/
|
|
10
10
|
export class SkiaBackdrop extends SkiaLayout {
|
|
11
|
+
/** Samples what is drawn below: a composite above redraws it whole when something inside changes (drawnui-cross 6m). */
|
|
12
|
+
get SamplesContent() { return true; }
|
|
11
13
|
Blur = 5;
|
|
12
14
|
Brightness = 1;
|
|
13
15
|
/** Snapshot the surface currently drawn into (true) or the on-screen surface (false). */
|
|
@@ -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. */
|
|
@@ -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. */
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -73,7 +73,12 @@ export class SkiaEditor extends SkiaShape {
|
|
|
73
73
|
stubSelectionStop = -1;
|
|
74
74
|
selectionMovingEdge = -1;
|
|
75
75
|
onKeyDown = (key, e) => this.OnKeyDown(key, e);
|
|
76
|
-
onKeyChar = (ch, e) => {
|
|
76
|
+
onKeyChar = (ch, e) => {
|
|
77
|
+
if (KeyboardManager.IsOwnedByElement(e) || KeyboardManager.IsInputMethodKey(e))
|
|
78
|
+
return; // a page control or the IME owns it
|
|
79
|
+
e.preventDefault();
|
|
80
|
+
this.StubTypeText(ch);
|
|
81
|
+
};
|
|
77
82
|
selectionAnchor = -1;
|
|
78
83
|
dragSelecting = false;
|
|
79
84
|
styleApplied = {};
|
|
@@ -596,6 +601,10 @@ export class SkiaEditor extends SkiaShape {
|
|
|
596
601
|
OnKeyDown(key, e) {
|
|
597
602
|
const shift = KeyboardManager.IsShiftPressed, ctrl = KeyboardManager.IsControlPressed, alt = KeyboardManager.IsAltPressed;
|
|
598
603
|
const handled = () => e.preventDefault();
|
|
604
|
+
// keys of another element (a page field or button, an overlay node) and of the input method are not the editor's:
|
|
605
|
+
// a composition keeps its arrows, Escape and Backspace (C# 845b26e9, GitHub #231)
|
|
606
|
+
if (KeyboardManager.IsInputMethodKey(e) || (KeyboardManager.IsOwnedByElement(e) && !TextInputProxy.IsProxyTarget(e.target)))
|
|
607
|
+
return;
|
|
599
608
|
// with the hidden textarea focused these keys become input / clipboard events there and are applied from those
|
|
600
609
|
if (TextInputProxy.IsActive(this) && TextInputProxy.IsProxyTarget(e.target) && (key === "Backspace" || key === "Delete" || key === "Enter" || key === "NumpadEnter" || (ctrl && (key === "KeyC" || key === "KeyX" || key === "KeyV"))))
|
|
601
610
|
return;
|
|
@@ -53,6 +53,8 @@ export declare class SkiaLabel extends SkiaControl {
|
|
|
53
53
|
protected DefaultAccessibilityLabel(): string | undefined;
|
|
54
54
|
constructor();
|
|
55
55
|
private Set;
|
|
56
|
+
/** A property that changes only the look (a color): no layout change, see UpdateDraw. */
|
|
57
|
+
private SetLook;
|
|
56
58
|
get Text(): string;
|
|
57
59
|
set Text(v: string);
|
|
58
60
|
get FontSize(): number;
|
|
@@ -43,12 +43,17 @@ export class SkiaLabel extends SkiaControl {
|
|
|
43
43
|
this[key] = v;
|
|
44
44
|
this.Update();
|
|
45
45
|
} }
|
|
46
|
+
/** A property that changes only the look (a color): no layout change, see UpdateDraw. */
|
|
47
|
+
SetLook(key, v) { if (this[key] !== v) {
|
|
48
|
+
this[key] = v;
|
|
49
|
+
this.UpdateDraw();
|
|
50
|
+
} }
|
|
46
51
|
get Text() { return this.text; }
|
|
47
52
|
set Text(v) { this.Set("text", v); }
|
|
48
53
|
get FontSize() { return this.fontSize; }
|
|
49
54
|
set FontSize(v) { this.Set("fontSize", v); }
|
|
50
55
|
get TextColor() { return this.textColor; }
|
|
51
|
-
set TextColor(v) { this.
|
|
56
|
+
set TextColor(v) { this.SetLook("textColor", v); }
|
|
52
57
|
get FontFamily() { return this.fontFamily; }
|
|
53
58
|
set FontFamily(v) { this.Set("fontFamily", v); }
|
|
54
59
|
/**
|
|
@@ -85,8 +90,14 @@ export class SkiaLabel extends SkiaControl {
|
|
|
85
90
|
InsertSubView(index, control) {
|
|
86
91
|
if (!(control instanceof TextSpan))
|
|
87
92
|
throw new Error("DrawnUi: SkiaLabel children must be <TextSpan>");
|
|
93
|
+
const at = control.Parent === this ? this.Spans.indexOf(control) : -1; // already a span: a move, never a 2nd copy
|
|
94
|
+
if (at >= 0) {
|
|
95
|
+
this.Spans.splice(at, 1);
|
|
96
|
+
if (at < index)
|
|
97
|
+
index--;
|
|
98
|
+
}
|
|
88
99
|
control.Parent = this;
|
|
89
|
-
this.Spans.splice(index, 0, control);
|
|
100
|
+
this.Spans.splice(Math.min(Math.max(0, index), this.Spans.length), 0, control);
|
|
90
101
|
this.Update();
|
|
91
102
|
}
|
|
92
103
|
RemoveSubView(control) {
|
|
@@ -112,7 +112,22 @@ export declare class SkiaLayout extends SkiaControl {
|
|
|
112
112
|
get Views(): readonly SkiaControl[];
|
|
113
113
|
/** Settable children list like DrawnUi Children (ignored while templated). */
|
|
114
114
|
get Children(): readonly SkiaControl[];
|
|
115
|
+
/**
|
|
116
|
+
* C# Children reset (ChildrenCollectionSync, 66a1eb02): the app's children the new list no longer holds are removed,
|
|
117
|
+
* the ones it gained are added, then all of them take the list's order inside the slots app children occupy, so the
|
|
118
|
+
* parts a control made itself keep their place.
|
|
119
|
+
*/
|
|
115
120
|
set Children(value: readonly SkiaControl[]);
|
|
121
|
+
/**
|
|
122
|
+
* C# Insert / Move of a Children collection (the reconciler's appendChild / insertBefore): `child` goes before
|
|
123
|
+
* `before` among the app's children, or last without it. A new child is attached at the end of the views and an
|
|
124
|
+
* append stops there; otherwise the app's children take their new order inside the slots they occupy.
|
|
125
|
+
*/
|
|
126
|
+
InsertChild(child: SkiaControl, before?: SkiaControl): void;
|
|
127
|
+
/** C# OrderViews: the given children, in their order, inside the slots they occupy in the views; nothing else moves. */
|
|
128
|
+
private OrderChildren;
|
|
129
|
+
/** C# ClearChildren: removes every subview, the control's own parts included. */
|
|
130
|
+
ClearChildren(): void;
|
|
116
131
|
private orderedViews?;
|
|
117
132
|
/** Static children sorted by ZIndex (stable), computed once per change (C# GetOrderedSubviews). */
|
|
118
133
|
protected GetOrderedSubviews(): readonly SkiaControl[];
|
|
@@ -359,11 +359,67 @@ export class SkiaLayout extends SkiaControl {
|
|
|
359
359
|
get Views() { return this.IsTemplated ? this.ChildrenFactory.GetViewsInUse() : this.views; }
|
|
360
360
|
/** Settable children list like DrawnUi Children (ignored while templated). */
|
|
361
361
|
get Children() { return this.views; }
|
|
362
|
+
/**
|
|
363
|
+
* C# Children reset (ChildrenCollectionSync, 66a1eb02): the app's children the new list no longer holds are removed,
|
|
364
|
+
* the ones it gained are added, then all of them take the list's order inside the slots app children occupy, so the
|
|
365
|
+
* parts a control made itself keep their place.
|
|
366
|
+
*/
|
|
362
367
|
set Children(value) {
|
|
368
|
+
const keep = new Set(value);
|
|
369
|
+
for (const v of [...this.views])
|
|
370
|
+
if (v.IsChildrenItem && !keep.has(v))
|
|
371
|
+
this.RemoveSubView(v);
|
|
372
|
+
const inViews = new Set(this.views);
|
|
373
|
+
for (const v of value) {
|
|
374
|
+
v.IsChildrenItem = true;
|
|
375
|
+
if (!inViews.has(v))
|
|
376
|
+
this.AddSubView(v);
|
|
377
|
+
}
|
|
378
|
+
this.OrderChildren(value);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* C# Insert / Move of a Children collection (the reconciler's appendChild / insertBefore): `child` goes before
|
|
382
|
+
* `before` among the app's children, or last without it. A new child is attached at the end of the views and an
|
|
383
|
+
* append stops there; otherwise the app's children take their new order inside the slots they occupy.
|
|
384
|
+
*/
|
|
385
|
+
InsertChild(child, before) {
|
|
386
|
+
child.IsChildrenItem = true;
|
|
387
|
+
const attached = child.Parent === this && this.views.includes(child);
|
|
388
|
+
if (!attached)
|
|
389
|
+
this.AddSubView(child);
|
|
390
|
+
if (!attached && !before)
|
|
391
|
+
return;
|
|
392
|
+
const order = this.views.filter((v) => v.IsChildrenItem && v !== child);
|
|
393
|
+
const at = before ? order.indexOf(before) : -1;
|
|
394
|
+
order.splice(at < 0 ? order.length : at, 0, child);
|
|
395
|
+
this.OrderChildren(order);
|
|
396
|
+
}
|
|
397
|
+
/** C# OrderViews: the given children, in their order, inside the slots they occupy in the views; nothing else moves. */
|
|
398
|
+
OrderChildren(ordered) {
|
|
399
|
+
const inViews = new Set(this.views);
|
|
400
|
+
const present = ordered.filter((v) => inViews.delete(v));
|
|
401
|
+
if (present.length < 2)
|
|
402
|
+
return;
|
|
403
|
+
const isChild = new Set(present);
|
|
404
|
+
let next = 0, changed = false;
|
|
405
|
+
for (let i = 0; i < this.views.length && next < present.length; i++) {
|
|
406
|
+
if (!isChild.has(this.views[i]))
|
|
407
|
+
continue;
|
|
408
|
+
if (this.views[i] !== present[next]) {
|
|
409
|
+
this.views[i] = present[next];
|
|
410
|
+
changed = true;
|
|
411
|
+
}
|
|
412
|
+
next++;
|
|
413
|
+
}
|
|
414
|
+
if (changed) {
|
|
415
|
+
this.orderedViews = undefined;
|
|
416
|
+
this.InvalidateMeasure();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
/** C# ClearChildren: removes every subview, the control's own parts included. */
|
|
420
|
+
ClearChildren() {
|
|
363
421
|
for (const v of [...this.views])
|
|
364
422
|
this.RemoveSubView(v);
|
|
365
|
-
for (const v of value)
|
|
366
|
-
this.AddSubView(v);
|
|
367
423
|
}
|
|
368
424
|
orderedViews;
|
|
369
425
|
/** Static children sorted by ZIndex (stable), computed once per change (C# GetOrderedSubviews). */
|
|
@@ -390,10 +446,18 @@ export class SkiaLayout extends SkiaControl {
|
|
|
390
446
|
}
|
|
391
447
|
AddSubView(control) { this.InsertSubView(this.views.length, control); }
|
|
392
448
|
InsertSubView(index, control) {
|
|
449
|
+
// already a child: a move (React reorders keyed children by inserting an existing one elsewhere), never a 2nd copy
|
|
450
|
+
const at = control.Parent === this ? this.views.indexOf(control) : -1;
|
|
451
|
+
if (at >= 0) {
|
|
452
|
+
this.views.splice(at, 1);
|
|
453
|
+
if (at < index)
|
|
454
|
+
index--;
|
|
455
|
+
}
|
|
393
456
|
control.Parent = this;
|
|
394
|
-
this.views.splice(index, 0, control);
|
|
457
|
+
this.views.splice(Math.min(Math.max(0, index), this.views.length), 0, control);
|
|
395
458
|
this.orderedViews = undefined;
|
|
396
459
|
this.InvalidateMeasure();
|
|
460
|
+
this.Superview?.Hover?.RequestCheck(); // a popup / page under a still mouse
|
|
397
461
|
}
|
|
398
462
|
RemoveSubView(control) {
|
|
399
463
|
const i = this.views.indexOf(control);
|
|
@@ -403,6 +467,7 @@ export class SkiaLayout extends SkiaControl {
|
|
|
403
467
|
this.orderedViews = undefined;
|
|
404
468
|
control.Parent = undefined;
|
|
405
469
|
this.InvalidateMeasure();
|
|
470
|
+
this.Superview?.Hover?.RequestCheck();
|
|
406
471
|
}
|
|
407
472
|
// ---- measure ----
|
|
408
473
|
MeasureAbsolute(widthConstraint, heightConstraint, scale) {
|
|
@@ -758,9 +823,13 @@ export class SkiaLayout extends SkiaControl {
|
|
|
758
823
|
return;
|
|
759
824
|
}
|
|
760
825
|
const composing = this.IsRenderingWithComposition;
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
826
|
+
// inside a composite that redraws only some areas, a child entirely outside them is not even traversed
|
|
827
|
+
const culling = !composing && SkiaControl.CompositionCulling > 0;
|
|
828
|
+
for (const v of this.GetOrderedSubviews()) {
|
|
829
|
+
if (composing ? !this.DirtyChildrenInternal.has(v) : culling && v.IsVisible && v.OutsideCompositionClip(ctx.Context.Canvas))
|
|
830
|
+
continue;
|
|
831
|
+
v.Render(ctx);
|
|
832
|
+
}
|
|
764
833
|
}
|
|
765
834
|
GetCompositeChildren() { return this.IsTemplatedList || this.IsRecycledLayout ? [] : this.GetOrderedSubviews(); }
|
|
766
835
|
drawnSlots = new Set();
|
|
@@ -33,9 +33,9 @@ export class SkiaProgress extends SkiaControl {
|
|
|
33
33
|
set Max(v) { this.max = v; this.Update(); }
|
|
34
34
|
/** Unset = per-style palette. */
|
|
35
35
|
get TrackColor() { return this.trackColor ?? SkiaProgress.Palette(this.UsingControlStyle).track; }
|
|
36
|
-
set TrackColor(v) { this.trackColor = v; this.
|
|
36
|
+
set TrackColor(v) { this.trackColor = v; this.UpdateDraw(); }
|
|
37
37
|
get ProgressColor() { return this.progressColor ?? SkiaProgress.Palette(this.UsingControlStyle).progress; }
|
|
38
|
-
set ProgressColor(v) { this.progressColor = v; this.
|
|
38
|
+
set ProgressColor(v) { this.progressColor = v; this.UpdateDraw(); }
|
|
39
39
|
/** C# ResolvedTrackColor / ResolvedProgressColor / track heights per style. */
|
|
40
40
|
static Palette(s) {
|
|
41
41
|
switch (s) {
|
|
@@ -156,6 +156,10 @@ export declare class SkiaScroll extends SkiaControl {
|
|
|
156
156
|
set ViewportOffsetX(value: number);
|
|
157
157
|
get ViewportOffsetY(): number;
|
|
158
158
|
set ViewportOffsetY(value: number);
|
|
159
|
+
/** An offset set without a glide or a pan (a jump, a touchpad step): the controls under a still mouse changed. */
|
|
160
|
+
private OffsetJumped;
|
|
161
|
+
/** Hover waits while the content glides, flings, bounces or follows a pan (C# IsScrolling). */
|
|
162
|
+
MovesContent(): boolean;
|
|
159
163
|
private readonly animatorFlingX;
|
|
160
164
|
private readonly animatorFlingY;
|
|
161
165
|
private readonly bounceX;
|
|
@@ -399,12 +399,19 @@ export class SkiaScroll extends SkiaControl {
|
|
|
399
399
|
set ViewportOffsetX(value) { if (this.offsetX !== value) {
|
|
400
400
|
this.offsetX = value;
|
|
401
401
|
this.OnScrolled();
|
|
402
|
+
this.OffsetJumped();
|
|
402
403
|
} }
|
|
403
404
|
get ViewportOffsetY() { return this.offsetY; }
|
|
404
405
|
set ViewportOffsetY(value) { if (this.offsetY !== value) {
|
|
405
406
|
this.offsetY = value;
|
|
406
407
|
this.OnScrolled();
|
|
408
|
+
this.OffsetJumped();
|
|
407
409
|
} }
|
|
410
|
+
/** An offset set without a glide or a pan (a jump, a touchpad step): the controls under a still mouse changed. */
|
|
411
|
+
OffsetJumped() { if (!this.MovesContent())
|
|
412
|
+
this.Superview?.Hover?.RequestCheck(); }
|
|
413
|
+
/** Hover waits while the content glides, flings, bounces or follows a pan (C# IsScrolling). */
|
|
414
|
+
MovesContent() { return this.IsScrolling || this.IsUserPanning || !!this.draggedScrollBar; }
|
|
408
415
|
animatorFlingX = new ScrollFlingAnimator(this);
|
|
409
416
|
animatorFlingY = new ScrollFlingAnimator(this);
|
|
410
417
|
bounceX = new SpringWithVelocityAnimator(this);
|
|
@@ -506,6 +513,7 @@ export class SkiaScroll extends SkiaControl {
|
|
|
506
513
|
}
|
|
507
514
|
InsertSubView(_index, control) { this.AddSubView(control); }
|
|
508
515
|
RemoveSubView(control) {
|
|
516
|
+
this.Superview?.Hover?.RequestCheck();
|
|
509
517
|
if (this.content === control)
|
|
510
518
|
this.Content = undefined;
|
|
511
519
|
else if (this.header === control)
|
|
@@ -626,6 +634,7 @@ export class SkiaScroll extends SkiaControl {
|
|
|
626
634
|
}
|
|
627
635
|
}
|
|
628
636
|
Paint(ctx) {
|
|
637
|
+
this.Superview?.Hover?.RegisterMover(this);
|
|
629
638
|
const c = this.content;
|
|
630
639
|
if (!c)
|
|
631
640
|
return;
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -60,11 +62,11 @@ export class SkiaSlider extends SkiaControl {
|
|
|
60
62
|
get SliderHeight() { return this.sliderHeight ?? this.Look().thumb + (this.EnableRange && this.UsingControlStyle !== "Unset" ? 8 : 0); }
|
|
61
63
|
set SliderHeight(v) { this.sliderHeight = v; this.Update(); }
|
|
62
64
|
get ThumbColor() { return this.thumbColor ?? this.Look().thumbColor; }
|
|
63
|
-
set ThumbColor(v) { this.thumbColor = v; this.
|
|
65
|
+
set ThumbColor(v) { this.thumbColor = v; this.UpdateDraw(); }
|
|
64
66
|
get TrackColor() { return this.trackColor ?? this.Look().track; }
|
|
65
|
-
set TrackColor(v) { this.trackColor = v; this.
|
|
67
|
+
set TrackColor(v) { this.trackColor = v; this.UpdateDraw(); }
|
|
66
68
|
get TrackSelectedColor() { return this.trackSelectedColor ?? this.Look().selected; }
|
|
67
|
-
set TrackSelectedColor(v) { this.trackSelectedColor = v; this.
|
|
69
|
+
set TrackSelectedColor(v) { this.trackSelectedColor = v; this.UpdateDraw(); }
|
|
68
70
|
/** C# style builders: thumb diameter, track height, palette, thumb shadow. */
|
|
69
71
|
Look() {
|
|
70
72
|
switch (this.UsingControlStyle) {
|
|
@@ -10,6 +10,8 @@ import { SkiaLayout } from "./SkiaLayout";
|
|
|
10
10
|
* take the style defaults (C# SetStyleDefault).
|
|
11
11
|
*/
|
|
12
12
|
export declare abstract class SkiaToggle extends SkiaLayout {
|
|
13
|
+
/** Takes mouse hover unless ReceivesHover is set false (C# ReceivesHoverByDefault): switch, checkbox, radio button. */
|
|
14
|
+
protected ReceivesHoverByDefault(): boolean;
|
|
13
15
|
private controlStyle;
|
|
14
16
|
get ControlStyle(): PrebuiltControlStyle;
|
|
15
17
|
set ControlStyle(v: PrebuiltControlStyle);
|
|
@@ -8,6 +8,8 @@ import { SkiaLayout } from "./SkiaLayout";
|
|
|
8
8
|
* take the style defaults (C# SetStyleDefault).
|
|
9
9
|
*/
|
|
10
10
|
export class SkiaToggle extends SkiaLayout {
|
|
11
|
+
/** Takes mouse hover unless ReceivesHover is set false (C# ReceivesHoverByDefault): switch, checkbox, radio button. */
|
|
12
|
+
ReceivesHoverByDefault() { return true; }
|
|
11
13
|
controlStyle = "Unset";
|
|
12
14
|
get ControlStyle() { return this.controlStyle; }
|
|
13
15
|
set ControlStyle(v) {
|
|
@@ -83,7 +85,7 @@ export class SkiaToggle extends SkiaLayout {
|
|
|
83
85
|
RebuildDefaultContent() {
|
|
84
86
|
if (!this.contentCreated)
|
|
85
87
|
return;
|
|
86
|
-
this.
|
|
88
|
+
this.ClearChildren();
|
|
87
89
|
for (const p of this.stylePinned)
|
|
88
90
|
this[p] = -1;
|
|
89
91
|
this.stylePinned = [];
|
|
@@ -50,6 +50,8 @@ export declare abstract class SnappingLayout extends SkiaLayout {
|
|
|
50
50
|
protected StopSnapAnimators(): void;
|
|
51
51
|
/** C# SnappingLayout.Render: the transition state follows the position every frame. */
|
|
52
52
|
Render(ctx: DrawingContext): void;
|
|
53
|
+
/** Hover waits while the content slides (C# InTransition) or follows a pan. */
|
|
54
|
+
MovesContent(): boolean;
|
|
53
55
|
ClampOffset(x: number, y: number, rubber: boolean): SKPoint;
|
|
54
56
|
/** C# GetContentOffsetBounds: the extent covered by the snap points. */
|
|
55
57
|
protected BoundsFromSnapPoints(): SKRect;
|
|
@@ -80,9 +80,12 @@ export class SnappingLayout extends SkiaLayout {
|
|
|
80
80
|
}
|
|
81
81
|
/** C# SnappingLayout.Render: the transition state follows the position every frame. */
|
|
82
82
|
Render(ctx) {
|
|
83
|
+
this.Superview?.Hover?.RegisterMover(this);
|
|
83
84
|
super.Render(ctx);
|
|
84
85
|
this.InTransition = !this.CheckTransitionEnded();
|
|
85
86
|
}
|
|
87
|
+
/** Hover waits while the content slides (C# InTransition) or follows a pan. */
|
|
88
|
+
MovesContent() { return this.InTransition || this.IsUserPanning; }
|
|
86
89
|
// ---- clamping ----
|
|
87
90
|
ClampOffset(x, y, rubber) {
|
|
88
91
|
const b = this.ContentOffsetBounds;
|
|
@@ -52,6 +52,12 @@ export declare const Aria: {
|
|
|
52
52
|
* its current item, and the arrow keys move between its items.
|
|
53
53
|
*/
|
|
54
54
|
readonly IsCompositeRole: (role?: string) => boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Roles a user operates (C# Aria.IsInteractiveRole, as DrawnUi.Rust is_control_role): a node with one of them that
|
|
57
|
+
* cannot take input is read as disabled (aria-disabled). C# also lists listbox and scrollbar; here a listbox is an
|
|
58
|
+
* arrow-key group and groups are never disabled.
|
|
59
|
+
*/
|
|
60
|
+
readonly IsInteractiveRole: (role?: string) => boolean;
|
|
55
61
|
};
|
|
56
62
|
/** One laid-out text line exposed for native selection (AccessibilityTextSelectable): CSS px relative to the node. */
|
|
57
63
|
export interface AccessibilityTextLine {
|
|
@@ -72,6 +78,8 @@ export interface AccessibilityNode {
|
|
|
72
78
|
Role: string;
|
|
73
79
|
Rect: SKRect;
|
|
74
80
|
CanInteract: boolean;
|
|
81
|
+
/** A role a user operates (Aria.IsInteractiveRole) that cannot take input: aria-disabled, no tab stop. */
|
|
82
|
+
Disabled: boolean;
|
|
75
83
|
IsPressed?: boolean;
|
|
76
84
|
Live?: string;
|
|
77
85
|
Source: SkiaControl;
|