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

Popup window to confirm quitting.

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