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 = 'Are you sure?', 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 ok_text is None: 35 ok_text = bui.Lstr(resource='okText') 36 if cancel_text is None: 37 cancel_text = bui.Lstr(resource='cancelText') 38 height += 40 39 width = max(width, 360) 40 self._action = action 41 42 # if they provided an origin-widget, scale up from that 43 self._transition_out: str | None 44 scale_origin: tuple[float, float] | None 45 if origin_widget is not None: 46 self._transition_out = 'out_scale' 47 scale_origin = origin_widget.get_screen_space_center() 48 transition = 'in_scale' 49 else: 50 self._transition_out = None 51 scale_origin = None 52 transition = 'in_right' 53 54 assert bui.app.classic is not None 55 uiscale = bui.app.ui_v1.uiscale 56 self.root_widget = bui.containerwidget( 57 size=(width, height), 58 transition=transition, 59 toolbar_visibility='menu_minimal_no_back', 60 parent=bui.get_special_widget('overlay_stack'), 61 scale=( 62 2.1 63 if uiscale is bui.UIScale.SMALL 64 else 1.5 65 if uiscale is bui.UIScale.MEDIUM 66 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 swish: bool = False, 157 back: 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._back = back 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 self._fade_and_quit, 191 origin_widget=origin_widget, 192 ).root_widget 193 194 def _fade_and_quit(self) -> None: 195 bui.fade_screen( 196 False, 197 time=0.2, 198 endcall=lambda: bui.quit(soft=True, back=self._back), 199 ) 200 201 # Prevent the user from doing anything else while we're on our 202 # way out. 203 bui.lock_all_input() 204 205 # On systems supporting soft-quit, unlock and fade back in shortly 206 # (soft-quit basically just backgrounds/hides the app). 207 if bui.app.env.supports_soft_quit: 208 # Unlock and fade back in shortly. Just in case something goes 209 # wrong (or on Android where quit just backs out of our activity 210 # and we may come back after). 211 def _come_back() -> None: 212 bui.unlock_all_input() 213 bui.fade_screen(True) 214 215 bui.apptimer(0.5, _come_back)
class
ConfirmWindow:
17class ConfirmWindow: 18 """Window for answering simple yes/no questions.""" 19 20 def __init__( 21 self, 22 text: str | bui.Lstr = 'Are you sure?', 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 ok_text is None: 36 ok_text = bui.Lstr(resource='okText') 37 if cancel_text is None: 38 cancel_text = bui.Lstr(resource='cancelText') 39 height += 40 40 width = max(width, 360) 41 self._action = action 42 43 # if they provided an origin-widget, scale up from that 44 self._transition_out: str | None 45 scale_origin: tuple[float, float] | None 46 if origin_widget is not None: 47 self._transition_out = 'out_scale' 48 scale_origin = origin_widget.get_screen_space_center() 49 transition = 'in_scale' 50 else: 51 self._transition_out = None 52 scale_origin = None 53 transition = 'in_right' 54 55 assert bui.app.classic is not None 56 uiscale = bui.app.ui_v1.uiscale 57 self.root_widget = bui.containerwidget( 58 size=(width, height), 59 transition=transition, 60 toolbar_visibility='menu_minimal_no_back', 61 parent=bui.get_special_widget('overlay_stack'), 62 scale=( 63 2.1 64 if uiscale is bui.UIScale.SMALL 65 else 1.5 66 if uiscale is bui.UIScale.MEDIUM 67 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._language.Lstr = 'Are you sure?', 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._language.Lstr | None = None, cancel_text: str | babase._language.Lstr | None = None, origin_widget: _bauiv1.Widget | None = None)
20 def __init__( 21 self, 22 text: str | bui.Lstr = 'Are you sure?', 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 ok_text is None: 36 ok_text = bui.Lstr(resource='okText') 37 if cancel_text is None: 38 cancel_text = bui.Lstr(resource='cancelText') 39 height += 40 40 width = max(width, 360) 41 self._action = action 42 43 # if they provided an origin-widget, scale up from that 44 self._transition_out: str | None 45 scale_origin: tuple[float, float] | None 46 if origin_widget is not None: 47 self._transition_out = 'out_scale' 48 scale_origin = origin_widget.get_screen_space_center() 49 transition = 'in_scale' 50 else: 51 self._transition_out = None 52 scale_origin = None 53 transition = 'in_right' 54 55 assert bui.app.classic is not None 56 uiscale = bui.app.ui_v1.uiscale 57 self.root_widget = bui.containerwidget( 58 size=(width, height), 59 transition=transition, 60 toolbar_visibility='menu_minimal_no_back', 61 parent=bui.get_special_widget('overlay_stack'), 62 scale=( 63 2.1 64 if uiscale is bui.UIScale.SMALL 65 else 1.5 66 if uiscale is bui.UIScale.MEDIUM 67 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 )
class
QuitWindow:
152class QuitWindow: 153 """Popup window to confirm quitting.""" 154 155 def __init__( 156 self, 157 swish: bool = False, 158 back: 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._back = back 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 self._fade_and_quit, 192 origin_widget=origin_widget, 193 ).root_widget 194 195 def _fade_and_quit(self) -> None: 196 bui.fade_screen( 197 False, 198 time=0.2, 199 endcall=lambda: bui.quit(soft=True, back=self._back), 200 ) 201 202 # Prevent the user from doing anything else while we're on our 203 # way out. 204 bui.lock_all_input() 205 206 # On systems supporting soft-quit, unlock and fade back in shortly 207 # (soft-quit basically just backgrounds/hides the app). 208 if bui.app.env.supports_soft_quit: 209 # Unlock and fade back in shortly. Just in case something goes 210 # wrong (or on Android where quit just backs out of our activity 211 # and we may come back after). 212 def _come_back() -> None: 213 bui.unlock_all_input() 214 bui.fade_screen(True) 215 216 bui.apptimer(0.5, _come_back)
Popup window to confirm quitting.
QuitWindow( swish: bool = False, back: bool = False, origin_widget: _bauiv1.Widget | None = None)
155 def __init__( 156 self, 157 swish: bool = False, 158 back: 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._back = back 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 self._fade_and_quit, 192 origin_widget=origin_widget, 193 ).root_widget