Interface UiHost


public interface UiHost
Shows a menu's screens and runs work for them. Each backend implements it; a mod receives one in ScreenFactory.create(UiHost) and keeps it for the screens' actions. (In unit tests, the API's test fixture FakeHost implements it.)

The screen stack

Screens form a stack. The menu's first screen sits at the bottom; open(Screen) pushes a screen on top, back() pops the current one, and close() pops them all. Popping the bottom screen returns to the screen the menu was opened from (e.g. the Options screen or the mod list).
Button.builder("details")
        .label(Text.translatable("mymod.details", "Details..."))
        .onPress(() -> host.open(detailsScreen(host)))     // its Done button calls host.back()
        .build();

Refreshing

The host re-reads the current screen's suppliers (labels, values, enabled states, rows, tooltips…) when a screen is shown, after every entry action, and after each runAsync(Callable, BiConsumer) callback, by itself. Call refresh() only when the mod's state changes some other way, e.g. in a runOnMainThread(Runnable) task.

Threads

Call every method except runOnMainThread(Runnable) on the game's main thread, which is where entry actions run anyway. Do blocking work (file or network IO) with runAsync(Callable, BiConsumer), never directly in an action, or the game freezes while it runs.

For a registered menu, the host the mod receives is wrapped by Failsafe: a failure of the backend in one of these calls turns the menus off, and exceptions from the mod's tasks and callbacks are logged with the mod's id.

  • Method Summary

    Modifier and Type
    Method
    Description
    void
    Closes the current screen and returns to the one below it, or, from the menu's first screen, to the screen the menu was opened from.
    void
    Closes every screen opened through this host, returning to the screen the menu was opened from.
    The running game's options, for menus that save or load options.txt (e.g.
    boolean
    Whether a runAsync(Callable, BiConsumer) task is running; the screen is inactive meanwhile.
    void
    open(Screen screen)
    Opens a screen on top of the current one: a new MenuScreen or a ConfirmDialog.
    void
    Re-reads all suppliers of the current screen (labels, values, enabled states, visibility, rows, tooltips…) and redraws it.
    <T> void
    runAsync(Callable<T> task, BiConsumer<? super T, ? super Throwable> onDone)
    Runs task off the main thread, for work that may block, such as file IO (which may wait on another game instance's file lock) or network requests.
    void
    Runs a task on the game's main thread, soon; safe to call from any thread.
  • Method Details

    • open

      void open(Screen screen)
      Opens a screen on top of the current one: a new MenuScreen or a ConfirmDialog. The new screen's back() returns to the current one. Build the screen fresh for each opening.
      Parameters:
      screen - the screen to show
    • back

      void back()
      Closes the current screen and returns to the one below it, or, from the menu's first screen, to the screen the menu was opened from. What a "Done" or "Cancel" button usually calls (.onPress(host::back)).
    • close

      void close()
      Closes every screen opened through this host, returning to the screen the menu was opened from.
    • refresh

      void refresh()
      Re-reads all suppliers of the current screen (labels, values, enabled states, visibility, rows, tooltips…) and redraws it. Happens by itself after entry actions; call it after changing state outside of one.
    • runOnMainThread

      void runOnMainThread(Runnable task)
      Runs a task on the game's main thread, soon; safe to call from any thread. Use it to get back to the main thread from the mod's own threads or callbacks. The screen is not refreshed afterwards; call refresh() in the task if it changed what the screen shows. If the task throws, the error is logged and the menu stays open.
      Parameters:
      task - the task
    • runAsync

      <T> void runAsync(Callable<T> task, BiConsumer<? super T, ? super Throwable> onDone)
      Runs task off the main thread, for work that may block, such as file IO (which may wait on another game instance's file lock) or network requests. While it runs, the current screen shows UiTexts.WORKING, its widgets are inactive and it ignores input, and isBusy() is true. Then onDone runs on the main thread with the task's result, or with null and the exception it threw, and the screen is refreshed.
      host.runAsync(() -> store.load(name), (profile, error) -> {
          if (error != null) {
              status.show(StatusLine.Kind.ERROR, Text.translatable("mymod.load.failed", "Couldn't load %s", name));
          } else {
              config.apply(profile);
              status.show(StatusLine.Kind.SUCCESS, Text.translatable("mymod.loaded", "Loaded %s", name));
          }
      });
      

      A registered menu's tasks run on a thread of their own (MenuWorkers), one at a time in the order they were started, so a task that hangs holds up only that mod's menu. task must not touch the game or the screen; onDone may.

      Type Parameters:
      T - the task's result type (Void with return null; for none)
      Parameters:
      task - the background work; may throw
      onDone - receives (result, null) on success or (null, exception) on failure, on the main thread
    • isBusy

      boolean isBusy()
      Whether a runAsync(Callable, BiConsumer) task is running; the screen is inactive meanwhile.
      Returns:
      true while a task started by runAsync(Callable, BiConsumer) has not finished
    • game

      GameBridge game()
      The running game's options, for menus that save or load options.txt (e.g. a settings profile mod).
      Returns:
      the game bridge; never null