Package limn.graphics

Class Fonts

java.lang.Object
limn.graphics.Fonts

public final class Fonts extends Object
Process-wide font configuration (mirrors the TextRulers / Ui facade lifecycle). Holds the installed FontCatalog (the list of usable families) and the current default family that Font.DEFAULT_FAMILY resolves to, so an application can switch its UI font at runtime.

Both the catalog becoming available (system-font enumeration finishes) and a default-family change notify listeners on the calling thread; scenes subscribe to re-layout, and the backend's font store subscribes to drop its resolution cache. Mutators are meant to be called on the UI thread.

  • Method Details

    • installCatalog

      public static void installCatalog(FontCatalog newCatalog)
      Installs the backend's catalog (e.g. once system-font enumeration completes).
    • uninstallCatalog

      public static void uninstallCatalog(FontCatalog current)
      Resets to FontCatalog.EMPTY (backend shutdown).
    • installLoader

      public static void installLoader(FontLoader newLoader)
      Installs the backend's file loader (backend startup).
    • uninstallLoader

      public static void uninstallLoader(FontLoader current)
      Resets to FontLoader.UNAVAILABLE (backend shutdown).
    • load

      public static String load(Path file)
      Registers a font file the application ships and returns the family name it declares, so the family can then be named in a Font or in setDefaultFamily(java.lang.String).

      UI thread. Synchronous, and deliberately so; there is no asynchronous form. What this reads is the file's name table, which is kilobytes: the glyph outlines are parsed on first use, exactly as they are for an operating-system face, so the large read this facade could offload is not the read it performs. Offloading it would also defeat the point of the call, because the family has to be registered before the caller builds the first widget that names it; an asynchronous form would hand back a name that resolves to the default face until some later frame, which is the silent-wrong-font failure this API exists to remove.

      A relative path resolves against the process working directory. That makes the call dependent on where the process was started, so an application that ships a typeface should resolve its own path from something it controls rather than assuming a directory.

      Throws:
      UnsupportedOperationException - if no backend is installed (nothing here can parse a font file; see FontLoader)
      IllegalArgumentException - if the file carries no usable face
    • available

      public static List<String> available()
      Returns:
      the available families (bundled + system), or empty when headless. Reads no file and does not block. It is the installed catalog's current answer, which may be a partial one: a backend that enumerates the operating system does so in the background and installs a fuller catalog when it finishes. Rebuild a family list from a change listener rather than reading this once at startup and trusting it.
    • defaultFamily

      public static String defaultFamily()
      Returns:
      the family Font.DEFAULT_FAMILY currently resolves to
    • setDefaultFamily

      public static void setDefaultFamily(String family)
      Sets the family the default UI font resolves to; null/blank restores Font.DEFAULT_FAMILY. No-op if unchanged. Notifies listeners so the UI re-lays-out in the new font. UI thread.

      This call itself reads nothing: it stores a name and runs the listeners, and there is no asynchronous form for that reason. The cost it schedules is the part worth knowing. A backend resolves families to font files lazily and drops its resolution cache when this fires, so the next measure or paint resolves the new family, and if its face is not already resident, that resolution reads and parses a font file (tens of megabytes for a CJK face) on the UI thread, inside that frame. Switching the UI font is the one public act in the toolkit that guarantees a large read inside the next frame, and it cannot be moved out of that frame from here: nothing in this package can read a font file, so nothing here can make the face resident first.

      What follows from that is a placement rule, not a workaround. Call this from a settings dialog or a menu, where one long frame reads as the switch happening; never from an animation, a drag, or anything that must stay at frame rate. It costs once per face; the second switch back to a family already read is cheap.

    • addChangeListener

      public static void addChangeListener(Runnable listener)
      Subscribes to catalog/default-family changes (idempotent per instance).
    • removeChangeListener

      public static void removeChangeListener(Runnable listener)
      Unsubscribes; no-op when it was never registered.