bastd.ui.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 8 9import _ba 10import ba 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__(self, 20 text: str | ba.Lstr = 'Are you sure?', 21 action: Callable[[], Any] | None = None, 22 width: float = 360.0, 23 height: float = 100.0, 24 cancel_button: bool = True, 25 cancel_is_selected: bool = False, 26 color: tuple[float, float, float] = (1, 1, 1), 27 text_scale: float = 1.0, 28 ok_text: str | ba.Lstr | None = None, 29 cancel_text: str | ba.Lstr | None = None, 30 origin_widget: ba.Widget | None = None): 31 # pylint: disable=too-many-locals 32 if ok_text is None: 33 ok_text = ba.Lstr(resource='okText') 34 if cancel_text is None: 35 cancel_text = ba.Lstr(resource='cancelText') 36 height += 40 37 width = max(width, 360) 38 self._action = action 39 40 # if they provided an origin-widget, scale up from that 41 self._transition_out: str | None 42 scale_origin: tuple[float, float] | None 43 if origin_widget is not None: 44 self._transition_out = 'out_scale' 45 scale_origin = origin_widget.get_screen_space_center() 46 transition = 'in_scale' 47 else: 48 self._transition_out = None 49 scale_origin = None 50 transition = 'in_right' 51 52 uiscale = ba.app.ui.uiscale 53 self.root_widget = ba.containerwidget( 54 size=(width, height), 55 transition=transition, 56 toolbar_visibility='menu_minimal_no_back', 57 parent=_ba.get_special_widget('overlay_stack'), 58 scale=(2.1 if uiscale is ba.UIScale.SMALL else 59 1.5 if uiscale is ba.UIScale.MEDIUM else 1.0), 60 scale_origin_stack_offset=scale_origin) 61 62 ba.textwidget(parent=self.root_widget, 63 position=(width * 0.5, height - 5 - (height - 75) * 0.5), 64 size=(0, 0), 65 h_align='center', 66 v_align='center', 67 text=text, 68 scale=text_scale, 69 color=color, 70 maxwidth=width * 0.9, 71 max_height=height - 75) 72 73 cbtn: ba.Widget | None 74 if cancel_button: 75 cbtn = btn = ba.buttonwidget(parent=self.root_widget, 76 autoselect=True, 77 position=(20, 20), 78 size=(150, 50), 79 label=cancel_text, 80 on_activate_call=self._cancel) 81 ba.containerwidget(edit=self.root_widget, cancel_button=btn) 82 ok_button_h = width - 175 83 else: 84 # if they don't want a cancel button, we still want back presses to 85 # be able to dismiss the window; just wire it up to do the ok 86 # button 87 ok_button_h = width * 0.5 - 75 88 cbtn = None 89 btn = ba.buttonwidget(parent=self.root_widget, 90 autoselect=True, 91 position=(ok_button_h, 20), 92 size=(150, 50), 93 label=ok_text, 94 on_activate_call=self._ok) 95 96 # if they didn't want a cancel button, we still want to be able to hit 97 # cancel/back/etc to dismiss the window 98 if not cancel_button: 99 ba.containerwidget(edit=self.root_widget, 100 on_cancel_call=btn.activate) 101 102 ba.containerwidget(edit=self.root_widget, 103 selected_child=(cbtn if cbtn is not None 104 and cancel_is_selected else btn), 105 start_button=btn) 106 107 def _cancel(self) -> None: 108 ba.containerwidget( 109 edit=self.root_widget, 110 transition=('out_right' if self._transition_out is None else 111 self._transition_out)) 112 113 def _ok(self) -> None: 114 if not self.root_widget: 115 return 116 ba.containerwidget( 117 edit=self.root_widget, 118 transition=('out_left' if self._transition_out is None else 119 self._transition_out)) 120 if self._action is not None: 121 self._action() 122 123 124class QuitWindow: 125 """Popup window to confirm quitting.""" 126 127 def __init__(self, 128 swish: bool = False, 129 back: bool = False, 130 origin_widget: ba.Widget | None = None): 131 ui = ba.app.ui 132 app = ba.app 133 self._back = back 134 135 # If there's already one of us up somewhere, kill it. 136 if ui.quit_window is not None: 137 ui.quit_window.delete() 138 ui.quit_window = None 139 if swish: 140 ba.playsound(ba.getsound('swish')) 141 quit_resource = ('quitGameText' 142 if app.platform == 'mac' else 'exitGameText') 143 self._root_widget = ui.quit_window = (ConfirmWindow( 144 ba.Lstr(resource=quit_resource, 145 subs=[('${APP_NAME}', ba.Lstr(resource='titleText'))]), 146 self._fade_and_quit, 147 origin_widget=origin_widget).root_widget) 148 149 def _fade_and_quit(self) -> None: 150 _ba.fade_screen(False, 151 time=0.2, 152 endcall=lambda: ba.quit(soft=True, back=self._back)) 153 _ba.lock_all_input() 154 155 # Unlock and fade back in shortly.. just in case something goes wrong 156 # (or on android where quit just backs out of our activity and 157 # we may come back) 158 ba.timer(0.3, _ba.unlock_all_input, timetype=ba.TimeType.REAL)
class
ConfirmWindow:
17class ConfirmWindow: 18 """Window for answering simple yes/no questions.""" 19 20 def __init__(self, 21 text: str | ba.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 | ba.Lstr | None = None, 30 cancel_text: str | ba.Lstr | None = None, 31 origin_widget: ba.Widget | None = None): 32 # pylint: disable=too-many-locals 33 if ok_text is None: 34 ok_text = ba.Lstr(resource='okText') 35 if cancel_text is None: 36 cancel_text = ba.Lstr(resource='cancelText') 37 height += 40 38 width = max(width, 360) 39 self._action = action 40 41 # if they provided an origin-widget, scale up from that 42 self._transition_out: str | None 43 scale_origin: tuple[float, float] | None 44 if origin_widget is not None: 45 self._transition_out = 'out_scale' 46 scale_origin = origin_widget.get_screen_space_center() 47 transition = 'in_scale' 48 else: 49 self._transition_out = None 50 scale_origin = None 51 transition = 'in_right' 52 53 uiscale = ba.app.ui.uiscale 54 self.root_widget = ba.containerwidget( 55 size=(width, height), 56 transition=transition, 57 toolbar_visibility='menu_minimal_no_back', 58 parent=_ba.get_special_widget('overlay_stack'), 59 scale=(2.1 if uiscale is ba.UIScale.SMALL else 60 1.5 if uiscale is ba.UIScale.MEDIUM else 1.0), 61 scale_origin_stack_offset=scale_origin) 62 63 ba.textwidget(parent=self.root_widget, 64 position=(width * 0.5, height - 5 - (height - 75) * 0.5), 65 size=(0, 0), 66 h_align='center', 67 v_align='center', 68 text=text, 69 scale=text_scale, 70 color=color, 71 maxwidth=width * 0.9, 72 max_height=height - 75) 73 74 cbtn: ba.Widget | None 75 if cancel_button: 76 cbtn = btn = ba.buttonwidget(parent=self.root_widget, 77 autoselect=True, 78 position=(20, 20), 79 size=(150, 50), 80 label=cancel_text, 81 on_activate_call=self._cancel) 82 ba.containerwidget(edit=self.root_widget, cancel_button=btn) 83 ok_button_h = width - 175 84 else: 85 # if they don't want a cancel button, we still want back presses to 86 # be able to dismiss the window; just wire it up to do the ok 87 # button 88 ok_button_h = width * 0.5 - 75 89 cbtn = None 90 btn = ba.buttonwidget(parent=self.root_widget, 91 autoselect=True, 92 position=(ok_button_h, 20), 93 size=(150, 50), 94 label=ok_text, 95 on_activate_call=self._ok) 96 97 # if they didn't want a cancel button, we still want to be able to hit 98 # cancel/back/etc to dismiss the window 99 if not cancel_button: 100 ba.containerwidget(edit=self.root_widget, 101 on_cancel_call=btn.activate) 102 103 ba.containerwidget(edit=self.root_widget, 104 selected_child=(cbtn if cbtn is not None 105 and cancel_is_selected else btn), 106 start_button=btn) 107 108 def _cancel(self) -> None: 109 ba.containerwidget( 110 edit=self.root_widget, 111 transition=('out_right' if self._transition_out is None else 112 self._transition_out)) 113 114 def _ok(self) -> None: 115 if not self.root_widget: 116 return 117 ba.containerwidget( 118 edit=self.root_widget, 119 transition=('out_left' if self._transition_out is None else 120 self._transition_out)) 121 if self._action is not None: 122 self._action()
Window for answering simple yes/no questions.
ConfirmWindow( text: str | ba._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 | ba._language.Lstr | None = None, cancel_text: str | ba._language.Lstr | None = None, origin_widget: _ba.Widget | None = None)
20 def __init__(self, 21 text: str | ba.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 | ba.Lstr | None = None, 30 cancel_text: str | ba.Lstr | None = None, 31 origin_widget: ba.Widget | None = None): 32 # pylint: disable=too-many-locals 33 if ok_text is None: 34 ok_text = ba.Lstr(resource='okText') 35 if cancel_text is None: 36 cancel_text = ba.Lstr(resource='cancelText') 37 height += 40 38 width = max(width, 360) 39 self._action = action 40 41 # if they provided an origin-widget, scale up from that 42 self._transition_out: str | None 43 scale_origin: tuple[float, float] | None 44 if origin_widget is not None: 45 self._transition_out = 'out_scale' 46 scale_origin = origin_widget.get_screen_space_center() 47 transition = 'in_scale' 48 else: 49 self._transition_out = None 50 scale_origin = None 51 transition = 'in_right' 52 53 uiscale = ba.app.ui.uiscale 54 self.root_widget = ba.containerwidget( 55 size=(width, height), 56 transition=transition, 57 toolbar_visibility='menu_minimal_no_back', 58 parent=_ba.get_special_widget('overlay_stack'), 59 scale=(2.1 if uiscale is ba.UIScale.SMALL else 60 1.5 if uiscale is ba.UIScale.MEDIUM else 1.0), 61 scale_origin_stack_offset=scale_origin) 62 63 ba.textwidget(parent=self.root_widget, 64 position=(width * 0.5, height - 5 - (height - 75) * 0.5), 65 size=(0, 0), 66 h_align='center', 67 v_align='center', 68 text=text, 69 scale=text_scale, 70 color=color, 71 maxwidth=width * 0.9, 72 max_height=height - 75) 73 74 cbtn: ba.Widget | None 75 if cancel_button: 76 cbtn = btn = ba.buttonwidget(parent=self.root_widget, 77 autoselect=True, 78 position=(20, 20), 79 size=(150, 50), 80 label=cancel_text, 81 on_activate_call=self._cancel) 82 ba.containerwidget(edit=self.root_widget, cancel_button=btn) 83 ok_button_h = width - 175 84 else: 85 # if they don't want a cancel button, we still want back presses to 86 # be able to dismiss the window; just wire it up to do the ok 87 # button 88 ok_button_h = width * 0.5 - 75 89 cbtn = None 90 btn = ba.buttonwidget(parent=self.root_widget, 91 autoselect=True, 92 position=(ok_button_h, 20), 93 size=(150, 50), 94 label=ok_text, 95 on_activate_call=self._ok) 96 97 # if they didn't want a cancel button, we still want to be able to hit 98 # cancel/back/etc to dismiss the window 99 if not cancel_button: 100 ba.containerwidget(edit=self.root_widget, 101 on_cancel_call=btn.activate) 102 103 ba.containerwidget(edit=self.root_widget, 104 selected_child=(cbtn if cbtn is not None 105 and cancel_is_selected else btn), 106 start_button=btn)
class
QuitWindow:
125class QuitWindow: 126 """Popup window to confirm quitting.""" 127 128 def __init__(self, 129 swish: bool = False, 130 back: bool = False, 131 origin_widget: ba.Widget | None = None): 132 ui = ba.app.ui 133 app = ba.app 134 self._back = back 135 136 # If there's already one of us up somewhere, kill it. 137 if ui.quit_window is not None: 138 ui.quit_window.delete() 139 ui.quit_window = None 140 if swish: 141 ba.playsound(ba.getsound('swish')) 142 quit_resource = ('quitGameText' 143 if app.platform == 'mac' else 'exitGameText') 144 self._root_widget = ui.quit_window = (ConfirmWindow( 145 ba.Lstr(resource=quit_resource, 146 subs=[('${APP_NAME}', ba.Lstr(resource='titleText'))]), 147 self._fade_and_quit, 148 origin_widget=origin_widget).root_widget) 149 150 def _fade_and_quit(self) -> None: 151 _ba.fade_screen(False, 152 time=0.2, 153 endcall=lambda: ba.quit(soft=True, back=self._back)) 154 _ba.lock_all_input() 155 156 # Unlock and fade back in shortly.. just in case something goes wrong 157 # (or on android where quit just backs out of our activity and 158 # we may come back) 159 ba.timer(0.3, _ba.unlock_all_input, timetype=ba.TimeType.REAL)
Popup window to confirm quitting.
QuitWindow( swish: bool = False, back: bool = False, origin_widget: _ba.Widget | None = None)
128 def __init__(self, 129 swish: bool = False, 130 back: bool = False, 131 origin_widget: ba.Widget | None = None): 132 ui = ba.app.ui 133 app = ba.app 134 self._back = back 135 136 # If there's already one of us up somewhere, kill it. 137 if ui.quit_window is not None: 138 ui.quit_window.delete() 139 ui.quit_window = None 140 if swish: 141 ba.playsound(ba.getsound('swish')) 142 quit_resource = ('quitGameText' 143 if app.platform == 'mac' else 'exitGameText') 144 self._root_widget = ui.quit_window = (ConfirmWindow( 145 ba.Lstr(resource=quit_resource, 146 subs=[('${APP_NAME}', ba.Lstr(resource='titleText'))]), 147 self._fade_and_quit, 148 origin_widget=origin_widget).root_widget)