Skip to content

Latest commit

 

History

History
60 lines (42 loc) · 3.31 KB

File metadata and controls

60 lines (42 loc) · 3.31 KB

Architecture

m3qs is a self-contained QML module with three layers:

  1. Config and theming (Config.qml, Colors.qml, qmldir) -- pure data, no visuals.
  2. Shared primitives (components/common/) -- motion tokens, surface primitive, digit component, motion blur wrapper.
  3. Clock faces (components/) -- four independent, swappable implementations consuming the same config, color, and motion layer.

shell.qml is the composition root. It reads Config.variant, mounts the matching component inside a MotionBlurWrapper, and does nothing else visual.

shell.qml
  MotionBlurWrapper
    Loader { sourceComponent: faceFor(Config.variant) }
      StackedDigitalClock / ScallopedAnalogClock
      PillDigitalClock / WorldClock
         (each composed from TonalSurface + SpringBehavior / EffectBehavior)

Loader pattern

Only one face is ever visible. Each face owns its own animation timers; the analog face repaints a Canvas every tick. Instantiating all four permanently would keep three idle faces driving background timers and repaints. A Loader ensures only the active face's bindings are live.

Motion blur

The MotionBlurWrapper encloses every face and tracks linear velocity from position changes. It renders the face into an offscreen texture and applies a directional Gaussian blur along the velocity vector via a compiled GLSL shader (MotionBlur.frag.qsb). The shader uses up to 31 samples with a Gaussian-weighted kernel and velocity decay framing.

Color flow

wallpaper / system theme
    |
    v
Matugen (matugen/config.toml + matugen/templates/colors.qml) -> Colors.qml
    |
    v
Components read from Colors.* -- no hardcoded hex values

Colors.qml exposes Material 3 semantic roles. The on* color roles (onPrimary, onSurface, etc.) are stored in a sub-object onRef and re-exported via property alias to avoid a QML engine conflict where property names starting with on + uppercase are interpreted as signal handler declarations.

Palettes are selected by Config.theme and read from the palette table in themes/Themes.qml — eight pre-built presets ship by default. If Matugen is installed, its template overwrites Colors.qml with a wallpaper-derived palette instead.

Motion flow

Every animated property uses one of two shared behavior components:

  • Spatial properties (position, size, rotation, radius): SpringBehavior. Duration 500ms times Config.animationSpeed, Easing.OutBack with overshoot 1.0.
  • Color and opacity properties: EffectBehavior. Duration 300ms times Config.animationSpeed, Easing.OutCubic, no overshoot.

Clock faces may define custom Behaviors for specific properties (for example, the analog face uses per-hand RotationAnimations with OutQuint easing), but they always read Config.animationSpeed as the duration multiplier.

Adding a new clock face

  1. Create components/YourClock.qml.
  2. Build panels from TonalSurface -- do not reimplement border and fill locally.
  3. Use SpringBehavior for spatial properties, EffectBehavior for color and opacity. Avoid raw NumberAnimation or ColorAnimation.
  4. Add a routing case in shell.qml's Loader.sourceComponent switch.
  5. Read all colors from Colors.* -- no hardcoded hex values.
  6. Update docs/COMPONENTS.md with the new face's property table.