UI
ZwyLib bundles a handful of ready-made native UI helpers — popup menus, alert dialogs, text/selection input dialogs, progress bottom sheets, and pickers for chats/users — so plugins don’t have to build these from scratch on top of raw Java APIs. Dialog helpers that need a result are coroutines, built on top of zwylib.async_manager.
zwylib.UI
class UINamespace class grouping together simple, native UI widgets.
UI.PopupMenuBuilder
UI.PopupMenuBuilder(view: View)Builds and shows a context (popup) menu anchored to a given Android View.
Constructor Arguments
view(View): The view the popup menu should be anchored to.
Methods
add_item
PopupMenuBuilder.add_item(item: UI.PopupMenuItem) -> NoneAdds an item to the menu. Order of calls determines display order.
Parameters
item(UI.PopupMenuItem): The item to add.
Example
builder = zwylib.UI.PopupMenuBuilder(some_view)
builder.add_item(zwylib.UI.PopupMenuItem(
text="Delete",
on_click=lambda: print("deleted"),
icon="msg_delete",
is_red=True,
))
builder.show()show
PopupMenuBuilder.show() -> NoneDisplays the built popup menu anchored to the constructor’s view.
UI.PopupMenuItem
UI.PopupMenuItem(text: str, on_click: () -> None, icon: str, is_red: bool = False)Dataclass describing a single entry in a PopupMenuBuilder.
Arguments
text(str): Item label.on_click(() ->None): Called when the item is tapped.icon(str): Drawable resource name shown next to the item.is_red(bool, defaultFalse): Whether to render the item in the destructive/red style.
UI.AlertDialog
UI.AlertDialog(title: str, text: str, buttons: List[UI.AlertButton])A simple title + message alert dialog with up to three buttons (positive/negative/neutral).
Constructor Arguments
title(str): Dialog title.text(str): Dialog message body.buttons(List[UI.AlertButton]): Buttons to show.Noneentries are ignored, letting you conditionally omit a button.
Methods
show
AlertDialog.show() -> NoneDisplays the dialog. Does nothing (and logs a warning) if this instance is already showing.
Example
dialog = zwylib.UI.AlertDialog(
title="Delete plugin?",
text="This cannot be undone.",
buttons=[
zwylib.UI.AlertButton("Cancel", type=AlertDialogBuilder.BUTTON_NEGATIVE),
zwylib.UI.AlertButton("Delete", red=True, on_click=lambda b, w: do_delete()),
],
)
dialog.show()dismiss
AlertDialog.dismiss() -> NoneDismisses the dialog if it’s currently showing.
UI.AlertButton
UI.AlertButton(
text: str,
on_click: Optional[(AlertDialogBuilder, int) -> None] = None,
type: int = AlertDialogBuilder.BUTTON_POSITIVE,
red: bool = False
)Dataclass describing a single button in an AlertDialog.
Arguments
text(str): Button label.on_click(Optional[(AlertDialogBuilder, int) -> None], defaultNone): Called with the dialog builder and the clicked button’s index/id.type(int, defaultAlertDialogBuilder.BUTTON_POSITIVE): One ofAlertDialogBuilder.BUTTON_POSITIVE,BUTTON_NEGATIVE, orBUTTON_NEUTRAL, determining the button’s position.red(bool, defaultFalse): Whether to render the button text in the destructive/red style.
UI.StringInputDialog
class StringInputDialogA single-line/multi-line text input dialog.
Methods
show
StringInputDialog.show(title: str, initial_text="", hint="") -> Optional[str]Coroutine — shows a text input dialog and returns the entered text, or None if the dialog was dismissed without confirming.
Parameters
title(str): Dialog title.initial_text(str, default""): Text pre-filled into the input field.hint(str, default""): Placeholder text shown when the field is empty.
Example
async def ask_name():
name = await zwylib.UI.StringInputDialog.show("Your name", hint="John Doe")
if name is not None:
print(f"Got: {name}")UI.SelectorDialog
class SelectorDialogA single-choice selection dialog rendered as a list of radio buttons.
Methods
show
SelectorDialog.show(title: str, items: List[str], current_index: int = -1) -> Optional[int]Coroutine — shows a selection dialog and returns the index of the chosen item, or None if the dialog was dismissed without a selection.
Parameters
title(str): Dialog title.items(List[str]): Labels to show, in order.current_index(int, default-1): Index pre-selected when the dialog opens.-1means no item is pre-selected.
Example
async def ask_theme():
index = await zwylib.UI.SelectorDialog.show("Theme", ["Light", "Dark", "System"], current_index=2)
if index is not None:
print(f"Chosen: {index}")UI.BottomSheet
UI.BottomSheet(
title: str,
description: Optional[str],
sticker: str,
worker: (UI.BottomSheet) -> None,
on_finish: Optional[(UI.BottomSheet) -> None] = None,
on_dismiss: Optional[(UI.BottomSheet) -> None] = None
)A non-dismissible bottom sheet with a sticker, title, description, and progress bar — meant for showing progress of a longer-running background task (e.g. downloading/installing something).
Constructor Arguments
title(str): Sheet title.description(Optional[str]): Initial description text shown under the progress bar.sticker(str): Sticker reference in"pack_name/index"form, shown at the top of the sheet.worker((UI.BottomSheet) ->None): Function that performs the actual work. Called (viasafe_func_call) whenstart_workeris invoked, receiving the sheet instance itself so it can callupdate_status/finish. May be a coroutine function.on_finish(Optional[(UI.BottomSheet) -> None], defaultNone): Called after the sheet is dismissed viafinish.on_dismiss(Optional[(UI.BottomSheet) -> None], defaultNone): Called whenever the sheet is dismissed, by any means.
Methods
show
BottomSheet.show() -> NoneDisplays the sheet.
start_worker
BottomSheet.start_worker() -> NoneInvokes the worker function passed to the constructor, passing this sheet instance as its only argument.
Example
def do_work(sheet: "zwylib.UI.BottomSheet"):
sheet.update_status(0.5, "Halfway there...")
...
sheet.finish()
sheet = zwylib.UI.BottomSheet("Installing", "Starting...", "MyPack/1", do_work)
sheet.show()
sheet.start_worker()update_status
BottomSheet.update_status(progress: float, text: str) -> NoneUpdates the progress bar and description text.
Parameters
progress(float): Progress fraction from0.0to1.0(values above1.0are clamped).text(str): New description text.
finish
BottomSheet.finish() -> NoneDismisses the sheet and calls on_finish, if provided.
zwylib.DialogPicker
class DialogPickerPresents native chat/user picker screens and awaits the user’s selection.
Methods
pick_dialogs
DialogPicker.pick_dialogs(
pre_picked: Optional[List[int]] = None,
chats_only=False
) -> PickedDialogsCoroutine — opens the multi-select “share with” screen and returns what the user picked.
Parameters
pre_picked(Optional[List[int]], defaultNone): Peer IDs to pre-select when the screen opens.chats_only(bool, defaultFalse): IfTrue, filters the list down to chats/channels only, hiding individual users.
Example
async def choose():
picked = await zwylib.DialogPicker.pick_dialogs(chats_only=True)
print(picked.chats)pick_chat
DialogPicker.pick_chat(groups=True, channels=True, topics=False) -> TopicKeyCoroutine — opens the single-select dialogs screen and returns the chosen chat (and topic, if applicable). Raises an exception if the picker is cancelled.
Parameters
groups(bool, defaultTrue): Whether groups/megagroups are selectable.channels(bool, defaultTrue): Whether channels are selectable.topics(bool, defaultFalse): Whether forum topics can be selected in addition to the chat itself.
Example
async def choose():
result = await zwylib.DialogPicker.pick_chat(topics=True)
print(result.dialog_id, result.topic_id)zwylib.PickedDialogs
@dataclass
class PickedDialogs:
users: List[int]
chats: List[int]Result of DialogPicker.pick_dialogs — the peer IDs the user selected, split into users and chats.
zwylib.TopicKey
@dataclass
class TopicKey:
dialog_id: int
topic_id: intResult of DialogPicker.pick_chat — the picked chat’s ID and, if applicable, the picked topic’s ID (0 when no topic was selected).