Class Fonts
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.
Letting the user choose the font takes three calls: available() for the
list, rebuilt from observeChanges(java.lang.Runnable) because the list grows when the operating system's
families have been found, and setDefaultFamilyWhenResident(java.lang.String) for the choice, which reads
the family's regular and bold faces on a worker and switches once they are in memory, so the
frame that shows the new font reads no file for them. setDefaultFamily(java.lang.String) switches at
once and leaves that read to the next frame. preload(java.lang.String) reads a family ahead without
switching to it.
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 Summary
Modifier and TypeMethodDescriptionstatic Stringstatic voidinstallCatalog(@Nullable FontCatalog newCatalog) Installs the backend's catalog (e.g.static voidinstallLoader(@Nullable FontLoader newLoader) Installs the backend's file loader (backend startup).static StringRegisters a font file the application ships and returns the family name it declares, so the family can then be named in aFontor insetDefaultFamily(java.lang.String).static SubscriptionobserveChanges(Runnable listener) Subscribes to catalog and default-family changes.static CompletionStage<Void> Makesfamily's regular and bold faces resident without reading a font file on the UI thread, so that the first measure or paint in that family finds them in memory instead of reading them inside its frame.static voidsetDefaultFamily(@Nullable String family) Sets the family the default UI font resolves to;null/blank restoresFont.DEFAULT_FAMILY.static CompletionStage<Boolean> setDefaultFamilyWhenResident(@Nullable String family) Sets the family the default UI font resolves to once its faces are resident: reads them on a worker aspreload(java.lang.String)does, then makes the switchsetDefaultFamily(java.lang.String)makes, on the UI thread, so the frame that shows the new font reads no file for its regular or bold text; an italic face, not read ahead, is still read where it is first drawn.static voiduninstallCatalog(FontCatalog current) Resets toFontCatalog.EMPTY(backend shutdown).static voiduninstallLoader(FontLoader current) Resets toFontLoader.UNAVAILABLE(backend shutdown).
-
Method Details
-
installCatalog
Installs the backend's catalog (e.g. once system-font enumeration completes). -
uninstallCatalog
Resets toFontCatalog.EMPTY(backend shutdown). -
installLoader
Installs the backend's file loader (backend startup). -
uninstallLoader
Resets toFontLoader.UNAVAILABLE(backend shutdown). AsetDefaultFamilyWhenResident(java.lang.String)still waiting on that loader's read is forgotten: its backend will not deliver it, and it switches nothing. -
load
Registers a font file the application ships and returns the family name it declares, so the family can then be named in aFontor insetDefaultFamily(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. The parse of the faces themselves can leave the frame that first draws them:
preload(java.lang.String)the family it returns.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; seeFontLoader)MalformedContentException- if the file carries no usable face
-
preload
Makesfamily's regular and bold faces resident without reading a font file on the UI thread, so that the first measure or paint in that family finds them in memory instead of reading them inside its frame. It does not switch to the family; seesetDefaultFamilyWhenResident(java.lang.String)for that.UI thread. The backend reads on its worker pool, and the returned stage completes on the UI thread, on a later turn and never inside this call, once the faces are resident. It completes the same way when there is nothing to read: a family the backend carries itself (
Font.DEFAULT_FAMILYamong them), one already resident, one the operating system does not have, a blank name, and any family when no backend has installed a font loader, as under a headless test's runtime. It never completes exceptionally: a face that cannot be read is logged and left out, and the family then resolves as it would have without this call.Asking for a family whose read is already under way shares that read, and a read once started is not withdrawn: what it produces joins the faces already resident, where it is worth the same to whoever names the family next. Italic faces are not read ahead, because regular and bold are what a UI draws on the frame after a switch. A backend keeps a bounded number of faces resident, so preload a family shortly before it is wanted, not at startup for every family a list could offer.
If the backend shuts down first, the stage does not complete: there is no UI thread left to complete it on.
- Parameters:
family- the family to read ahead, matched as aFontmatches it- Returns:
- a stage completed on the UI thread once the faces are resident
- Throws:
IllegalStateException- off the UI thread, or when noUiRuntimeis installed
-
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
- Returns:
- the family
Font.DEFAULT_FAMILYcurrently resolves to
-
setDefaultFamily
Sets the family the default UI font resolves to;null/blank restoresFont.DEFAULT_FAMILY. No-op if unchanged. Notifies listeners so the UI re-lays-out in the new font. UI thread.A switch that
setDefaultFamilyWhenResident(java.lang.String)is still waiting to make is abandoned by this call, whether or not the name changes: the later choice wins.This call itself reads nothing: it stores a name and runs the listeners. 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 faces are 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.
setDefaultFamilyWhenResident(java.lang.String)is the form that keeps that read out of the frame, and the one a font picker or a settings screen calls: it reads the faces on a worker and makes this same switch once they are resident. This form is for a switch that has to have happened when the call returns: before the first window opens, or in a test or a capture that draws next. Called from a menu or a settings dialog, its one long frame reads as the switch happening; never call it from an animation, a drag, or anything that must stay at frame rate. It costs once per face: switching back to a family already read is cheap, and so is switching to onepreload(java.lang.String)has made resident. -
setDefaultFamilyWhenResident
Sets the family the default UI font resolves to once its faces are resident: reads them on a worker aspreload(java.lang.String)does, then makes the switchsetDefaultFamily(java.lang.String)makes, on the UI thread, so the frame that shows the new font reads no file for its regular or bold text; an italic face, not read ahead, is still read where it is first drawn.null/blank restoresFont.DEFAULT_FAMILY. The call a font picker and a settings screen make.UI thread. Nothing changes inside this call: until the faces are resident, text stays in the family it was in. The returned stage completes on the UI thread, on a later turn and never inside this call, with
trueoncefamilyis the default family, which is right after the switch has notified listeners (they are not notified when it already was), or withfalsewhen a latersetDefaultFamilyorsetDefaultFamilyWhenResidentcame first and this one switched nothing.The later choice wins. A font picker moving through a list asks once per entry, and only the last request switches: an earlier one whose read lands afterwards leaves the default family where the later call put it, or is about to, and answers
false. The overtaken request is withdrawn from the backend, whose read cannot be called back once started, and what that read produces disturbs nothing: it lays nothing out again, and the backend keeps it only in room it can make without letting go of a face in use, so stepping back to that family is cheap while the backend has room for it, and costs another read off the UI thread when it had none. Asking again for the family a pending call names replaces that call too: the first stage answersfalseand the secondtrue, and the backend reads the faces once for both.A family the operating system does not have is switched to as
setDefaultFamilywould switch to it, and then resolves to the toolkit's embedded face. If the backend shuts down before the faces are resident, nothing switches and the stage does not complete.There is nothing to start: the name does not end in
Asyncbecause the toolkit keeps that suffix for an unstartedWork. This call has started when it returns and answers with a stage, as a dialog'sshowdoes, and what withdraws it is the next choice rather than a cancel.- Parameters:
family- the family to switch to, ornull/blank forFont.DEFAULT_FAMILY- Returns:
- a stage completed on the UI thread with whether this call's family became the default family
- Throws:
IllegalStateException- off the UI thread, or when noUiRuntimeis installed
-
observeChanges
Subscribes to catalog and default-family changes.- Parameters:
listener- what to run when either moves- Returns:
- a handle that unsubscribes; cancelling it twice is a no-op. UI thread
-