Add shared UI animation scheduler#4
Merged
Conversation
Document the existing Task.Delay-based animation loop, local UI timers, dispatcher boundaries, lifecycle gaps, and the distinction between property animation and ordinary debounce or media timing. Choose one process-wide scheduler backed by a monotonic clock, a single idle-aware tick source, and the existing Windows or Android UI dispatcher. Define owner-key replacement, deterministic testing, reduced motion, brush mutation, fault isolation, disposal, and diagnostics before changing implementation. The decision preserves existing control helpers and defers ThemeManager, Shape, navigation, advanced layout animation, and native frame pacing.
Replace the Task.Delay animation loop with one monotonic, idle-aware scheduler that coalesces timer requests through the existing UI dispatcher and derives progress from elapsed time rather than frame count. Add public handles, state, options, owner-key replacement, pause and resume, shutdown, reduced-motion policy, diagnostics, easing validation, and platform-neutral value interpolators. Preserve the existing control animation helpers and Switch behavior by delegating them to the shared implementation. Start, cancel, state reads, and shutdown are thread-safe; user callbacks never run under the scheduler lock. Control disposal and visual-tree detachment cancel owned work, and application exit shuts the scheduler down. Android lifecycle signaling and brush-specific transitions are completed in focused follow-up commits.
Introduce a platform-neutral application lifecycle contract in the backend foundation and have AndroidActivityTracker publish foreground, background, and no-host transitions. Bind the default animation scheduler lazily to the registered lifecycle service. Backgrounding stops the shared tick source and freezes effective monotonic time; foregrounding resumes remaining work without jumping over the paused interval. Windows continues to use application shutdown and the existing dispatcher because it has no equivalent backend lifecycle service. The Android target builds with the new contract, while physical-device frame pacing remains a manual validation item.
Add allocation-conscious interpolation for solid, linear, radial, and sweep brushes, including opacity, transforms, gradient geometry, stops, and spread-mode transitions. Keep local transitions isolated by cloning the source brush once, while providing explicit in-place helpers for shared observable resources. Existing Brush.Changed notifications continue to drive render-only invalidation on the UI thread without adding a parallel rendering path. Reject incompatible brush types and gradient structures before scheduling so failures are immediate and deterministic on Windows and the experimental Android backend.
Add a manual monotonic clock, tick source, and dispatcher harness so animation tests advance time deterministically without sleeps or real frame timers. Cover elapsed-time progress, delayed and dropped frames, UI-thread dispatch, replacement, cancellation, pause and resume, reduced motion, duration scaling, fault isolation, shutdown, high animation counts, primitive interpolation, Brush cloning, in-place resource animation, invalidation, and control lifetime cancellation. The complete ModernFormsNext.Tests project passes with 466 tests after these additions.
Add an opt-in ControlGallery page for opacity, color, easing, cancellation, owner-key replacement, gradient-stop and brush-transform animation, parallel channels, reduced motion, and scheduler diagnostics. The page starts no background work on its own. It tracks every non-panel-owned handle, cancels all work on unload or disposal, and restores the application motion policy so navigation away cannot leave CPU activity or altered global settings behind. ControlGallery builds successfully; visual interaction remains a manual Windows validation step.
Inject the platform-neutral application lifecycle through the internal scheduler seam so background pause/resume and event detachment can be verified without a global service registration or Android runtime. Expand deterministic coverage for cancellation before the first update and during delay, terminal idempotence, easing boundaries and failures, no repaint after cancellation, and the distinction between layout-affecting bounds and render-only transforms. All 484 ModernFormsNext.Tests cases pass without Thread.Sleep; the production API and platform behavior remain unchanged.
Document control and typed animations, easing, cancellation, replacement, local versus in-place Brush transitions, dynamic resources, UI-thread rules, owner disposal, Android background pause, reduced motion, diagnostics, Designer behavior, and current limitations. Align the architecture document with the implemented clone-versus-shared Brush decision, link the guide from README, and mark the scheduler foundation complete in the roadmap without claiming ThemeManager or Shape delivery. The recommended next stage is ThemeManager with JSON serialization and animated theme transitions; all relative documentation links were validated locally.
ProGraMajster
marked this pull request as ready for review
July 19, 2026 19:54
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The previous animation foundation used a
Task.Delay(16)loop around a sharedStopwatchand mutable animation list. The continuation could leave the UI context, progress and lifecycle behavior were difficult to test deterministically, owner disposal was not part of the contract, and Windows/Android did not share an explicit application-lifecycle policy.This PR replaces that foundation without implementing ThemeManager, Shape, navigation, or a full animated-layout system.
Architecture
Stopwatch.GetTimestamptime, not frame counts, so delayed frames catch up without extending duration.Thread.Sleep.The design and alternatives are documented in:
docs/architecture/ui-animation-scheduler.mddocs/architecture/decisions/ADR-UI-Animation-Scheduler.mdPublic API
The feature adds:
AnimationSchedulerAnimationHandleAnimationStateAnimationOptionsAnimationPolicyAnimationReplacementModeAnimationSchedulerDiagnosticsIAnimationInterpolator<T>AnimationInterpolatorsExisting
FadeToAsync,TranslateToAsync,ScaleToAsync,RotateToAsync, andSwitchbehavior delegates to the new scheduler rather than maintaining a second timing system.Cancellation and replacement
Animations are identified by owner reference plus string key. The default behavior replaces and cancels the matching animation;
IgnoreNewkeeps the existing handle. Cancellation is idempotent, never reports successful completion, removes the entry, and prevents later updates or repaint.Control-owned work is canceled when the control is detached or disposed. Application shutdown permanently shuts down the default scheduler.
Easing and interpolation
Built-in linear/ease-in/ease-out/ease-in-out functions remain available and custom easing is supported. Finite overshoot is preserved for Back/Elastic-style functions; NaN, infinity, or exceptions fault only the affected animation and are reported through diagnostics/trace.
Typed interpolation covers
float,double,int,PointF,SizeF,RectangleF, alpha-awareColor,Matrix3x2,GradientStop, and compatible brushes.Brush integration
Solid, linear, radial, and sweep brushes support color, opacity, transform, geometry, gradient-stop, and spread-mode transitions.
AnimateTohelpers can intentionally mutate a shared observable brush or gradient stop.Lifecycle and reduced motion
The backend foundation now exposes a platform-neutral application lifecycle contract. Android's activity tracker reports foreground, background, and no-host states. Backgrounding pauses scheduler time and the tick source; foregrounding resumes remaining work without a time jump.
AnimationPolicycentrally supports animation enable/disable, reduced motion, and duration scaling. Disabled/reduced-motion and zero-duration work posts the final value once without starting the tick source. Design-time execution likewise settles at the final value instead of leaving an active timer.Windows and Android impact
net10.0-android.Tests
Deterministic coverage includes:
Validation completed locally:
ControlGallery
A dedicated opt-in page demonstrates opacity, color, easing, cancel/replace, gradient stops, brush transforms, parallel animations, reduced motion/disabled motion, and diagnostics. It starts no automatic work and cancels tracked handles/restores global policy when unloaded.
No screenshot or recording is attached; interactive Windows visual verification remains a manual review item.
Known limitations
Next step
ThemeManager with JSON serialization and animated theme transitions, built on this scheduler. ThemeManager and Shape are deliberately not marked complete by this PR.