Skip to content

Add ThemeManager, JSON themes and animated transitions#17

Merged
ProGraMajster merged 6 commits into
masterfrom
feature/theme-manager
Jul 23, 2026
Merged

Add ThemeManager, JSON themes and animated transitions#17
ProGraMajster merged 6 commits into
masterfrom
feature/theme-manager

Conversation

@ProGraMajster

@ProGraMajster ProGraMajster commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a production-oriented, application-wide ThemeManager for ModernFormsNext with typed, inheritable themes, strict JSON import/export, atomic resource publication, and optional animated transitions driven by the existing animation scheduler.

Architecture

  • Keeps shared theming logic platform-neutral under ModernFormsNext/Theming.
  • Preserves the existing CLR-property and resource model; no dependency-property or parallel rendering infrastructure was introduced.
  • Adds an application-scoped ThemeManager singleton, a registry of immutable resolved snapshots, and versioned theme definitions.
  • Built-in Base, Light, and Dark themes provide semantic colors, brushes, typography, spacing, padding, sizing, corners, borders, animations, and compatibility aliases.
  • Theme inheritance is validated for missing bases, cycles, depth, key/type conflicts, and invalid values.
  • Applying a theme resolves and validates off the UI thread, then commits a single versioned snapshot on the UI dispatcher.
  • Resource publication and the legacy static Theme projection are swapped atomically. Observer failures are surfaced without leaving partially applied framework state, and commit failures roll back.

Public API

The change introduces:

  • ThemeManager, ThemeManagerOptions, apply results, diagnostics, events, transition handles, and cancellation/replacement policies.
  • ThemeDefinition, ThemeResolvedSnapshot, ThemeVariant, typed token/value records, token counts, validation results, and security limits.
  • ThemeJsonSerializer with sync/async string, stream, and file APIs.
  • Application.ThemeResources, a read-only application theme-resource surface.
  • IPlatformThemeSettings for backend-provided system theme and reduced-motion preferences.

All new public and protected APIs include XML documentation. The usage guide contains code-first examples and migration guidance.

JSON schema and security

  • Schema version 1 is documented in docs/theme-json-schema.md.
  • Serialization is deterministic and round-trippable.
  • Deserialization uses explicit allowlists for token kinds, resource value kinds, brush discriminators, and brush fields.
  • Unknown or duplicate properties, future schema versions, unsupported brushes, invalid numbers, nulls, excessive document size/depth/token counts/string length/gradient stops/inheritance depth, and overlong animations are rejected.
  • Loading one file never implicitly loads adjacent files.
  • No arbitrary CLR type names, reflection-based construction, or executable payloads are accepted.
  • An internal migration hook provides a controlled path for future schema versions.

Inheritance and resources

Theme lookup now follows:

Control -> nearest parent -> window -> Application.Resources -> Application.ThemeResources -> captured CLR fallback

Theme resources remain below application overrides, so application resources keep precedence. Definitions are deeply cloned and resolved snapshots are immutable/read-only; mutable brushes are cloned at boundaries. Custom resources use a closed typed union and derived themes must preserve the inherited resource kind.

Transitions

  • Immediate and animated apply paths are supported.
  • Transitions reuse the existing AnimationScheduler and BrushAnimationPlan.
  • Color, numeric custom resources, solid brushes, and compatible linear/radial/sweep gradients interpolate, including opacity, transforms, geometry, stops, colors, and offsets.
  • Layout-affecting tokens, Glass/NoBrush, incompatible brushes, and discrete values commit immediately.
  • One working brush is reused during compatible brush animation to avoid per-frame brush allocation.
  • Replacement defaults to latest-wins; IgnoreNew preserves the active transition.
  • Stale handles cannot cancel newer transitions. Explicit cancellation deterministically snaps to the final target.
  • Disabled animations, reduced motion, missing presentation hosts, and lifecycle transitions deterministically fall back to immediate completion.

Platform behavior

  • Windows reads the system application theme and client-area animation preference through the Windows backend.
  • Android support remains experimental and currently uses the documented explicit fallback because no platform theme-settings provider is implemented there.
  • No platform-specific implementation leaked into shared framework projects.

Designer behavior

