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 eachrunAsync(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 exceptrunOnMainThread(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 TypeMethodDescriptionvoidback()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.voidclose()Closes every screen opened through this host, returning to the screen the menu was opened from.game()The running game's options, for menus that save or loadoptions.txt(e.g.booleanisBusy()Whether arunAsync(Callable, BiConsumer)task is running; the screen is inactive meanwhile.voidOpens a screen on top of the current one: a newMenuScreenor aConfirmDialog.voidrefresh()Re-reads all suppliers of the current screen (labels, values, enabled states, visibility, rows, tooltips…) and redraws it.<T> voidrunAsync(Callable<T> task, BiConsumer<? super T, ? super Throwable> onDone) Runstaskoff 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.voidrunOnMainThread(Runnable task) Runs a task on the game's main thread, soon; safe to call from any thread.
-
Method Details
-
open
Opens a screen on top of the current one: a newMenuScreenor aConfirmDialog. The new screen'sback()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
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; callrefresh()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
Runstaskoff 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 showsUiTexts.WORKING, its widgets are inactive and it ignores input, andisBusy()is true. ThenonDoneruns on the main thread with the task's result, or withnulland 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.taskmust not touch the game or the screen;onDonemay.- Type Parameters:
T- the task's result type (Voidwithreturn null;for none)- Parameters:
task- the background work; may throwonDone- receives(result, null)on success or(null, exception)on failure, on the main thread
-
isBusy
boolean isBusy()Whether arunAsync(Callable, BiConsumer)task is running; the screen is inactive meanwhile.- Returns:
truewhile a task started byrunAsync(Callable, BiConsumer)has not finished
-
game
GameBridge game()The running game's options, for menus that save or loadoptions.txt(e.g. a settings profile mod).- Returns:
- the game bridge; never
null
-