Skip to content

Styling

Purpose

Turns the theme into GUIStyle objects and RectOffset geometry, caches them, and invalidates the cache when the theme or the editor skin changes. This is the layer that stops a long editor session allocating a GUIStyle and a Texture2D on every repaint.

Owns

  • StyleCache — the cached style getters consumers read
  • StyleRegistry — the backing fields and the single complete Clear()
  • StyleDrawer — layout helpers, the generated-texture cache, the theme designer's own drawing, and deferred live refresh
  • GUIStyleFactory — construction primitives
  • GUIStyleExtensions — the fluent SetFont / SetPadding / SetMargin set
  • SkintGeometry — every padding and margin, derived from four theme tokens
  • QuantizedColorKey — the texture cache key

Does NOT Own

  • The theme model or its persistence — that is Core and the package root
  • Widget behaviour and layout — that is UI
  • Window lifecycle — that is Editor
  • Deciding when a theme changed; it is told

Allowed Dependencies

Styling may depend on:

  • UnityEditor GUI APIs, EditorStyles, UnityEngine
  • Core, for the theme and the resolver

Styling should avoid depending on:

  • UI and Editor territories
  • File IO

Notes

Caching is the point of this territory, so cache invalidation is its main hazard. StyleRegistry.Clear() is deliberately the only complete clear: there were once five partial paths that each forgot something different. It also drops the generated textures, because HideFlags.DontSave means Unity can reclaim a texture while a dictionary still holds the reference — so the texture cache validates entries on read as well.

ApplyLiveStyleRefresh defers through EditorApplication.delayCall with a guard. Rebuilding styles in the middle of the layout pass that is reading them is not survivable.

SkintGeometry derives fifteen distinct padding and margin values from four theme tokens using fixed per-site offsets. This does not eliminate the magic numbers — it names them, puts them in one file, and gives the user a density control rather than sixty dials. Individual edges cannot be tuned in isolation, which is the cost.

The column layout in StyleDrawer.DrawThemeSelector commits its width measurement at the end of a Repaint pass, never where it is taken. IMGUI runs Layout and Repaint as separate passes over the same code and both must emit the same number of layout groups; measuring at the top of Repaint meant the two passes could choose different column counts, which produces "You are pushing more GUIClips than you are popping" and a corrupted layout.

Future Candidates

  • StyleCache.cs is around 700 lines of near-identical lazy getters. Splitting them across partial-class files would move line counts without reducing coupling, so it is deliberately left alone.
  • Borders and corner radii are not themeable.