Designer-facing metadata hides complex dictionaries from property-grid expansion, while serialization and cloning remain deterministic. Designer tests verify that theme operations do not create unintended runtime side effects.

Samples

A dedicated ControlGallery ThemeManager panel demonstrates:

  • immediate and animated Light/Dark changes;
  • rapid replacement and cancellation;
  • animation and reduced-motion policies;
  • inherited valid JSON;
  • malformed JSON, invalid brush discriminators, missing bases, and cycles;
  • theme resources, typography, spacing, corners, and diagnostics.

ModernFormsNext.DemoApp was not changed; it remains the default template/reference application.

Tests and validation

Deterministic tests cover the model, inheritance, schema, security limits, atomic apply/rollback, UI-thread commit, latest-wins behavior, cancellation, transitions, resources, lookup precedence, legacy compatibility, designer safety, platform fallbacks, and diagnostics. No Thread.Sleep is used.

Local validation completed:

  • dotnet restore .\ModernFormsNext.slnx --force --no-cache
  • full Debug build
  • full Release build
  • full Debug test run: 664 passed, 0 failed, 0 skipped
  • full Release test run: 664 passed, 0 failed, 0 skipped
  • Release package validation: 8 nupkg and 7 snupkg produced
  • ControlGallery manual visual validation: immediate and animated Light/Dark, rapid switching, leave/re-enter, minimize/restore, and resize
  • ModernFormsNext.DemoApp startup smoke test
  • VSIX artifact produced
  • Android SmokeTest signed and unsigned APK artifacts produced
  • git diff --check origin/master...HEAD

Existing solution warnings remain, including dependency advisories/compatibility warnings in the Visual Studio extension, legacy nullability/obsolete API warnings, platform analyzer warnings, and the Android 16 KB page-size warning. A no-incremental warning audit found no warning lines in the new ThemeManager-related areas.

No version was bumped and no package was published.

Documentation

  • accepted architecture decision record
  • complete usage guide and API examples
  • versioned JSON schema
  • draft 1.9.0 release/migration guide
  • README, architecture, dynamic-resource, Android-platform, and roadmap updates

Known limitations

  • No file watcher or automatic theme hot reload.
  • No automatic re-apply when the operating-system theme changes.
  • Layout tokens and incompatible/non-interpolatable brush forms switch immediately.
  • No shadow-token model in this version.
  • Typography line height and letter spacing are stored and serialized but are not globally consumed by every renderer.
  • Android does not yet provide live system-theme/reduced-motion integration.
  • Interactive Designer and device/emulator validation were not available; ControlGallery manual visual validation was completed on Windows.

Manual verification checklist

  • Light to Dark, immediate and animated.
  • Rapid successive applies and replacement policy.
  • Explicit cancellation and stale-handle behavior.
  • Toggle animations and reduced motion during a transition.
  • Leave/re-enter, minimize/restore, and resize while transitions run.
  • Valid inherited JSON; malformed JSON; invalid brush discriminator; missing base; cycle.
  • Gradient geometry/stops/opacity/transform visuals.
  • Active animation count returns to zero and ticking stops.
  • Android background/foreground lifecycle behavior on a device or emulator.
  • Designer loading and editing without runtime side effects.

UI refresh regression fix

Root cause

The committed legacy Theme values and static ControlStyle defaults were updated correctly, but Form.OnThemeChanged visited only the adapter's immediate children and Control.OnThemeChanged only marked controls dirty without scheduling a platform repaint. Nested controls therefore resolved the new values only after hover, click, focus, resize, or another later state invalidation. Dynamic-resource setters invalidated some controls independently, which made the failure appear partial.

Propagation and invalidation

The UI-thread commit now follows one coherent order:

resource snapshot -> ResourceDictionary notifications/setters -> legacy Theme/ControlStyle refresh -> recursive refresh of every open form and the active popup -> one batched platform repaint per affected window

The complete explicit and framework-owned implicit control tree is visited. Normal, Hover, Pressed, Focused, Disabled, and control-specific state remain unchanged and are resolved again against the committed style values. The generic path performs visual invalidation only; it does not force a full layout. A control may still request its existing targeted layout path when theme typography or metrics affect cached measurement.

