bauiv1lib.confirm

Provides ConfirmWindow base class and commonly used derivatives.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides ConfirmWindow base class and commonly used derivatives."""
  4
  5from __future__ import annotations
  6
  7from typing import TYPE_CHECKING
  8import logging
  9
 10import bauiv1 as bui
 11
 12if TYPE_CHECKING:
 13    from typing import Any, Callable
 14
 15
 16class ConfirmWindow:
 17    """Window for answering simple yes/no questions."""
 18
 19    def __init__(
 20        self,
 21        text: str | bui.Lstr | None = None,
 22        action: Callable[[], Any] | None = None,
 23        width: float = 360.0,
 24        height: float = 100.0,
 25        cancel_button: bool = True,
 26        cancel_is_selected: bool = False,
 27        color: tuple[float, float, float] = (1, 1, 1),
 28        text_scale: float = 1.0,
 29        ok_text: str | bui.Lstr | None = None,
 30        cancel_text: str | bui.Lstr | None = None,
 31        origin_widget: bui.Widget | None = None,
 32    ):
 33        # pylint: disable=too-many-locals
 34        if text is None:
 35            text = bui.Lstr(resource='areYouSureText')
 36        if ok_text is None:
 37            ok_text = bui.Lstr(resource='okText')
 38        if cancel_text is None:
 39            cancel_text = bui.Lstr(resource='cancelText')
 40        height += 40
 41        width = max(width, 360)
 42        self._action = action
 43
 44        # if they provided an origin-widget, scale up from that
 45        self._transition_out: str | None
 46        scale_origin: tuple[float, float] | None
 47        if origin_widget is not None:
 48            self._transition_out = 'out_scale'
 49            scale_origin = origin_widget.get_screen_space_center()
 50            transition = 'in_scale'
 51        else:
 52            self._transition_out = None
 53            scale_origin = None
 54            transition = 'in_right'
 55
 56        assert bui.app.classic is not None
 57        uiscale = bui.app.ui_v1.uiscale
 58        self.root_widget = bui.containerwidget(
 59            size=(width, height),
 60            transition=transition,
 61            toolbar_visibility='menu_minimal_no_back',
 62            parent=bui.get_special_widget('overlay_stack'),
 63            scale=(
 64                1.9
 65                if uiscale is bui.UIScale.SMALL
 66                else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0
 67            ),
 68            scale_origin_stack_offset=scale_origin,
 69        )
 70
 71        bui.textwidget(
 72            parent=self.root_widget,
 73            position=(width * 0.5, height - 5 - (height - 75) * 0.5),
 74            size=(0, 0),
 75            h_align='center',
 76            v_align='center',
 77            text=text,
 78            scale=text_scale,
 79            color=color,
 80            maxwidth=width * 0.9,
 81            max_height=height - 75,
 82        )
 83
 84        cbtn: bui.Widget | None
 85        if cancel_button:
 86            cbtn = btn = bui.buttonwidget(
 87                parent=self.root_widget,
 88                autoselect=True,
 89                position=(20, 20),
 90                size=(150, 50),
 91                label=cancel_text,
 92                on_activate_call=self._cancel,
 93            )
 94            bui.containerwidget(edit=self.root_widget, cancel_button=btn)
 95            ok_button_h = width - 175
 96        else:
 97            # if they don't want a cancel button, we still want back presses to
 98            # be able to dismiss the window; just wire it up to do the ok
 99            # button
100            ok_button_h = width * 0.5 - 75
101            cbtn = None
102        btn = bui.buttonwidget(
103            parent=self.root_widget,
104            autoselect=True,
105            position=(ok_button_h, 20),
106            size=(150, 50),
107            label=ok_text,
108            on_activate_call=self._ok,
109        )
110
111        # if they didn't want a cancel button, we still want to be able to hit
112        # cancel/back/etc to dismiss the window
113        if not cancel_button:
114            bui.containerwidget(
115                edit=self.root_widget, on_cancel_call=btn.activate
116            )
117
118        bui.containerwidget(
119            edit=self.root_widget,
120            selected_child=(
121                cbtn if cbtn is not None and cancel_is_selected else btn
122            ),
123            start_button=btn,
124        )
125
126    def _cancel(self) -> None:
127        bui.containerwidget(
128            edit=self.root_widget,
129            transition=(
130                'out_right'
131                if self._transition_out is None
132                else self._transition_out
133            ),
134        )
135
136    def _ok(self) -> None:
137        if not self.root_widget:
138            return
139        bui.containerwidget(
140            edit=self.root_widget,
141            transition=(
142                'out_left'
143                if self._transition_out is None
144                else self._transition_out
145            ),
146        )
147        if self._action is not None:
148            self._action()
149
150
151class QuitWindow:
152    """Popup window to confirm quitting."""
153
154    def __init__(
155        self,
156        quit_type: bui.QuitType | None = None,
157        swish: bool = False,
158        origin_widget: bui.Widget | None = None,
159    ):
160        classic = bui.app.classic
161        assert classic is not None
162        ui = bui.app.ui_v1
163        app = bui.app
164        self._quit_type = quit_type
165
166        # If there's already one of us up somewhere, kill it.
167        if ui.quit_window is not None:
168            ui.quit_window.delete()
169            ui.quit_window = None
170        if swish:
171            bui.getsound('swish').play()
172
173        if app.classic is None:
174            if bui.do_once():
175                logging.warning(
176                    'QuitWindow needs to be updated to work without classic.'
177                )
178            quit_resource = 'exitGameText'
179        else:
180            quit_resource = (
181                'quitGameText'
182                if app.classic.platform == 'mac'
183                else 'exitGameText'
184            )
185        self._root_widget = ui.quit_window = ConfirmWindow(
186            bui.Lstr(
187                resource=quit_resource,
188                subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))],
189            ),
190            lambda: (
191                bui.quit(confirm=False, quit_type=self._quit_type)
192                if self._quit_type is not None
193                else bui.quit(confirm=False)
194            ),
195            origin_widget=origin_widget,
196        ).root_widget
class ConfirmWindow:
 17class ConfirmWindow:
 18    """Window for answering simple yes/no questions."""
 19
 20    def __init__(
 21        self,
 22        text: str | bui.Lstr | None = None,
 23        action: Callable[[], Any] | None = None,
 24        width: float = 360.0,
 25        height: float = 100.0,
 26        cancel_button: bool = True,
 27        cancel_is_selected: bool = False,
 28        color: tuple[float, float, float] = (1, 1, 1),
 29        text_scale: float = 1.0,
 30        ok_text: str | bui.Lstr | None = None,
 31        cancel_text: str | bui.Lstr | None = None,
 32        origin_widget: bui.Widget | None = None,
 33    ):
 34        # pylint: disable=too-many-locals
 35        if text is None:
 36            text = bui.Lstr(resource='areYouSureText')
 37        if ok_text is None:
 38            ok_text = bui.Lstr(resource='okText')
 39        if cancel_text is None:
 40            cancel_text = bui.Lstr(resource='cancelText')
 41        height += 40
 42        width = max(width, 360)
 43        self._action = action
 44
 45        # if they provided an origin-widget, scale up from that
 46        self._transition_out: str | None
 47        scale_origin: tuple[float, float] | None
 48        if origin_widget is not None:
 49            self._transition_out = 'out_scale'
 50            scale_origin = origin_widget.get_screen_space_center()
 51            transition = 'in_scale'
 52        else:
 53            self._transition_out = None
 54            scale_origin = None
 55            transition = 'in_right'
 56
 57        assert bui.app.classic is not None
 58        uiscale = bui.app.ui_v1.uiscale
 59        self.root_widget = bui.containerwidget(
 60            size=(width, height),
 61            transition=transition,
 62            toolbar_visibility='menu_minimal_no_back',
 63            parent=bui.get_special_widget('overlay_stack'),
 64            scale=(
 65                1.9
 66                if uiscale is bui.UIScale.SMALL
 67                else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0
 68            ),
 69            scale_origin_stack_offset=scale_origin,
 70        )
 71
 72        bui.textwidget(
 73            parent=self.root_widget,
 74            position=(width * 0.5, height - 5 - (height - 75) * 0.5),
 75            size=(0, 0),
 76            h_align='center',
 77            v_align='center',
 78            text=text,
 79            scale=text_scale,
 80            color=color,
 81            maxwidth=width * 0.9,
 82            max_height=height - 75,
 83        )
 84
 85        cbtn: bui.Widget | None
 86        if cancel_button:
 87            cbtn = btn = bui.buttonwidget(
 88                parent=self.root_widget,
 89                autoselect=True,
 90                position=(20, 20),
 91                size=(150, 50),
 92                label=cancel_text,
 93                on_activate_call=self._cancel,
 94            )
 95            bui.containerwidget(edit=self.root_widget, cancel_button=btn)
 96            ok_button_h = width - 175
 97        else:
 98            # if they don't want a cancel button, we still want back presses to
 99            # be able to dismiss the window; just wire it up to do the ok
100            # button
101            ok_button_h = width * 0.5 - 75
102            cbtn = None
103        btn = bui.buttonwidget(
104            parent=self.root_widget,
105            autoselect=True,
106            position=(ok_button_h, 20),
107            size=(150, 50),
108            label=ok_text,
109            on_activate_call=self._ok,
110        )
111
112        # if they didn't want a cancel button, we still want to be able to hit
113        # cancel/back/etc to dismiss the window
114        if not cancel_button:
115            bui.containerwidget(
116                edit=self.root_widget, on_cancel_call=btn.activate
117            )
118
119        bui.containerwidget(
120            edit=self.root_widget,
121            selected_child=(
122                cbtn if cbtn is not None and cancel_is_selected else btn
123            ),
124            start_button=btn,
125        )
126
127    def _cancel(self) -> None:
128        bui.containerwidget(
129            edit=self.root_widget,
130            transition=(
131                'out_right'
132                if self._transition_out is None
133                else self._transition_out
134            ),
135        )
136
137    def _ok(self) -> None:
138        if not self.root_widget:
139            return
140        bui.containerwidget(
141            edit=self.root_widget,
142            transition=(
143                'out_left'
144                if self._transition_out is None
145                else self._transition_out
146            ),
147        )
148        if self._action is not None:
149            self._action()

Window for answering simple yes/no questions.

ConfirmWindow( text: str | babase.Lstr | None = None, action: Optional[Callable[[], Any]] = 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 | babase.Lstr | None = None, cancel_text: str | babase.Lstr | None = None, origin_widget: _bauiv1.Widget | None = None)
 20    def __init__(
 21        self,
 22        text: str | bui.Lstr | None = None,
 23        action: Callable[[], Any] | None = None,
 24        width: float = 360.0,
 25        height: float = 100.0,
 26        cancel_button: bool = True,
 27        cancel_is_selected: bool = False,
 28        color: tuple[float, float, float] = (1, 1, 1),
 29        text_scale: float = 1.0,
 30        ok_text: str | bui.Lstr | None = None,
 31        cancel_text: str | bui.Lstr | None = None,
 32        origin_widget: bui.Widget | None = None,
 33    ):
 34        # pylint: disable=too-many-locals
 35        if text is None:
 36            text = bui.Lstr(resource='areYouSureText')
 37        if ok_text is None:
 38            ok_text = bui.Lstr(resource='okText')
 39        if cancel_text is None:
 40            cancel_text = bui.Lstr(resource='cancelText')
 41        height += 40
 42        width = max(width, 360)
 43        self._action = action
 44
 45        # if they provided an origin-widget, scale up from that
 46        self._transition_out: str | None
 47        scale_origin: tuple[float, float] | None
 48        if origin_widget is not None:
 49            self._transition_out = 'out_scale'
 50            scale_origin = origin_widget.get_screen_space_center()
 51            transition = 'in_scale'
 52        else:
 53            self._transition_out = None
 54            scale_origin = None
 55            transition = 'in_right'
 56
 57        assert bui.app.classic is not None
 58        uiscale = bui.app.ui_v1.uiscale
 59        self.root_widget = bui.containerwidget(
 60            size=(width, height),
 61            transition=transition,
 62            toolbar_visibility='menu_minimal_no_back',
 63            parent=bui.get_special_widget('overlay_stack'),
 64            scale=(
 65                1.9
 66                if uiscale is bui.UIScale.SMALL
 67                else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0
 68            ),
 69            scale_origin_stack_offset=scale_origin,
 70        )
 71
 72        bui.textwidget(
 73            parent=self.root_widget,
 74            position=(width * 0.5, height - 5 - (height - 75) * 0.5),
 75            size=(0, 0),
 76            h_align='center',
 77            v_align='center',
 78            text=text,
 79            scale=text_scale,
 80            color=color,
 81            maxwidth=width * 0.9,
 82            max_height=height - 75,
 83        )
 84
 85        cbtn: bui.Widget | None
 86        if cancel_button:
 87            cbtn = btn = bui.buttonwidget(
 88                parent=self.root_widget,
 89                autoselect=True,
 90                position=(20, 20),
 91                size=(150, 50),
 92                label=cancel_text,
 93                on_activate_call=self._cancel,
 94            )
 95            bui.containerwidget(edit=self.root_widget, cancel_button=btn)
 96            ok_button_h = width - 175
 97        else:
 98            # if they don't want a cancel button, we still want back presses to
 99            # be able to dismiss the window; just wire it up to do the ok
100            # button
101            ok_button_h = width * 0.5 - 75
102            cbtn = None
103        btn = bui.buttonwidget(
104            parent=self.root_widget,
105            autoselect=True,
106            position=(ok_button_h, 20),
107            size=(150, 50),
108            label=ok_text,
109            on_activate_call=self._ok,
110        )
111
112        # if they didn't want a cancel button, we still want to be able to hit
113        # cancel/back/etc to dismiss the window
114        if not cancel_button:
115            bui.containerwidget(
116                edit=self.root_widget, on_cancel_call=btn.activate
117            )
118
119        bui.containerwidget(
120            edit=self.root_widget,
121            selected_child=(
122                cbtn if cbtn is not None and cancel_is_selected else btn
123            ),
124            start_button=btn,
125        )
root_widget
class QuitWindow:
152class QuitWindow:
153    """Popup window to confirm quitting."""
154
155    def __init__(
156        self,
157        quit_type: bui.QuitType | None = None,
158        swish: bool = False,
159        origin_widget: bui.Widget | None = None,
160    ):
161        classic = bui.app.classic
162        assert classic is not None
163        ui = bui.app.ui_v1
164        app = bui.app
165        self._quit_type = quit_type
166
167        # If there's already one of us up somewhere, kill it.
168        if ui.quit_window is not None:
169            ui.quit_window.delete()
170            ui.quit_window = None
171        if swish:
172            bui.getsound('swish').play()
173
174        if app.classic is None:
175            if bui.do_once():
176                logging.warning(
177                    'QuitWindow needs to be updated to work without classic.'
178                )
179            quit_resource = 'exitGameText'
180        else:
181            quit_resource = (
182                'quitGameText'
183                if app.classic.platform == 'mac'
184                else 'exitGameText'
185            )
186        self._root_widget = ui.quit_window = ConfirmWindow(
187            bui.Lstr(
188                resource=quit_resource,
189                subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))],
190            ),
191            lambda: (
192                bui.quit(confirm=False, quit_type=self._quit_type)
193                if self._quit_type is not None
194                else bui.quit(confirm=False)
195            ),
196            origin_widget=origin_widget,
197        ).root_widget

Popup window to confirm quitting.

QuitWindow( quit_type: babase.QuitType | None = None, swish: bool = False, origin_widget: _bauiv1.Widget | None = None)
155    def __init__(
156        self,
157        quit_type: bui.QuitType | None = None,
158        swish: bool = False,
159        origin_widget: bui.Widget | None = None,
160    ):
161        classic = bui.app.classic
162        assert classic is not None
163        ui = bui.app.ui_v1
164        app = bui.app
165        self._quit_type = quit_type
166
167        # If there's already one of us up somewhere, kill it.
168        if ui.quit_window is not None:
169            ui.quit_window.delete()
170            ui.quit_window = None
171        if swish:
172            bui.getsound('swish').play()
173
174        if app.classic is None:
175            if bui.do_once():
176                logging.warning(
177                    'QuitWindow needs to be updated to work without classic.'
178                )
179            quit_resource = 'exitGameText'
180        else:
181            quit_resource = (
182                'quitGameText'
183                if app.classic.platform == 'mac'
184                else 'exitGameText'
185            )
186        self._root_widget = ui.quit_window = ConfirmWindow(
187            bui.Lstr(
188                resource=quit_resource,
189                subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))],
190            ),
191            lambda: (
192                bui.quit(confirm=False, quit_type=self._quit_type)
193                if self._quit_type is not None
194                else bui.quit(confirm=False)
195            ),
196            origin_widget=origin_widget,
197        ).root_widget