Class Ui

java.lang.Object
limn.concurrent.Ui

public final class Ui extends Object
Static facade over the toolkit's UiRuntime, Limn's first-class async API. The backend installs the runtime at startup; application code then uses:

 button.onAction(() ->
     Ui.async(() -> repository.load())          // worker pool
       .thenAccept(data -> label.setText(data)) // back on the UI thread
 );
 

Thread confinement is a hard rule: every widget mutation calls checkUiThread() and throws IllegalStateException when violated.

  • Method Details

    • install

      public static void install(UiRuntime candidate)
      Installs the process-wide runtime. Called by the backend once at startup.
      Throws:
      IllegalStateException - if a different runtime is already installed
    • uninstall

      public static void uninstall(UiRuntime candidate)
      Uninstalls candidate if it is the installed runtime (backend shutdown).
    • isInstalled

      public static boolean isInstalled()
      Returns:
      whether a runtime is installed (i.e. a Backend is running)
    • post

      public static void post(Runnable action)
      Runs action on the UI thread on the next frame. Any-thread safe.

      A task that mutates widget or scene state is responsible for invalidating what it touched: Widget.invalidate(), Widget.markNeedsLayout() or Scene.requestRender(). Running a task buys no frame of its own, so a mutation nothing invalidates stays unpainted until something else asks for one. Every widget setter and every tree change invalidates already; a task that writes a field behind a setter's back, or that changes what a custom onPaint reads, does not.

    • postDelayed

      public static void postDelayed(Runnable action, long delayMillis)
      Runs action on the UI thread after delayMillis. Any-thread safe.

      A task that mutates widget or scene state is responsible for invalidating what it touched: see post(java.lang.Runnable). Firing buys no frame of its own, which is what makes a timer the cheap way to watch something that rarely changes: a poll that re-reads a value and finds it unchanged costs a wake-up and nothing else, where a ticker asks for a frame every frame it stays registered.

    • async

      public static <T> CompletionStage<T> async(Supplier<T> work)
      Runs work on the worker pool; the returned stage completes on the UI thread (including its default async executor).

      A dependent that mutates widget or scene state is responsible for invalidating what it touched: see post(java.lang.Runnable). Completing on the UI thread buys no frame; label.setText(data) asks for the frame that shows it, a field written directly does not.

    • work

      public static <T> Work<T> work(Work.Body<T> body)
      Describes background work with a lifecycle: cancellable, able to report progress, and able to decline delivery when whoever asked has gone away. Nothing runs until Work.start():
      
       job = Ui.work(progress -> repository.search(term))
               .onSuccess(results::setItems)
               .deliverIf(results::isAttached)
               .start();
       

      Reach for this over async(java.util.function.Supplier<T>) whenever the request can be superseded: cancelling the previous job means the view shows the answer to the question last asked, rather than whichever answer happened to finish last.

    • isUiThread

      public static boolean isUiThread()
      Returns:
      true iff called on the UI thread (false when no runtime is installed)
    • checkUiThread

      public static void checkUiThread()
      Throws:
      IllegalStateException - if not on the UI thread (or no runtime installed)
    • executor

      public static Executor executor()
      Returns:
      an executor that posts to the UI thread