Dynamic-resource subscriptions continue to update controls without recreation, and their setter invalidations join the same window batch. Regression coverage verifies that reparenting does not duplicate subscriptions and that disposed controls stop receiving updates.

Animated transitions

Each scheduler tick updates brush/color resources and the legacy projection inside a visual-only invalidation batch. Every affected window receives one platform repaint request per tick, while control-specific layout/cache hooks are bypassed. At completion the active animation count returns to zero and the tick source stops. The completion event is delivered before the public completion task is released, so awaited state is stable.

Regression coverage

Ten deterministic tests cover immediate Normal refresh; preserved Hover; Pressed, Focused, and Disabled states; resource invalidation without mouse input; nested trees; multiple windows; successive animation ticks; one coherent immediate refresh; subscription/disposal cleanup; and scheduler/invalidation idle cleanup.

Final local validation:

  • full Debug build: succeeded;
  • full Release build: succeeded;
  • full Debug test run: 664 passed, 0 failed, 0 skipped;
  • full Release test run: 664 passed, 0 failed, 0 skipped;
  • theme-focused regression run: 69 passed, 0 failed;
  • ControlGallery: immediate and animated Light/Dark, Rapid x3, leave/re-enter, minimize/restore, and resize all passed without requiring hover, click, focus, or mouse movement after the apply action.

Closes #7
Closes #8
Closes #9
Closes #10

Record application lifetime, dispatcher affinity, immutable resolution, Brush ownership, and resource precedence.

Specify atomic commit and rollback ordering, scheduler-only transitions, strict JSON trust boundaries, Designer behavior, compatibility, and platform limitations.
Add mutable definitions, typed semantic and custom tokens, single-base inheritance, immutable resolved snapshots, built-in Light/Dark themes, validation diagnostics, and strict schema-v1 System.Text.Json serialization.

Commit manager-owned resources atomically on the UI dispatcher with rollback and legacy Theme projection. Integrate scheduler-only color, numeric, solid, and compatible gradient transitions with replacement, cancellation, reduced motion, lifecycle handling, diagnostics, Designer isolation, and Windows preference providers.
Cover definitions, inheritance, immutable Brush ownership, resource precedence, dynamic refresh, Designer behavior, JSON round trips and security limits, atomic rollback, dispatcher affinity, and safe failures.

Exercise scheduler-driven colors, numeric resources, solid and gradient geometry/stops/transforms, rapid replacement, stale handles, cancellation, motion policies, observer faults, lifecycle pause/resume, no-host behavior, and orphan cleanup without Thread.Sleep.
Add an isolated ControlGallery page for immediate and animated Light/Dark switches, rapid replacement, cancellation, scheduler policy toggles, inherited JSON themes, safe validation failures, semantic values, Brushes, metrics, and diagnostics.

Restore scheduler policy and event subscriptions on unload, and keep the template-reference DemoApp unchanged.
Document built-ins, typed and custom tokens, inheritance, immutable snapshots, Brush clone boundaries, resource precedence, atomic event ordering, transitions, diagnostics, strict schema-v1 JSON, security limits, and the interactive checklist.

Update architecture, dynamic-resource precedence, roadmap, Windows/experimental Android limitations, migration guidance, known limitations, and draft 1.9.0 notes without changing package versions.
Refresh complete open-window control trees after atomic theme commits and coalesce invalidations into one platform repaint per window.

Preserve live interaction state, keep animation frames visual-only, and make transition completion observable after the completion event. Add deterministic regression coverage for resources, nested and multiple windows, state preservation, animation ticks, subscriptions, and idle cleanup.
@ProGraMajster
ProGraMajster marked this pull request as ready for review July 23, 2026 16:12
@ProGraMajster
ProGraMajster merged commit 002b920 into master Jul 23, 2026
1 check passed
@ProGraMajster
ProGraMajster deleted the feature/theme-manager branch July 23, 2026 16:21
@github-project-automation github-project-automation Bot moved this from Backlog to Done in ModernFormsNext Roadmap Jul 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Document the ModernFormsNext 1.9.0 theme system Add animated theme transitions Add JSON theme serialization Implement ThemeManager

1 participant