Source code for bauiv1lib.confirm

# Released under the MIT License. See LICENSE for details.
#
"""Provides ConfirmWindow base class and commonly used derivatives."""

from __future__ import annotations  # Docs-generation hack.

from typing import TYPE_CHECKING

from bacommon.app import AppPlatform

import bauiv1 as bui
from bauiv1 import _commonassets, _classicassets

if TYPE_CHECKING:
    from typing import Any, Callable


[docs] class ConfirmWindow: """Window for answering simple yes/no questions.""" def __init__( self, text: str | bui.Lstr | bui.LangStr | None = None, action: Callable[[], Any] | None = None, width: float = 360.0, height: float = 100.0, *, cancel_button: bool = True, cancel_is_selected: bool = False, color: tuple[float, float, float] = (1, 1, 1), text_scale: float = 1.0, ok_text: str | bui.Lstr | bui.LangStr | None = None, cancel_text: str | bui.Lstr | bui.LangStr | None = None, origin_widget: bui.Widget | None = None, permanent_ok_fade: bool = False, scale: float | None = None, ): """Show the window. ``scale`` overrides the window's default per-ui-scale scale. """ ui = bui.app.ui_v1 # Make sure our widgets have globally unique ids. self._id_prefix = ui.new_id_prefix('confirm') if text is None: text = _commonassets.strings.status.are_you_sure if ok_text is None: ok_text = _commonassets.strings.actions.ok if cancel_text is None: cancel_text = _commonassets.strings.actions.cancel height += 40 width = max(width, 360) self._action = action self._permanent_ok_fade = permanent_ok_fade # If they provided an origin-widget, scale up from that. self._transition_out: str | None scale_origin: tuple[float, float] | None if origin_widget is not None: self._transition_out = 'out_scale' scale_origin = origin_widget.get_screen_space_center() transition = 'in_scale' else: self._transition_out = None scale_origin = None transition = 'in_right' assert bui.app.classic is not None uiscale = bui.app.ui_v1.uiscale self.root_widget = bui.containerwidget( size=(width, height), transition=transition, toolbar_visibility='menu_minimal_no_back', parent=bui.get_special_widget('overlay_stack'), scale=( scale if scale is not None else ( 1.9 if uiscale is bui.UIScale.SMALL else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0 ) ), scale_origin_stack_offset=scale_origin, darken_behind=True, ) bui.textwidget( parent=self.root_widget, position=(width * 0.5, height - 5 - (height - 75) * 0.5), size=(0, 0), h_align='center', v_align='center', text=text, scale=text_scale, color=color, maxwidth=width * 0.9, max_height=height - 75, ) cbtn: bui.Widget | None if cancel_button: cbtn = btn = bui.buttonwidget( parent=self.root_widget, id=f'{self._id_prefix}|cancel', autoselect=True, position=(20, 20), size=(150, 50), label=cancel_text, on_activate_call=self._cancel, ) bui.containerwidget(edit=self.root_widget, cancel_button=btn) ok_button_h = width - 175 else: # if they don't want a cancel button, we still want back # presses to be able to dismiss the window; just wire it up # to do the ok button ok_button_h = width * 0.5 - 75 cbtn = None btn = bui.buttonwidget( parent=self.root_widget, id=f'{self._id_prefix}|ok', autoselect=True, position=(ok_button_h, 20), size=(150, 50), label=ok_text, on_activate_call=self._ok, ) # if they didn't want a cancel button, we still want to be able # to hit cancel/back/etc to dismiss the window if not cancel_button: bui.containerwidget( edit=self.root_widget, on_cancel_call=btn.activate ) bui.containerwidget( edit=self.root_widget, selected_child=( cbtn if cbtn is not None and cancel_is_selected else btn ), start_button=btn, ) def _cancel(self) -> None: bui.containerwidget( edit=self.root_widget, transition=( 'out_right' if self._transition_out is None else self._transition_out ), ) def _ok(self) -> None: if not self.root_widget: return bui.containerwidget( edit=self.root_widget, darken_behind_is_permanent=self._permanent_ok_fade, transition=( 'out_left' if self._transition_out is None else self._transition_out ), ) if self._action is not None: self._action()
[docs] class QuitWindow: """Popup window to confirm quitting.""" def __init__( self, quit_type: bui.QuitType | None = None, swish: bool = False, origin_widget: bui.Widget | None = None, ): ui = bui.app.ui_v1 platform = bui.app.env.platform self._quit_type = quit_type # If there's already one of us up somewhere, kill it. if ui.quit_window is not None: ui.quit_window.delete() ui.quit_window = None if swish: bui.play_swish() # Generally Macs say Quit, iPads say Close (quitting there closes # our window), and other stuff says Exit. strs = _classicassets.strings.ui if platform is AppPlatform.MACOS: confirmstr = strs.quit_app_confirm elif platform is AppPlatform.IOS: confirmstr = strs.close_app_confirm else: confirmstr = strs.exit_app_confirm quit_text = confirmstr(app_name=strs.app_name) self._root_widget = ui.quit_window = ConfirmWindow( quit_text, lambda: ( bui.quit(confirm=False, quit_type=self._quit_type) if self._quit_type is not None else bui.quit(confirm=False) ), origin_widget=origin_widget, # In situations where the quit action will *actually* kill # the process, tell the confirm to not fade back in when the # confirm button is pressed. It just looks a bit visually # odd if the confirm fades back in just before the app fades # out to quit. permanent_ok_fade=not bui.app.env.supports_soft_quit, ).root_widget
# Docs-generation hack; import some stuff that we likely only forward-declared # in our actual source code so that docs tools can find it. from typing import (Coroutine, Any, Literal, Callable, Generator, Awaitable, Sequence, Self, TypeIs) import asyncio from concurrent.futures import Future from pathlib import Path from enum import Enum