Dialogs and modals¶
A modal is a coroutine you await. The question and what you do with the answer stay in one place:
from termopy.widgets import dialog
async def maybe_delete() -> None:
if await dialog.confirm(ui, "Delete bookmark?", name):
remove(name)
ui.spawn(maybe_delete())
While a dialog is open the app underneath still draws but receives no input at all, and focus is scoped, so dismissing the dialog puts the keyboard back where it was.
The built-in dialogs¶
await dialog.alert(ui, "Failed", "The server said no.")
await dialog.confirm(ui, "Stop app", "Stop blog?", yes="Stop") # -> bool
await dialog.choose(ui, "Pick", "Which one?", ["A", "B", "C"]) # -> str | None
await dialog.prompt(ui, "Name", "Call it what?", initial="") # -> str | None
confirm treats Esc as no; choose and prompt return None when
dismissed. Passing dangerous= to choose paints that option in the error colour.
Your own¶
ui.open_modal takes a render function and returns a future:
async def pick_colour(ui: UI) -> str | None:
def render(handle: UI, size: Size) -> View:
handle.focus("picker")
def keys(event: Event) -> bool:
if isinstance(event, KeyPress) and event.key is Key.ESCAPE:
handle.close_modal(None)
return True
return False
handle.on_event(keys, focused=True)
return border(body(handle), title=" Colour ").center(within=size)
chosen: str | None = await handle.open_modal(render)
return chosen
close_modal(value) resolves the future. Annotating the awaited result, as above, is
what tells the caller (and the type checker) what this particular dialog answers.
Why this and not a screen stack¶
Both applications ported to termopy needed overlays, and they needed different things.
hop3-tui has six top-level screens that
are places you navigate between, so it keeps a mode plus a stack in ui.state.
Prezo has six overlays that are questions (pick a slide, type a number, show the
keys) and awaits each one, with no stack at all.
Neither shape is general enough to belong in the library, so termopy has open_modal
and no Screen class. That conclusion needed the second application to
reach; one app would have produced an abstraction the other did not want.