# Released under the MIT License. See LICENSE for details.
#
"""Provides ConfirmWindow base class and commonly used derivatives."""
from __future__ import annotations
from typing import TYPE_CHECKING
import logging
import bauiv1 as bui
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 | 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 | None = None,
cancel_text: str | bui.Lstr | None = None,
origin_widget: bui.Widget | None = None,
):
# pylint: disable=too-many-locals
if text is None:
text = bui.Lstr(resource='areYouSureText')
if ok_text is None:
ok_text = bui.Lstr(resource='okText')
if cancel_text is None:
cancel_text = bui.Lstr(resource='cancelText')
height += 40
width = max(width, 360)
self._action = action
# 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=(
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,
)
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,
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,
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,
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,
):
classic = bui.app.classic
assert classic is not None
ui = bui.app.ui_v1
app = bui.app
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.getsound('swish').play()
if app.classic is None:
if bui.do_once():
logging.warning(
'QuitWindow needs to be updated to work without classic.'
)
quit_resource = 'exitGameText'
else:
quit_resource = (
'quitGameText'
if app.classic.platform == 'mac'
else 'exitGameText'
)
self._root_widget = ui.quit_window = ConfirmWindow(
bui.Lstr(
resource=quit_resource,
subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))],
),
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,
).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)
import asyncio
from concurrent.futures import Future
from pathlib import Path
from enum import Enum