Class ThemeEditor

java.lang.Object
limn.scene.Widget
limn.themeeditor.ThemeEditor

public final class ThemeEditor extends Widget
The screen an application embeds so that its users can build a palette of their own: every tone of a Theme on a colour well, a preview that shows all of them at once, and a legibility report that names the unreadable pair before anyone ships it.

 ThemeEditor editor = new ThemeEditor(Theme.current());
 editor.onChange(theme -> settings.put("theme", ThemeFormat.write(theme)));
 

It applies what it edits, by default. The palette is process-wide, so the honest preview of a change is the application wearing it, including this editor, which re-skins under the user's hands. setApplyLive(boolean) turns that off for an application that cannot afford it; the ThemePreview beside the tones shows the edited palette either way, because it paints from the value rather than from Theme.current().

What it does not decide is where a palette lives. Copy and Paste move one through the clipboard as ThemeFormat text and need nothing from the platform; a file is an application's business, and ThemeEditorFiles is the optional half that opens a chooser for one.

UI thread only, like every widget here.

  • Constructor Details

    • ThemeEditor

      public ThemeEditor()
      An editor on the palette the application is currently wearing.
    • ThemeEditor

      public ThemeEditor(Theme theme)
      An editor on theme, which it neither owns nor mutates.
  • Method Details

    • onAttached

      protected void onAttached()
      Subscribes to the font catalog while this editor is on screen. Here rather than in the constructor because Fonts holds its listeners strongly: an editor that subscribed once and never unsubscribed would be kept alive by the toolkit for the life of the process, and would go on rebuilding a control in a tree nobody is showing.
      Overrides:
      onAttached in class Widget
    • onDetached

      protected void onDetached()
      Description copied from class: Widget
      Called when this widget leaves the scene (detached from the tree).

      Widget.scene() still answers the scene being left, and becomes null once this returns, so releasing something the scene owns needs no field of your own to remember it by. Fires bottom-up, so children run before their parent, and every one of them can still reach the scene.

      Release resources here, but GPU resources must be handed to Scene.disposeLater(limn.graphics.GpuSurface) (disposal needs the owning GL context, which is only current inside a frame). Not called on Widget.setVisible(boolean). Default no-op.

      Overrides:
      onDetached in class Widget
    • theme

      public Theme theme()
      The palette as edited. A fresh value each call; the editor holds a builder.
    • setTheme

      public ThemeEditor setTheme(Theme theme)
      Loads a palette in, replacing what is being edited and what revert() goes back to. Does not notify: the application is the source of this change.
    • setToken

      public ThemeEditor setToken(Theme.Token token, Color colour)
      Sets one tone, by exactly the path choosing it on its well takes: the well moves, the preview and the report follow, the palette goes live if it is meant to, and onChange is told. The entry point for an application that drives the editor from somewhere else: a preset menu, a colour sampled off an image, an undo stack.
    • setCornerScale

      public ThemeEditor setCornerScale(float value)
      Sets how round the corners are (see Theme.cornerScale) by the same path the slider takes, onChange included. The value is clamped to [0, Theme.MAX_CORNER_SCALE].
    • revert

      public ThemeEditor revert()
      Puts back the palette this editor opened on, or the last one given to setTheme(limn.components.Theme).
    • isModified

      public boolean isModified()
      Whether anything has changed since the editor opened or was last loaded.
    • onChange

      public ThemeEditor onChange(Consumer<Theme> listener)
      Called after every edit, with the palette as it now stands, including the one revert() restores. Not called by setTheme(limn.components.Theme). Keep it cheap: a colour well reports on every frame of a drag, and so does this.
    • setApplyLive

      public ThemeEditor setApplyLive(boolean value)
      Whether an edit is installed process-wide as it is made (default true).

      Turning it on installs what is being edited at once; turning it off leaves Theme.current() where it stands rather than putting anything back. detachLive() is what puts the application back, and nothing calls it for you: a widget cannot tell being removed from a scene apart from being moved to another one.

    • setPickerDisplayMode

      public ThemeEditor setPickerDisplayMode(DisplayMode mode)
      Asks for the colour pickers to open in a window of their own or as an overlay inside this editor's window; see DisplayMode. Default DisplayMode.NATIVE_WINDOW.

      Worth choosing here more than almost anywhere else, because of what this screen is: every tone the picker changes is being repainted live in the window behind it, and a native picker floats over that window rather than inside it. An application that records, streams or screenshots its own theming screen (the toolkit's own gallery does) sees the picker at all only in scene.

      Applies to every well, including the ones already built, so it can be called after construction. Pickers already open are not moved.

    • pickerDisplayMode

      public DisplayMode pickerDisplayMode()
      Returns:
      the presentation the wells will raise their next picker in.
    • isApplyLive

      public boolean isApplyLive()
      Whether edits are installed process-wide as they are made.
    • detachLive

      public void detachLive()
      Puts back the palette the application was wearing when this editor was built, what a "close without keeping this" button calls. Does nothing if the palette has not moved.
    • audit

      public List<ThemeAudit.Finding> audit()
      What is illegible in the palette as it stands, the same list the panel shows.
    • copyToClipboard

      public void copyToClipboard()
      Puts the palette on the clipboard as ThemeFormat text.
    • pasteFromClipboard

      public boolean pasteFromClipboard()
      Reads a palette off the clipboard, if what is there is one.
      Returns:
      whether it was. When it was not, the reason lands in the editor's own status line rather than in an exception: a paste is a guess by definition, and a wrong guess is not a failure the application has to handle
    • onMeasure

      protected Size onMeasure(Constraints constraints)
      Description copied from class: Widget
      Reports the size this widget wants within constraints. Called once per layout pass, and the result is cached against the constraints and the resolved axes (size step, layout direction, locale), so it must be a pure function of them and of this widget's own state.

      Resolve the ControlSize and the LayoutDirection once each here and thread them down; never read either in a constructor. The locale needs no threading: it is in scope, and I18n.locale() answers it wherever text is resolved.

      Specified by:
      onMeasure in class Widget
    • onLayout

      protected void onLayout()
      Description copied from class: Widget
      Containers position children here (measure + Widget.layoutBox(float, float, float, float) per child).
      Overrides:
      onLayout in class Widget