Class Theme

java.lang.Object
limn.components.Theme

public final class Theme extends Object
Central design tokens: colors, corner radii, typography and spacing. Components read everything from the theme: zero hardcoded colors in widgets. Ships with a set of built-in palettes (see builtins()): light()/dark(), the project's own limn() and limnLight(), plus ones drawn from familiar editor palettes (Darkling, Draculite, Nordic, Arch Dark, Onyx Dark, Monoko Pro, Grovebox Dark, Solaris light/dark, GitHub light, High contrast). The active theme is process-wide (current()/setCurrent(limn.components.Theme)). After switching, call root.markNeedsLayout() so sizes/typography update.

An application can build its own. builder(String, boolean) starts from a working palette and toBuilder() starts from this one, so a palette of your own costs only the tones that make it yours; Theme.Token enumerates them so that code treating them alike does not have to name all of them.

What a palette carries is colour, plus one metric: cornerScale, how round the corners are. Spacing and typography stay process-wide and identical in every palette; see tokens(limn.scene.ControlSize) for why that boundary falls exactly where it does, and why a radius is the one metric allowed across it.

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static final class 
    Collects the colours of a palette and builds one.
    static enum 
    Every colour a palette is made of, as values, so that code which has to treat every tone alike (a serializer, an editor, a contrast report) enumerates them instead of naming them all and getting all but one right.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    final Easing
    Default timing curve for state transitions.
    final double
    Generic show/hide and value fades: the progress bar, a chart's entry animation, and the check and dot marks.
    final double
    Focus-ring fade in/out.
    final double
    Hover fade in/out (buttons, tabs, combo, rows).
    final double
    Selected-tab indicator slide (TabbedPane).
    final double
    Whole-window show/hide fade (native dialogs, popups, floating windows).
    final Color
     
    final Font
     
    final float
    How round the corners are, as a multiplier on every radius in the size table: 0 for square, 1 for the shipped ramp, above 1 for softer, and far enough above for a pill (a radius past half the shorter side is capped when it is drawn).
    final Color
    Error / destructive (red).
    final boolean
    Whether this is a dark palette (lets callers group/pick a sensible default).
    final Color
     
    final Color
     
    final Color
     
    final String
    The typeface family this palette asks for, or Font.DEFAULT_FAMILY: the value it carries when a palette expresses no preference, and the one every built-in carries.
    final Color
    Informational / neutral accent (blue).
    final Font
     
    static final float
    The largest cornerScale a palette may ask for.
    final String
    Human-readable name, the label a theme picker shows.
    final Color
     
    final Color
     
    final Color
     
    final Color
     
    final Color
     
    final Color
    The veil an in-scene modal dialog paints over everything beneath it, alpha included: the one tone in a palette whose alpha carries meaning.
    final float
     
    final float
     
    final float
     
    final Color
    Success / valid (green).
    final Color
     
    final Color
     
    final Color
     
    final Color
     
    final Font
     
    final Color
    Warning / caution (amber).
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    Asks Fonts to resolve Font.DEFAULT_FAMILY to this palette's fontFamily: the act that makes a palette's type preference visible.
    builder(String name, boolean dark)
    A palette of this application's own, seeded from the built-in light() or dark() so that every tone is already a working one and only what makes the palette distinct has to be spelled out.
    static List<Theme>
    Every palette that ships with the toolkit, in presentation order.
    static Theme
     
    static Theme
    The built-in dark palette.
    The name a picker shows, in the UI language; name stays the identifier.
    boolean
    equals(Object other)
    Two palettes are equal when they carry the same name, mode, shape, typeface preference and colours, which is everything a palette is, and everything ThemeFormat writes.
    int
     
    static Theme
    The built-in light palette.
    static Theme
    The project's own palette: a deep violet canvas with a vivid violet accent, tuned so that body text, muted text, the accent's own label and the focus ring each clear WCAG AA on the surface they land on.
    static Theme
    The light companion to limn(), held to the same contrast targets.
    static void
    Switches the process-wide palette.
    This palette's tones, in a builder: the form to start from when an application wants a built-in with two things changed rather than a palette of its own.
    Metric tokens for a size step.
    tokensFor(Widget widget)
    Tokens for the step resolved on widget, the one line components call.
    The palette's name.

    Methods inherited from class java.lang.Object

    clone, finalize, getClass, notify, notifyAll, wait, wait, wait
  • Field Details

    • MAX_CORNER_SCALE

      public static final float MAX_CORNER_SCALE
      The largest cornerScale a palette may ask for. Past this every control in the toolkit is already a pill (a radius beyond half the shorter side is capped when it is drawn), so a higher number would change nothing and only look like it might.
      See Also:
    • name

      public final String name
      Human-readable name, the label a theme picker shows.
    • dark

      public final boolean dark
      Whether this is a dark palette (lets callers group/pick a sensible default).
    • background

      public final Color background
    • surface

      public final Color surface
    • surfaceRaised

      public final Color surfaceRaised
    • primary

      public final Color primary
    • primaryHover

      public final Color primaryHover
    • primaryPressed

      public final Color primaryPressed
    • onPrimary

      public final Color onPrimary
    • text

      public final Color text
    • textMuted

      public final Color textMuted
    • outline

      public final Color outline
    • focusRing

      public final Color focusRing
    • disabledFill

      public final Color disabledFill
    • disabledText

      public final Color disabledText
    • scrim

      public final Color scrim
      The veil an in-scene modal dialog paints over everything beneath it, alpha included: the one tone in a palette whose alpha carries meaning. It is painted once, over the whole scene, and the card sits on top of it.

      Between transparent and opaque, and neither end is usable: at alpha 0 nothing behind the card dims and there is no sign the rest of the window has stopped accepting input, and at alpha 1 the content behind the card is gone rather than blocked. ThemeContrastTest holds every shipped palette between those.

      A window blocked by a native modal is dimmed by the scene, not by this. That veil lives a layer below, where no palette can be seen at all, so moving this tone does not move it and the two are only ever approximately alike.

    • danger

      public final Color danger
      Error / destructive (red).
    • success

      public final Color success
      Success / valid (green).
    • warning

      public final Color warning
      Warning / caution (amber).
    • info

      public final Color info
      Informational / neutral accent (blue).
    • cornerScale

      public final float cornerScale
      How round the corners are, as a multiplier on every radius in the size table: 0 for square, 1 for the shipped ramp, above 1 for softer, and far enough above for a pill (a radius past half the shorter side is capped when it is drawn).

      The one metric a palette carries, and it is here rather than in the size table because it is the only one nothing measures from: not one onMeasure, onLayout, baselineOffset or paintOutset in this toolkit reads a radius, so a change of shape is a repaint exactly like a change of colour. ThemeShapeTest asserts that, and it is what the whole design rests on; see tokens(limn.scene.ControlSize).

    • fontFamily

      public final String fontFamily
      The typeface family this palette asks for, or Font.DEFAULT_FAMILY: the value it carries when a palette expresses no preference, and the one every built-in carries.

      A declaration, not a resolution. Nothing here reads a font file or checks that the family exists; a palette is a value and stays one. What acts on it is applyFontFamily(), which hands the name to Fonts.setDefaultFamily(java.lang.String), and that is where the fallback lives: a family the backend cannot resolve, including every family on a machine that does not have it installed, renders in the toolkit's embedded Roboto. Naming a font that only exists on the designer's laptop therefore degrades to the shipped face rather than to tofu or to an exception.

      Why this is a name and not a Font on the size rows. Type is measured from, and setCurrent(limn.components.Theme) notifies nobody; see tokens(limn.scene.ControlSize). Baking the family into the five rows would change every text measurement in every live window with no relayout anywhere. Font.DEFAULT_FAMILY is resolved late, by the backend, and Fonts already notifies its listeners so that scenes re-lay-out; routing a palette's type preference through that mechanism is the only shape that does not need a theme-change listener this repository does not have.

    • body

      public final Font body
    • label

      public final Font label
    • title

      public final Font title
    • spacingSmall

      public final float spacingSmall
      See Also:
    • spacingMedium

      public final float spacingMedium
      See Also:
    • spacingLarge

      public final float spacingLarge
      See Also:
    • animEasing

      public final Easing animEasing
      Default timing curve for state transitions.
    • animHover

      public final double animHover
      Hover fade in/out (buttons, tabs, combo, rows).
      See Also:
    • animFocus

      public final double animFocus
      Focus-ring fade in/out.
      See Also:
    • animFade

      public final double animFade
      Generic show/hide and value fades: the progress bar, a chart's entry animation, and the check and dot marks. Deliberately not the scrollbar, which declares its own four durations because its reveal is asymmetric (it appears fast and leaves slowly, after a hold), and no single token can say that.
      See Also:
    • animTab

      public final double animTab
      Selected-tab indicator slide (TabbedPane).
      See Also:
    • animWindow

      public final double animWindow
      Whole-window show/hide fade (native dialogs, popups, floating windows).
      See Also:
  • Method Details

    • light

      public static Theme light()
      The built-in light palette.
    • dark

      public static Theme dark()
      The built-in dark palette.
    • limn

      public static Theme limn()
      The project's own palette: a deep violet canvas with a vivid violet accent, tuned so that body text, muted text, the accent's own label and the focus ring each clear WCAG AA on the surface they land on.
    • limnLight

      public static Theme limnLight()
      The light companion to limn(), held to the same contrast targets.
    • displayName

      public I18nString displayName()
      The name a picker shows, in the UI language; name stays the identifier. Only the descriptive palettes translate (Light, Dark, High Contrast); the rest are the names of the palettes they came from and read the same everywhere.
    • builtins

      public static List<Theme> builtins()
      Every palette that ships with the toolkit, in presentation order.
    • current

      public static Theme current()
      Returns:
      the process-wide active theme
    • tokens

      public final SizeTokens tokens(ControlSize size)
      Metric tokens for a size step. The returned record (and the Fonts inside it) are stable for the life of this palette, so == holds and the backend's identity-keyed font memo keeps hitting. Never build tokens per call, and never call Font.withSize to derive a step's font.

      Every metric here is palette-independent except the corner radii, which follow cornerScale. At the default scale this returns the process-wide row itself, so a palette without a shape of its own is indistinguishable from what shipped before shape existed.

      Why the radii are allowed out and nothing else is. setCurrent(limn.components.Theme) only assigns a volatile field (there is no theme-change listener anywhere in this repository), so a palette that could change a metric something measures from would change every measurement in every window with zero relayout. A radius is the one metric nothing measures from: no onMeasure, onLayout, baselineOffset or paintOutset in this toolkit reads one, which ThemeShapeTest asserts by measuring a tree at two shapes and demanding the same numbers. Break that and shape stops being safe, not just the test.

      Two further traps stand in the way of any metric that is measured from. The six float token fields above are inlined at their call sites (JLS 13.1), so a per-palette value cannot reach a field read at all, which is exactly why the three radius fields are deprecated rather than made to follow the scale. And the nine token-backed fields read the static MEDIUM row, so they would be invisible to every unmigrated call site. If per-palette type or spacing is ever wanted, both prerequisites are required: route setCurrent through the same weak per-scene registry as Fonts/ControlSize, and initialize those fields from this.tokens(MEDIUM) in the constructor.

    • tokensFor

      public final SizeTokens tokensFor(Widget widget)
      Tokens for the step resolved on widget, the one line components call.
    • setCurrent

      public static void setCurrent(Theme theme)
      Switches the process-wide palette. Nothing is notified: call root.markNeedsLayout() on each live scene afterwards, since type and spacing can differ between palettes.
    • applyFontFamily

      public void applyFontFamily()
      Asks Fonts to resolve Font.DEFAULT_FAMILY to this palette's fontFamily: the act that makes a palette's type preference visible.

      Separate from setCurrent(limn.components.Theme) on purpose, and the separation is the contract: switching a palette is a repaint, while switching a font is a relayout and, the first time a face is named, a font file read inside the next frame. Fonts says so on Fonts.setDefaultFamily(java.lang.String) and the rule it states applies here unchanged; call this from a settings screen or a theme picker, where one long frame reads as the switch happening, and never from an animation or a drag. Folding it into setCurrent would smuggle that read into every palette change, including the ones a colour well makes on every frame of a drag.

      A palette that expresses no preference carries Font.DEFAULT_FAMILY, so calling this for one restores the toolkit's own face rather than leaving the previous palette's choice in place. Both directions are therefore safe to call unconditionally.

      UI thread.

    • builder

      public static Theme.Builder builder(String name, boolean dark)
      A palette of this application's own, seeded from the built-in light() or dark() so that every tone is already a working one and only what makes the palette distinct has to be spelled out.
      
       Theme mine = Theme.builder("Ocean", true)
               .background(Color.rgb(0x0B1A24))
               .surface(Color.rgb(0x11242F))
               .surfaceRaised(Color.rgb(0x1A3340))
               .primary(Color.rgb(0x4FD1C5))
               .onPrimary(Color.rgb(0x04141A))
               .deriveAccentStates()
               .deriveDisabled()
               .build();
       Theme.setCurrent(mine);
       
      Parameters:
      name - the palette's identifier and its fallback display text; see Theme.Builder.name, which explains why a name shared with a built-in is a name to avoid
      dark - whether the palette is dark, which decides the seed and which tones the derive methods produce
    • toBuilder

      public Theme.Builder toBuilder()
      This palette's tones, in a builder: the form to start from when an application wants a built-in with two things changed rather than a palette of its own.

      The theme is unaffected: a Theme is immutable and the builder holds a copy.

    • equals

      public boolean equals(Object other)
      Two palettes are equal when they carry the same name, mode, shape, typeface preference and colours, which is everything a palette is, and everything ThemeFormat writes. The metrics do not appear here because they are process-wide: only the corner scale and the family name are a palette's to choose, and both are compared.

      Here so that an editor can ask "has this been changed" and a parser can be held to a round trip. The toolkit itself still compares the active palette by identity.

      Overrides:
      equals in class Object
    • hashCode

      public int hashCode()
      Overrides:
      hashCode in class Object
    • toString

      public String toString()
      The palette's name. A theme picker shows displayName(), not this.
      Overrides:
      toString in class Object