bauiv1

Ballistica user interface api version 1

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Ballistica user interface api version 1"""
  4
  5# ba_meta require api 9
  6
  7# The stuff we expose here at the top level is our 'public' api.
  8# It should only be imported by code outside of this package or
  9# from 'if TYPE_CHECKING' blocks (which will not exec at runtime).
 10# Code within our package should import things directly from their
 11# submodules.
 12
 13from __future__ import annotations
 14
 15# pylint: disable=redefined-builtin
 16
 17import logging
 18
 19from efro.util import set_canonical_module_names
 20from babase import (
 21    add_clean_frame_callback,
 22    allows_ticket_sales,
 23    app,
 24    AppIntent,
 25    AppIntentDefault,
 26    AppIntentExec,
 27    AppMode,
 28    appname,
 29    appnameupper,
 30    apptime,
 31    AppTime,
 32    apptimer,
 33    AppTimer,
 34    Call,
 35    fullscreen_control_available,
 36    fullscreen_control_get,
 37    fullscreen_control_key_shortcut,
 38    fullscreen_control_set,
 39    charstr,
 40    clipboard_is_supported,
 41    clipboard_set_text,
 42    commit_app_config,
 43    ContextRef,
 44    displaytime,
 45    DisplayTime,
 46    displaytimer,
 47    DisplayTimer,
 48    do_once,
 49    fade_screen,
 50    get_display_resolution,
 51    get_input_idle_time,
 52    get_ip_address_type,
 53    get_low_level_config_value,
 54    get_max_graphics_quality,
 55    get_remote_app_name,
 56    get_replays_dir,
 57    get_string_height,
 58    get_string_width,
 59    get_type_name,
 60    getclass,
 61    have_permission,
 62    in_logic_thread,
 63    in_main_menu,
 64    increment_analytics_count,
 65    is_browser_likely_available,
 66    is_xcode_build,
 67    lock_all_input,
 68    LoginAdapter,
 69    LoginInfo,
 70    Lstr,
 71    native_review_request,
 72    native_review_request_supported,
 73    NotFoundError,
 74    open_file_externally,
 75    open_url,
 76    overlay_web_browser_close,
 77    overlay_web_browser_is_open,
 78    overlay_web_browser_is_supported,
 79    overlay_web_browser_open_url,
 80    Permission,
 81    Plugin,
 82    PluginSpec,
 83    pushcall,
 84    quit,
 85    QuitType,
 86    request_permission,
 87    safecolor,
 88    screenmessage,
 89    set_analytics_screen,
 90    set_low_level_config_value,
 91    set_ui_input_device,
 92    SpecialChar,
 93    supports_max_fps,
 94    supports_vsync,
 95    timestring,
 96    UIScale,
 97    unlock_all_input,
 98    WeakCall,
 99    workspaces_in_use,
100)
101
102from _bauiv1 import (
103    buttonwidget,
104    checkboxwidget,
105    columnwidget,
106    containerwidget,
107    get_qrcode_texture,
108    get_special_widget,
109    getmesh,
110    getsound,
111    gettexture,
112    hscrollwidget,
113    imagewidget,
114    Mesh,
115    rowwidget,
116    scrollwidget,
117    set_party_window_open,
118    Sound,
119    Texture,
120    textwidget,
121    uibounds,
122    Widget,
123    widget,
124)
125from bauiv1._keyboard import Keyboard
126from bauiv1._uitypes import (
127    Window,
128    MainWindowState,
129    BasicMainWindowState,
130    uicleanupcheck,
131    MainWindow,
132)
133from bauiv1._appsubsystem import UIV1AppSubsystem
134
135__all__ = [
136    'add_clean_frame_callback',
137    'allows_ticket_sales',
138    'app',
139    'AppIntent',
140    'AppIntentDefault',
141    'AppIntentExec',
142    'AppMode',
143    'appname',
144    'appnameupper',
145    'appnameupper',
146    'apptime',
147    'AppTime',
148    'apptimer',
149    'AppTimer',
150    'BasicMainWindowState',
151    'buttonwidget',
152    'Call',
153    'fullscreen_control_available',
154    'fullscreen_control_get',
155    'fullscreen_control_key_shortcut',
156    'fullscreen_control_set',
157    'charstr',
158    'checkboxwidget',
159    'clipboard_is_supported',
160    'clipboard_set_text',
161    'columnwidget',
162    'commit_app_config',
163    'containerwidget',
164    'ContextRef',
165    'displaytime',
166    'DisplayTime',
167    'displaytimer',
168    'DisplayTimer',
169    'do_once',
170    'fade_screen',
171    'get_display_resolution',
172    'get_input_idle_time',
173    'get_ip_address_type',
174    'get_low_level_config_value',
175    'get_max_graphics_quality',
176    'get_qrcode_texture',
177    'get_remote_app_name',
178    'get_replays_dir',
179    'get_special_widget',
180    'get_string_height',
181    'get_string_width',
182    'get_type_name',
183    'getclass',
184    'getmesh',
185    'getsound',
186    'gettexture',
187    'have_permission',
188    'hscrollwidget',
189    'imagewidget',
190    'in_logic_thread',
191    'in_main_menu',
192    'increment_analytics_count',
193    'is_browser_likely_available',
194    'is_xcode_build',
195    'Keyboard',
196    'lock_all_input',
197    'LoginAdapter',
198    'LoginInfo',
199    'Lstr',
200    'MainWindow',
201    'MainWindowState',
202    'Mesh',
203    'native_review_request',
204    'native_review_request_supported',
205    'NotFoundError',
206    'open_file_externally',
207    'open_url',
208    'overlay_web_browser_close',
209    'overlay_web_browser_is_open',
210    'overlay_web_browser_is_supported',
211    'overlay_web_browser_open_url',
212    'Permission',
213    'Plugin',
214    'PluginSpec',
215    'pushcall',
216    'quit',
217    'QuitType',
218    'request_permission',
219    'rowwidget',
220    'safecolor',
221    'screenmessage',
222    'scrollwidget',
223    'set_analytics_screen',
224    'set_low_level_config_value',
225    'set_party_window_open',
226    'set_ui_input_device',
227    'Sound',
228    'SpecialChar',
229    'supports_max_fps',
230    'supports_vsync',
231    'Texture',
232    'textwidget',
233    'timestring',
234    'uibounds',
235    'uicleanupcheck',
236    'UIScale',
237    'UIV1AppSubsystem',
238    'unlock_all_input',
239    'WeakCall',
240    'widget',
241    'Widget',
242    'Window',
243    'workspaces_in_use',
244]
245
246# We want stuff to show up as bauiv1.Foo instead of bauiv1._sub.Foo.
247set_canonical_module_names(globals())
248
249# Sanity check: we want to keep ballistica's dependencies and
250# bootstrapping order clearly defined; let's check a few particular
251# modules to make sure they never directly or indirectly import us
252# before their own execs complete.
253if __debug__:
254    for _mdl in 'babase', '_babase':
255        if not hasattr(__import__(_mdl), '_REACHED_END_OF_MODULE'):
256            logging.warning(
257                '%s was imported before %s finished importing;'
258                ' should not happen.',
259                __name__,
260                _mdl,
261            )
app = <babase.App object>
class AppIntent:
13class AppIntent:
14    """A high level directive given to the app.
15
16    Category: **App Classes**
17    """

A high level directive given to the app.

Category: App Classes

class AppIntentDefault(bauiv1.AppIntent):
20class AppIntentDefault(AppIntent):
21    """Tells the app to simply run in its default mode."""

Tells the app to simply run in its default mode.

class AppIntentExec(bauiv1.AppIntent):
24class AppIntentExec(AppIntent):
25    """Tells the app to exec some Python code."""
26
27    def __init__(self, code: str):
28        self.code = code

Tells the app to exec some Python code.

AppIntentExec(code: str)
27    def __init__(self, code: str):
28        self.code = code
code
class AppMode:
14class AppMode:
15    """A high level mode for the app.
16
17    Category: **App Classes**
18
19    """
20
21    @classmethod
22    def get_app_experience(cls) -> AppExperience:
23        """Return the overall experience provided by this mode."""
24        raise NotImplementedError('AppMode subclasses must override this.')
25
26    @classmethod
27    def can_handle_intent(cls, intent: AppIntent) -> bool:
28        """Return whether this mode can handle the provided intent.
29
30        For this to return True, the AppMode must claim to support the
31        provided intent (via its _supports_intent() method) AND the
32        AppExperience associated with the AppMode must be supported by
33        the current app and runtime environment.
34        """
35        # TODO: check AppExperience.
36        return cls._supports_intent(intent)
37
38    @classmethod
39    def _supports_intent(cls, intent: AppIntent) -> bool:
40        """Return whether our mode can handle the provided intent.
41
42        AppModes should override this to define what they can handle.
43        Note that AppExperience does not have to be considered here; that
44        is handled automatically by the can_handle_intent() call."""
45        raise NotImplementedError('AppMode subclasses must override this.')
46
47    def handle_intent(self, intent: AppIntent) -> None:
48        """Handle an intent."""
49        raise NotImplementedError('AppMode subclasses must override this.')
50
51    def on_activate(self) -> None:
52        """Called when the mode is being activated."""
53
54    def on_deactivate(self) -> None:
55        """Called when the mode is being deactivated."""
56
57    def on_app_active_changed(self) -> None:
58        """Called when ba*.app.active changes while this mode is active.
59
60        The app-mode may want to take action such as pausing a running
61        game in such cases.
62        """

A high level mode for the app.

Category: App Classes

@classmethod
def get_app_experience(cls) -> bacommon.app.AppExperience:
21    @classmethod
22    def get_app_experience(cls) -> AppExperience:
23        """Return the overall experience provided by this mode."""
24        raise NotImplementedError('AppMode subclasses must override this.')

Return the overall experience provided by this mode.

@classmethod
def can_handle_intent(cls, intent: AppIntent) -> bool:
26    @classmethod
27    def can_handle_intent(cls, intent: AppIntent) -> bool:
28        """Return whether this mode can handle the provided intent.
29
30        For this to return True, the AppMode must claim to support the
31        provided intent (via its _supports_intent() method) AND the
32        AppExperience associated with the AppMode must be supported by
33        the current app and runtime environment.
34        """
35        # TODO: check AppExperience.
36        return cls._supports_intent(intent)

Return whether this mode can handle the provided intent.

For this to return True, the AppMode must claim to support the provided intent (via its _supports_intent() method) AND the AppExperience associated with the AppMode must be supported by the current app and runtime environment.

def handle_intent(self, intent: AppIntent) -> None:
47    def handle_intent(self, intent: AppIntent) -> None:
48        """Handle an intent."""
49        raise NotImplementedError('AppMode subclasses must override this.')

Handle an intent.

def on_activate(self) -> None:
51    def on_activate(self) -> None:
52        """Called when the mode is being activated."""

Called when the mode is being activated.

def on_deactivate(self) -> None:
54    def on_deactivate(self) -> None:
55        """Called when the mode is being deactivated."""

Called when the mode is being deactivated.

def on_app_active_changed(self) -> None:
57    def on_app_active_changed(self) -> None:
58        """Called when ba*.app.active changes while this mode is active.
59
60        The app-mode may want to take action such as pausing a running
61        game in such cases.
62        """

Called when ba*.app.active changes while this mode is active.

The app-mode may want to take action such as pausing a running game in such cases.

def apptime() -> AppTime:
552def apptime() -> babase.AppTime:
553    """Return the current app-time in seconds.
554
555    Category: **General Utility Functions**
556
557    App-time is a monotonic time value; it starts at 0.0 when the app
558    launches and will never jump by large amounts or go backwards, even if
559    the system time changes. Its progression will pause when the app is in
560    a suspended state.
561
562    Note that the AppTime returned here is simply float; it just has a
563    unique type in the type-checker's eyes to help prevent it from being
564    accidentally used with time functionality expecting other time types.
565    """
566    import babase  # pylint: disable=cyclic-import
567
568    return babase.AppTime(0.0)

Return the current app-time in seconds.

Category: General Utility Functions

App-time is a monotonic time value; it starts at 0.0 when the app launches and will never jump by large amounts or go backwards, even if the system time changes. Its progression will pause when the app is in a suspended state.

Note that the AppTime returned here is simply float; it just has a unique type in the type-checker's eyes to help prevent it from being accidentally used with time functionality expecting other time types.

AppTime = AppTime
def apptimer(time: float, call: Callable[[], Any]) -> None:
571def apptimer(time: float, call: Callable[[], Any]) -> None:
572    """Schedule a callable object to run based on app-time.
573
574    Category: **General Utility Functions**
575
576    This function creates a one-off timer which cannot be canceled or
577    modified once created. If you require the ability to do so, or need
578    a repeating timer, use the babase.AppTimer class instead.
579
580    ##### Arguments
581    ###### time (float)
582    > Length of time in seconds that the timer will wait before firing.
583
584    ###### call (Callable[[], Any])
585    > A callable Python object. Note that the timer will retain a
586    strong reference to the callable for as long as the timer exists, so you
587    may want to look into concepts such as babase.WeakCall if that is not
588    desired.
589
590    ##### Examples
591    Print some stuff through time:
592    >>> babase.screenmessage('hello from now!')
593    >>> babase.apptimer(1.0, babase.Call(babase.screenmessage,
594                              'hello from the future!'))
595    >>> babase.apptimer(2.0, babase.Call(babase.screenmessage,
596    ...                       'hello from the future 2!'))
597    """
598    return None

Schedule a callable object to run based on app-time.

Category: General Utility Functions

This function creates a one-off timer which cannot be canceled or modified once created. If you require the ability to do so, or need a repeating timer, use the babase.AppTimer class instead.

Arguments
time (float)

Length of time in seconds that the timer will wait before firing.

call (Callable[[], Any])

A callable Python object. Note that the timer will retain a strong reference to the callable for as long as the timer exists, so you may want to look into concepts such as babase.WeakCall if that is not desired.

Examples

Print some stuff through time:

>>> babase.screenmessage('hello from now!')
>>> babase.apptimer(1.0, babase.Call(babase.screenmessage,
                          'hello from the future!'))
>>> babase.apptimer(2.0, babase.Call(babase.screenmessage,
...                       'hello from the future 2!'))
class AppTimer:
53class AppTimer:
54    """Timers are used to run code at later points in time.
55
56    Category: **General Utility Classes**
57
58    This class encapsulates a timer based on app-time.
59    The underlying timer will be destroyed when this object is no longer
60    referenced. If you do not want to worry about keeping a reference to
61    your timer around, use the babase.apptimer() function instead to get a
62    one-off timer.
63
64    ##### Arguments
65    ###### time
66    > Length of time in seconds that the timer will wait before firing.
67
68    ###### call
69    > A callable Python object. Remember that the timer will retain a
70    strong reference to the callable for as long as it exists, so you
71    may want to look into concepts such as babase.WeakCall if that is not
72    desired.
73
74    ###### repeat
75    > If True, the timer will fire repeatedly, with each successive
76    firing having the same delay as the first.
77
78    ##### Example
79
80    Use a Timer object to print repeatedly for a few seconds:
81    ... def say_it():
82    ...     babase.screenmessage('BADGER!')
83    ... def stop_saying_it():
84    ...     global g_timer
85    ...     g_timer = None
86    ...     babase.screenmessage('MUSHROOM MUSHROOM!')
87    ... # Create our timer; it will run as long as we have the self.t ref.
88    ... g_timer = babase.AppTimer(0.3, say_it, repeat=True)
89    ... # Now fire off a one-shot timer to kill it.
90    ... babase.apptimer(3.89, stop_saying_it)
91    """
92
93    def __init__(
94        self, time: float, call: Callable[[], Any], repeat: bool = False
95    ) -> None:
96        pass

Timers are used to run code at later points in time.

Category: General Utility Classes

This class encapsulates a timer based on app-time. The underlying timer will be destroyed when this object is no longer referenced. If you do not want to worry about keeping a reference to your timer around, use the babase.apptimer() function instead to get a one-off timer.

Arguments
time

Length of time in seconds that the timer will wait before firing.

call

A callable Python object. Remember that the timer will retain a strong reference to the callable for as long as it exists, so you may want to look into concepts such as babase.WeakCall if that is not desired.

repeat

If True, the timer will fire repeatedly, with each successive firing having the same delay as the first.

Example

Use a Timer object to print repeatedly for a few seconds: ... def say_it(): ... babase.screenmessage('BADGER!') ... def stop_saying_it(): ... global g_timer ... g_timer = None ... babase.screenmessage('MUSHROOM MUSHROOM!') ... # Create our timer; it will run as long as we have the self.t ref. ... g_timer = babase.AppTimer(0.3, say_it, repeat=True) ... # Now fire off a one-shot timer to kill it. ... babase.apptimer(3.89, stop_saying_it)

AppTimer(time: float, call: Callable[[], Any], repeat: bool = False)
93    def __init__(
94        self, time: float, call: Callable[[], Any], repeat: bool = False
95    ) -> None:
96        pass
class BasicMainWindowState(bauiv1.MainWindowState):
233class BasicMainWindowState(MainWindowState):
234    """A basic MainWindowState holding a lambda to recreate a MainWindow."""
235
236    def __init__(
237        self,
238        create_call: Callable[
239            [
240                Literal['in_right', 'in_left', 'in_scale'] | None,
241                bauiv1.Widget | None,
242            ],
243            bauiv1.MainWindow,
244        ],
245    ) -> None:
246        super().__init__()
247        self.create_call = create_call
248
249    @override
250    def create_window(
251        self,
252        transition: Literal['in_right', 'in_left', 'in_scale'] | None = None,
253        origin_widget: bauiv1.Widget | None = None,
254    ) -> bauiv1.MainWindow:
255        return self.create_call(transition, origin_widget)

A basic MainWindowState holding a lambda to recreate a MainWindow.

BasicMainWindowState( create_call: Callable[[Optional[Literal['in_right', 'in_left', 'in_scale']], _bauiv1.Widget | None], MainWindow])
236    def __init__(
237        self,
238        create_call: Callable[
239            [
240                Literal['in_right', 'in_left', 'in_scale'] | None,
241                bauiv1.Widget | None,
242            ],
243            bauiv1.MainWindow,
244        ],
245    ) -> None:
246        super().__init__()
247        self.create_call = create_call
create_call
@override
def create_window( self, transition: Optional[Literal['in_right', 'in_left', 'in_scale']] = None, origin_widget: _bauiv1.Widget | None = None) -> MainWindow:
249    @override
250    def create_window(
251        self,
252        transition: Literal['in_right', 'in_left', 'in_scale'] | None = None,
253        origin_widget: bauiv1.Widget | None = None,
254    ) -> bauiv1.MainWindow:
255        return self.create_call(transition, origin_widget)

Create a window based on this state.

WindowState child classes should override this to recreate their particular type of window.

def buttonwidget( *, edit: _bauiv1.Widget | None = None, parent: _bauiv1.Widget | None = None, size: Optional[Sequence[float]] = None, position: Optional[Sequence[float]] = None, on_activate_call: Optional[Callable] = None, label: str | Lstr | None = None, color: Optional[Sequence[float]] = None, down_widget: _bauiv1.Widget | None = None, up_widget: _bauiv1.Widget | None = None, left_widget: _bauiv1.Widget | None = None, right_widget: _bauiv1.Widget | None = None, texture: _bauiv1.Texture | None = None, text_scale: float | None = None, textcolor: Optional[Sequence[float]] = None, enable_sound: bool | None = None, mesh_transparent: _bauiv1.Mesh | None = None, mesh_opaque: _bauiv1.Mesh | None = None, repeat: bool | None = None, scale: float | None = None, transition_delay: float | None = None, on_select_call: Optional[Callable] = None, button_type: str | None = None, extra_touch_border_scale: float | None = None, selectable: bool | None = None, show_buffer_top: float | None = None, icon: _bauiv1.Texture | None = None, iconscale: float | None = None, icon_tint: float | None = None, icon_color: Optional[Sequence[float]] = None, autoselect: bool | None = None, mask_texture: _bauiv1.Texture | None = None, tint_texture: _bauiv1.Texture | None = None, tint_color: Optional[Sequence[float]] = None, tint2_color: Optional[Sequence[float]] = None, text_flatness: float | None = None, text_res_scale: float | None = None, enabled: bool | None = None) -> _bauiv1.Widget:
147def buttonwidget(
148    *,
149    edit: bauiv1.Widget | None = None,
150    parent: bauiv1.Widget | None = None,
151    size: Sequence[float] | None = None,
152    position: Sequence[float] | None = None,
153    on_activate_call: Callable | None = None,
154    label: str | bauiv1.Lstr | None = None,
155    color: Sequence[float] | None = None,
156    down_widget: bauiv1.Widget | None = None,
157    up_widget: bauiv1.Widget | None = None,
158    left_widget: bauiv1.Widget | None = None,
159    right_widget: bauiv1.Widget | None = None,
160    texture: bauiv1.Texture | None = None,
161    text_scale: float | None = None,
162    textcolor: Sequence[float] | None = None,
163    enable_sound: bool | None = None,
164    mesh_transparent: bauiv1.Mesh | None = None,
165    mesh_opaque: bauiv1.Mesh | None = None,
166    repeat: bool | None = None,
167    scale: float | None = None,
168    transition_delay: float | None = None,
169    on_select_call: Callable | None = None,
170    button_type: str | None = None,
171    extra_touch_border_scale: float | None = None,
172    selectable: bool | None = None,
173    show_buffer_top: float | None = None,
174    icon: bauiv1.Texture | None = None,
175    iconscale: float | None = None,
176    icon_tint: float | None = None,
177    icon_color: Sequence[float] | None = None,
178    autoselect: bool | None = None,
179    mask_texture: bauiv1.Texture | None = None,
180    tint_texture: bauiv1.Texture | None = None,
181    tint_color: Sequence[float] | None = None,
182    tint2_color: Sequence[float] | None = None,
183    text_flatness: float | None = None,
184    text_res_scale: float | None = None,
185    enabled: bool | None = None,
186) -> bauiv1.Widget:
187    """Create or edit a button widget.
188
189    Category: **User Interface Functions**
190
191    Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise
192    a new one is created and returned. Arguments that are not set to None
193    are applied to the Widget.
194    """
195    import bauiv1  # pylint: disable=cyclic-import
196
197    return bauiv1.Widget()

Create or edit a button widget.

Category: User Interface Functions

Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise a new one is created and returned. Arguments that are not set to None are applied to the Widget.

Call = <class 'babase._general._Call'>
def charstr(char_id: SpecialChar) -> str:
621def charstr(char_id: babase.SpecialChar) -> str:
622    """Get a unicode string representing a special character.
623
624    Category: **General Utility Functions**
625
626    Note that these utilize the private-use block of unicode characters
627    (U+E000-U+F8FF) and are specific to the game; exporting or rendering
628    them elsewhere will be meaningless.
629
630    See babase.SpecialChar for the list of available characters.
631    """
632    return str()

Get a unicode string representing a special character.

Category: General Utility Functions

Note that these utilize the private-use block of unicode characters (U+E000-U+F8FF) and are specific to the game; exporting or rendering them elsewhere will be meaningless.

See babase.SpecialChar for the list of available characters.

def checkboxwidget( *, edit: _bauiv1.Widget | None = None, parent: _bauiv1.Widget | None = None, size: Optional[Sequence[float]] = None, position: Optional[Sequence[float]] = None, text: str | Lstr | None = None, value: bool | None = None, on_value_change_call: Optional[Callable[[bool], NoneType]] = None, on_select_call: Optional[Callable[[], NoneType]] = None, text_scale: float | None = None, textcolor: Optional[Sequence[float]] = None, scale: float | None = None, is_radio_button: bool | None = None, maxwidth: float | None = None, autoselect: bool | None = None, color: Optional[Sequence[float]] = None) -> _bauiv1.Widget:
200def checkboxwidget(
201    *,
202    edit: bauiv1.Widget | None = None,
203    parent: bauiv1.Widget | None = None,
204    size: Sequence[float] | None = None,
205    position: Sequence[float] | None = None,
206    text: str | bauiv1.Lstr | None = None,
207    value: bool | None = None,
208    on_value_change_call: Callable[[bool], None] | None = None,
209    on_select_call: Callable[[], None] | None = None,
210    text_scale: float | None = None,
211    textcolor: Sequence[float] | None = None,
212    scale: float | None = None,
213    is_radio_button: bool | None = None,
214    maxwidth: float | None = None,
215    autoselect: bool | None = None,
216    color: Sequence[float] | None = None,
217) -> bauiv1.Widget:
218    """Create or edit a check-box widget.
219
220    Category: **User Interface Functions**
221
222    Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise
223    a new one is created and returned. Arguments that are not set to None
224    are applied to the Widget.
225    """
226    import bauiv1  # pylint: disable=cyclic-import
227
228    return bauiv1.Widget()

Create or edit a check-box widget.

Category: User Interface Functions

Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise a new one is created and returned. Arguments that are not set to None are applied to the Widget.

def clipboard_is_supported() -> bool:
657def clipboard_is_supported() -> bool:
658    """Return whether this platform supports clipboard operations at all.
659
660    Category: **General Utility Functions**
661
662    If this returns False, UIs should not show 'copy to clipboard'
663    buttons, etc.
664    """
665    return bool()

Return whether this platform supports clipboard operations at all.

Category: General Utility Functions

If this returns False, UIs should not show 'copy to clipboard' buttons, etc.

def clipboard_set_text(value: str) -> None:
668def clipboard_set_text(value: str) -> None:
669    """Copy a string to the system clipboard.
670
671    Category: **General Utility Functions**
672
673    Ensure that babase.clipboard_is_supported() returns True before adding
674     buttons/etc. that make use of this functionality.
675    """
676    return None

Copy a string to the system clipboard.

Category: General Utility Functions

Ensure that babase.clipboard_is_supported() returns True before adding buttons/etc. that make use of this functionality.

def columnwidget( *, edit: _bauiv1.Widget | None = None, parent: _bauiv1.Widget | None = None, size: Optional[Sequence[float]] = None, position: Optional[Sequence[float]] = None, background: bool | None = None, selected_child: _bauiv1.Widget | None = None, visible_child: _bauiv1.Widget | None = None, single_depth: bool | None = None, print_list_exit_instructions: bool | None = None, left_border: float | None = None, top_border: float | None = None, bottom_border: float | None = None, selection_loops_to_parent: bool | None = None, border: float | None = None, margin: float | None = None, claims_left_right: bool | None = None, claims_tab: bool | None = None) -> _bauiv1.Widget:
231def columnwidget(
232    *,
233    edit: bauiv1.Widget | None = None,
234    parent: bauiv1.Widget | None = None,
235    size: Sequence[float] | None = None,
236    position: Sequence[float] | None = None,
237    background: bool | None = None,
238    selected_child: bauiv1.Widget | None = None,
239    visible_child: bauiv1.Widget | None = None,
240    single_depth: bool | None = None,
241    print_list_exit_instructions: bool | None = None,
242    left_border: float | None = None,
243    top_border: float | None = None,
244    bottom_border: float | None = None,
245    selection_loops_to_parent: bool | None = None,
246    border: float | None = None,
247    margin: float | None = None,
248    claims_left_right: bool | None = None,
249    claims_tab: bool | None = None,
250) -> bauiv1.Widget:
251    """Create or edit a column widget.
252
253    Category: **User Interface Functions**
254
255    Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise
256    a new one is created and returned. Arguments that are not set to None
257    are applied to the Widget.
258    """
259    import bauiv1  # pylint: disable=cyclic-import
260
261    return bauiv1.Widget()

Create or edit a column widget.

Category: User Interface Functions

Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise a new one is created and returned. Arguments that are not set to None are applied to the Widget.

def containerwidget( *, edit: _bauiv1.Widget | None = None, parent: _bauiv1.Widget | None = None, size: Optional[Sequence[float]] = None, position: Optional[Sequence[float]] = None, background: bool | None = None, selected_child: _bauiv1.Widget | None = None, transition: str | None = None, cancel_button: _bauiv1.Widget | None = None, start_button: _bauiv1.Widget | None = None, root_selectable: bool | None = None, on_activate_call: Optional[Callable[[], NoneType]] = None, claims_left_right: bool | None = None, claims_tab: bool | None = None, selection_loops: bool | None = None, selection_loops_to_parent: bool | None = None, scale: float | None = None, on_outside_click_call: Optional[Callable[[], NoneType]] = None, single_depth: bool | None = None, visible_child: _bauiv1.Widget | None = None, stack_offset: Optional[Sequence[float]] = None, color: Optional[Sequence[float]] = None, on_cancel_call: Optional[Callable[[], NoneType]] = None, print_list_exit_instructions: bool | None = None, click_activate: bool | None = None, always_highlight: bool | None = None, selectable: bool | None = None, scale_origin_stack_offset: Optional[Sequence[float]] = None, toolbar_visibility: Optional[Literal['menu_minimal', 'menu_minimal_no_back', 'menu_full', 'menu_full_no_back', 'menu_store', 'menu_store_no_back', 'menu_in_game', 'menu_tokens', 'get_tokens', 'inherit']] = None, on_select_call: Optional[Callable[[], NoneType]] = None, claim_outside_clicks: bool | None = None, claims_up_down: bool | None = None) -> _bauiv1.Widget:
264def containerwidget(
265    *,
266    edit: bauiv1.Widget | None = None,
267    parent: bauiv1.Widget | None = None,
268    size: Sequence[float] | None = None,
269    position: Sequence[float] | None = None,
270    background: bool | None = None,
271    selected_child: bauiv1.Widget | None = None,
272    transition: str | None = None,
273    cancel_button: bauiv1.Widget | None = None,
274    start_button: bauiv1.Widget | None = None,
275    root_selectable: bool | None = None,
276    on_activate_call: Callable[[], None] | None = None,
277    claims_left_right: bool | None = None,
278    claims_tab: bool | None = None,
279    selection_loops: bool | None = None,
280    selection_loops_to_parent: bool | None = None,
281    scale: float | None = None,
282    on_outside_click_call: Callable[[], None] | None = None,
283    single_depth: bool | None = None,
284    visible_child: bauiv1.Widget | None = None,
285    stack_offset: Sequence[float] | None = None,
286    color: Sequence[float] | None = None,
287    on_cancel_call: Callable[[], None] | None = None,
288    print_list_exit_instructions: bool | None = None,
289    click_activate: bool | None = None,
290    always_highlight: bool | None = None,
291    selectable: bool | None = None,
292    scale_origin_stack_offset: Sequence[float] | None = None,
293    toolbar_visibility: (
294        Literal[
295            'menu_minimal',
296            'menu_minimal_no_back',
297            'menu_full',
298            'menu_full_no_back',
299            'menu_store',
300            'menu_store_no_back',
301            'menu_in_game',
302            'menu_tokens',
303            'get_tokens',
304            'inherit',
305        ]
306        | None
307    ) = None,
308    on_select_call: Callable[[], None] | None = None,
309    claim_outside_clicks: bool | None = None,
310    claims_up_down: bool | None = None,
311) -> bauiv1.Widget:
312    """Create or edit a container widget.
313
314    Category: **User Interface Functions**
315
316    Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise
317    a new one is created and returned. Arguments that are not set to None
318    are applied to the Widget.
319    """
320    import bauiv1  # pylint: disable=cyclic-import
321
322    return bauiv1.Widget()

Create or edit a container widget.

Category: User Interface Functions

Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise a new one is created and returned. Arguments that are not set to None are applied to the Widget.

class ContextRef:
148class ContextRef:
149    """Store or use a ballistica context.
150
151    Category: **General Utility Classes**
152
153    Many operations such as bascenev1.newnode() or bascenev1.gettexture()
154    operate implicitly on a current 'context'. A context is some sort of
155    state that functionality can implicitly use. Context determines, for
156    example, which scene nodes or textures get added to without having to
157    specify it explicitly in the newnode()/gettexture() call. Contexts can
158    also affect object lifecycles; for example a babase.ContextCall will
159    become a no-op when the context it was created in is destroyed.
160
161    In general, if you are a modder, you should not need to worry about
162    contexts; mod code should mostly be getting run in the correct
163    context and timers and other callbacks will take care of saving
164    and restoring contexts automatically. There may be rare cases,
165    however, where you need to deal directly with contexts, and that is
166    where this class comes in.
167
168    Creating a babase.ContextRef() will capture a reference to the current
169    context. Other modules may provide ways to access their contexts; for
170    example a bascenev1.Activity instance has a 'context' attribute. You
171    can also use babase.ContextRef.empty() to create a reference to *no*
172    context. Some code such as UI calls may expect this and may complain
173    if you try to use them within a context.
174
175    ##### Usage
176    ContextRefs are generally used with the Python 'with' statement, which
177    sets the context they point to as current on entry and resets it to
178    the previous value on exit.
179
180    ##### Example
181    Explicitly create a few UI bits with no context set.
182    (UI stuff may complain if called within a context):
183    >>> with bui.ContextRef.empty():
184    ...     my_container = bui.containerwidget()
185    """
186
187    def __init__(
188        self,
189    ) -> None:
190        pass
191
192    def __enter__(self) -> None:
193        """Support for "with" statement."""
194        pass
195
196    def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> Any:
197        """Support for "with" statement."""
198        pass
199
200    @classmethod
201    def empty(cls) -> ContextRef:
202        """Return a ContextRef pointing to no context.
203
204        This is useful when code should be run free of a context.
205        For example, UI code generally insists on being run this way.
206        Otherwise, callbacks set on the UI could inadvertently stop working
207        due to a game activity ending, which would be unintuitive behavior.
208        """
209        return ContextRef()
210
211    def is_empty(self) -> bool:
212        """Whether the context was created as empty."""
213        return bool()
214
215    def is_expired(self) -> bool:
216        """Whether the context has expired."""
217        return bool()

Store or use a ballistica context.

Category: General Utility Classes

Many operations such as bascenev1.newnode() or bascenev1.gettexture() operate implicitly on a current 'context'. A context is some sort of state that functionality can implicitly use. Context determines, for example, which scene nodes or textures get added to without having to specify it explicitly in the newnode()/gettexture() call. Contexts can also affect object lifecycles; for example a babase.ContextCall will become a no-op when the context it was created in is destroyed.

In general, if you are a modder, you should not need to worry about contexts; mod code should mostly be getting run in the correct context and timers and other callbacks will take care of saving and restoring contexts automatically. There may be rare cases, however, where you need to deal directly with contexts, and that is where this class comes in.

Creating a babase.ContextRef() will capture a reference to the current context. Other modules may provide ways to access their contexts; for example a bascenev1.Activity instance has a 'context' attribute. You can also use babase.ContextRef.empty() to create a reference to no context. Some code such as UI calls may expect this and may complain if you try to use them within a context.

Usage

ContextRefs are generally used with the Python 'with' statement, which sets the context they point to as current on entry and resets it to the previous value on exit.

Example

Explicitly create a few UI bits with no context set. (UI stuff may complain if called within a context):

>>> with bui.ContextRef.empty():
...     my_container = bui.containerwidget()
@classmethod
def empty(cls) -> _babase.ContextRef:
200    @classmethod
201    def empty(cls) -> ContextRef:
202        """Return a ContextRef pointing to no context.
203
204        This is useful when code should be run free of a context.
205        For example, UI code generally insists on being run this way.
206        Otherwise, callbacks set on the UI could inadvertently stop working
207        due to a game activity ending, which would be unintuitive behavior.
208        """
209        return ContextRef()

Return a ContextRef pointing to no context.

This is useful when code should be run free of a context. For example, UI code generally insists on being run this way. Otherwise, callbacks set on the UI could inadvertently stop working due to a game activity ending, which would be unintuitive behavior.

def is_empty(self) -> bool:
211    def is_empty(self) -> bool:
212        """Whether the context was created as empty."""
213        return bool()

Whether the context was created as empty.

def is_expired(self) -> bool:
215    def is_expired(self) -> bool:
216        """Whether the context has expired."""
217        return bool()

Whether the context has expired.

def displaytime() -> DisplayTime:
762def displaytime() -> babase.DisplayTime:
763    """Return the current display-time in seconds.
764
765    Category: **General Utility Functions**
766
767    Display-time is a time value intended to be used for animation and other
768    visual purposes. It will generally increment by a consistent amount each
769    frame. It will pass at an overall similar rate to AppTime, but trades
770    accuracy for smoothness.
771
772    Note that the value returned here is simply a float; it just has a
773    unique type in the type-checker's eyes to help prevent it from being
774    accidentally used with time functionality expecting other time types.
775    """
776    import babase  # pylint: disable=cyclic-import
777
778    return babase.DisplayTime(0.0)

Return the current display-time in seconds.

Category: General Utility Functions

Display-time is a time value intended to be used for animation and other visual purposes. It will generally increment by a consistent amount each frame. It will pass at an overall similar rate to AppTime, but trades accuracy for smoothness.

Note that the value returned here is simply a float; it just has a unique type in the type-checker's eyes to help prevent it from being accidentally used with time functionality expecting other time types.

DisplayTime = DisplayTime
def displaytimer(time: float, call: Callable[[], Any]) -> None:
781def displaytimer(time: float, call: Callable[[], Any]) -> None:
782    """Schedule a callable object to run based on display-time.
783
784    Category: **General Utility Functions**
785
786    This function creates a one-off timer which cannot be canceled or
787    modified once created. If you require the ability to do so, or need
788    a repeating timer, use the babase.DisplayTimer class instead.
789
790    Display-time is a time value intended to be used for animation and other
791    visual purposes. It will generally increment by a consistent amount each
792    frame. It will pass at an overall similar rate to AppTime, but trades
793    accuracy for smoothness.
794
795    ##### Arguments
796    ###### time (float)
797    > Length of time in seconds that the timer will wait before firing.
798
799    ###### call (Callable[[], Any])
800    > A callable Python object. Note that the timer will retain a
801    strong reference to the callable for as long as the timer exists, so you
802    may want to look into concepts such as babase.WeakCall if that is not
803    desired.
804
805    ##### Examples
806    Print some stuff through time:
807    >>> babase.screenmessage('hello from now!')
808    >>> babase.displaytimer(1.0, babase.Call(babase.screenmessage,
809    ...                       'hello from the future!'))
810    >>> babase.displaytimer(2.0, babase.Call(babase.screenmessage,
811    ...                       'hello from the future 2!'))
812    """
813    return None

Schedule a callable object to run based on display-time.

Category: General Utility Functions

This function creates a one-off timer which cannot be canceled or modified once created. If you require the ability to do so, or need a repeating timer, use the babase.DisplayTimer class instead.

Display-time is a time value intended to be used for animation and other visual purposes. It will generally increment by a consistent amount each frame. It will pass at an overall similar rate to AppTime, but trades accuracy for smoothness.

Arguments
time (float)

Length of time in seconds that the timer will wait before firing.

call (Callable[[], Any])

A callable Python object. Note that the timer will retain a strong reference to the callable for as long as the timer exists, so you may want to look into concepts such as babase.WeakCall if that is not desired.

Examples

Print some stuff through time:

>>> babase.screenmessage('hello from now!')
>>> babase.displaytimer(1.0, babase.Call(babase.screenmessage,
...                       'hello from the future!'))
>>> babase.displaytimer(2.0, babase.Call(babase.screenmessage,
...                       'hello from the future 2!'))
class DisplayTimer:
220class DisplayTimer:
221    """Timers are used to run code at later points in time.
222
223    Category: **General Utility Classes**
224
225    This class encapsulates a timer based on display-time.
226    The underlying timer will be destroyed when this object is no longer
227    referenced. If you do not want to worry about keeping a reference to
228    your timer around, use the babase.displaytimer() function instead to get a
229    one-off timer.
230
231    Display-time is a time value intended to be used for animation and
232    other visual purposes. It will generally increment by a consistent
233    amount each frame. It will pass at an overall similar rate to AppTime,
234    but trades accuracy for smoothness.
235
236    ##### Arguments
237    ###### time
238    > Length of time in seconds that the timer will wait before firing.
239
240    ###### call
241    > A callable Python object. Remember that the timer will retain a
242    strong reference to the callable for as long as it exists, so you
243    may want to look into concepts such as babase.WeakCall if that is not
244    desired.
245
246    ###### repeat
247    > If True, the timer will fire repeatedly, with each successive
248    firing having the same delay as the first.
249
250    ##### Example
251
252    Use a Timer object to print repeatedly for a few seconds:
253    ... def say_it():
254    ...     babase.screenmessage('BADGER!')
255    ... def stop_saying_it():
256    ...     global g_timer
257    ...     g_timer = None
258    ...     babase.screenmessage('MUSHROOM MUSHROOM!')
259    ... # Create our timer; it will run as long as we have the self.t ref.
260    ... g_timer = babase.DisplayTimer(0.3, say_it, repeat=True)
261    ... # Now fire off a one-shot timer to kill it.
262    ... babase.displaytimer(3.89, stop_saying_it)
263    """
264
265    def __init__(
266        self, time: float, call: Callable[[], Any], repeat: bool = False
267    ) -> None:
268        pass

Timers are used to run code at later points in time.

Category: General Utility Classes

This class encapsulates a timer based on display-time. The underlying timer will be destroyed when this object is no longer referenced. If you do not want to worry about keeping a reference to your timer around, use the babase.displaytimer() function instead to get a one-off timer.

Display-time is a time value intended to be used for animation and other visual purposes. It will generally increment by a consistent amount each frame. It will pass at an overall similar rate to AppTime, but trades accuracy for smoothness.

Arguments
time

Length of time in seconds that the timer will wait before firing.

call

A callable Python object. Remember that the timer will retain a strong reference to the callable for as long as it exists, so you may want to look into concepts such as babase.WeakCall if that is not desired.

repeat

If True, the timer will fire repeatedly, with each successive firing having the same delay as the first.

Example

Use a Timer object to print repeatedly for a few seconds: ... def say_it(): ... babase.screenmessage('BADGER!') ... def stop_saying_it(): ... global g_timer ... g_timer = None ... babase.screenmessage('MUSHROOM MUSHROOM!') ... # Create our timer; it will run as long as we have the self.t ref. ... g_timer = babase.DisplayTimer(0.3, say_it, repeat=True) ... # Now fire off a one-shot timer to kill it. ... babase.displaytimer(3.89, stop_saying_it)

DisplayTimer(time: float, call: Callable[[], Any], repeat: bool = False)
265    def __init__(
266        self, time: float, call: Callable[[], Any], repeat: bool = False
267    ) -> None:
268        pass
def do_once() -> bool:
821def do_once() -> bool:
822    """Return whether this is the first time running a line of code.
823
824    Category: **General Utility Functions**
825
826    This is used by 'print_once()' type calls to keep from overflowing
827    logs. The call functions by registering the filename and line where
828    The call is made from.  Returns True if this location has not been
829    registered already, and False if it has.
830
831    ##### Example
832    This print will only fire for the first loop iteration:
833    >>> for i in range(10):
834    ... if babase.do_once():
835    ...     print('HelloWorld once from loop!')
836    """
837    return bool()

Return whether this is the first time running a line of code.

Category: General Utility Functions

This is used by 'print_once()' type calls to keep from overflowing logs. The call functions by registering the filename and line where The call is made from. Returns True if this location has not been registered already, and False if it has.

Example

This print will only fire for the first loop iteration:

>>> for i in range(10):
... if babase.do_once():
...     print('HelloWorld once from loop!')
def get_input_idle_time() -> float:
1006def get_input_idle_time() -> float:
1007    """Return seconds since any local input occurred (touch, keypress, etc.)."""
1008    return float()

Return seconds since any local input occurred (touch, keypress, etc.).

def get_ip_address_type(addr: str) -> socket.AddressFamily:
45def get_ip_address_type(addr: str) -> socket.AddressFamily:
46    """Return socket.AF_INET6 or socket.AF_INET4 for the provided address."""
47
48    version = ipaddress.ip_address(addr).version
49    if version == 4:
50        return socket.AF_INET
51    assert version == 6
52    return socket.AF_INET6

Return socket.AF_INET6 or socket.AF_INET4 for the provided address.

def get_qrcode_texture(url: str) -> _bauiv1.Texture:
325def get_qrcode_texture(url: str) -> bauiv1.Texture:
326    """Return a QR code texture.
327
328    The provided url must be 64 bytes or less.
329    """
330    import bauiv1  # pylint: disable=cyclic-import
331
332    return bauiv1.Texture()

Return a QR code texture.

The provided url must be 64 bytes or less.

def get_type_name(cls: type) -> str:
112def get_type_name(cls: type) -> str:
113    """Return a full type name including module for a class."""
114    return f'{cls.__module__}.{cls.__name__}'

Return a full type name including module for a class.

def getclass( name: str, subclassof: type[~T], check_sdlib_modulename_clash: bool = False) -> type[~T]:
71def getclass(
72    name: str, subclassof: type[T], check_sdlib_modulename_clash: bool = False
73) -> type[T]:
74    """Given a full class name such as foo.bar.MyClass, return the class.
75
76    Category: **General Utility Functions**
77
78    The class will be checked to make sure it is a subclass of the provided
79    'subclassof' class, and a TypeError will be raised if not.
80    """
81    import importlib
82
83    splits = name.split('.')
84    modulename = '.'.join(splits[:-1])
85    classname = splits[-1]
86    if modulename in sys.stdlib_module_names and check_sdlib_modulename_clash:
87        raise Exception(f'{modulename} is an inbuilt module.')
88    module = importlib.import_module(modulename)
89    cls: type = getattr(module, classname)
90
91    if not issubclass(cls, subclassof):
92        raise TypeError(f'{name} is not a subclass of {subclassof}.')
93    return cls

Given a full class name such as foo.bar.MyClass, return the class.

Category: General Utility Functions

The class will be checked to make sure it is a subclass of the provided 'subclassof' class, and a TypeError will be raised if not.

def getmesh(name: str) -> _bauiv1.Mesh:
359def getmesh(name: str) -> bauiv1.Mesh:
360    """Load a mesh for use solely in the local user interface."""
361    import bauiv1  # pylint: disable=cyclic-import
362
363    return bauiv1.Mesh()

Load a mesh for use solely in the local user interface.

def getsound(name: str) -> _bauiv1.Sound:
366def getsound(name: str) -> bauiv1.Sound:
367    """Load a sound for use in the ui."""
368    import bauiv1  # pylint: disable=cyclic-import
369
370    return bauiv1.Sound()

Load a sound for use in the ui.

def gettexture(name: str) -> _bauiv1.Texture:
373def gettexture(name: str) -> bauiv1.Texture:
374    """Load a texture for use in the ui."""
375    import bauiv1  # pylint: disable=cyclic-import
376
377    return bauiv1.Texture()

Load a texture for use in the ui.

def hscrollwidget( *, edit: _bauiv1.Widget | None = None, parent: _bauiv1.Widget | None = None, size: Optional[Sequence[float]] = None, position: Optional[Sequence[float]] = None, background: bool | None = None, selected_child: _bauiv1.Widget | None = None, capture_arrows: bool | None = None, on_select_call: Optional[Callable[[], NoneType]] = None, center_small_content: bool | None = None, color: Optional[Sequence[float]] = None, highlight: bool | None = None, border_opacity: float | None = None, simple_culling_h: float | None = None, claims_left_right: bool | None = None, claims_up_down: bool | None = None, claims_tab: bool | None = None) -> _bauiv1.Widget:
380def hscrollwidget(
381    *,
382    edit: bauiv1.Widget | None = None,
383    parent: bauiv1.Widget | None = None,
384    size: Sequence[float] | None = None,
385    position: Sequence[float] | None = None,
386    background: bool | None = None,
387    selected_child: bauiv1.Widget | None = None,
388    capture_arrows: bool | None = None,
389    on_select_call: Callable[[], None] | None = None,
390    center_small_content: bool | None = None,
391    color: Sequence[float] | None = None,
392    highlight: bool | None = None,
393    border_opacity: float | None = None,
394    simple_culling_h: float | None = None,
395    claims_left_right: bool | None = None,
396    claims_up_down: bool | None = None,
397    claims_tab: bool | None = None,
398) -> bauiv1.Widget:
399    """Create or edit a horizontal scroll widget.
400
401    Category: **User Interface Functions**
402
403    Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise
404    a new one is created and returned. Arguments that are not set to None
405    are applied to the Widget.
406    """
407    import bauiv1  # pylint: disable=cyclic-import
408
409    return bauiv1.Widget()

Create or edit a horizontal scroll widget.

Category: User Interface Functions

Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise a new one is created and returned. Arguments that are not set to None are applied to the Widget.

def imagewidget( *, edit: _bauiv1.Widget | None = None, parent: _bauiv1.Widget | None = None, size: Optional[Sequence[float]] = None, position: Optional[Sequence[float]] = None, color: Optional[Sequence[float]] = None, texture: _bauiv1.Texture | None = None, opacity: float | None = None, mesh_transparent: _bauiv1.Mesh | None = None, mesh_opaque: _bauiv1.Mesh | None = None, has_alpha_channel: bool = True, tint_texture: _bauiv1.Texture | None = None, tint_color: Optional[Sequence[float]] = None, transition_delay: float | None = None, draw_controller: _bauiv1.Widget | None = None, tint2_color: Optional[Sequence[float]] = None, tilt_scale: float | None = None, mask_texture: _bauiv1.Texture | None = None, radial_amount: float | None = None, draw_controller_mult: float | None = None) -> _bauiv1.Widget:
412def imagewidget(
413    *,
414    edit: bauiv1.Widget | None = None,
415    parent: bauiv1.Widget | None = None,
416    size: Sequence[float] | None = None,
417    position: Sequence[float] | None = None,
418    color: Sequence[float] | None = None,
419    texture: bauiv1.Texture | None = None,
420    opacity: float | None = None,
421    mesh_transparent: bauiv1.Mesh | None = None,
422    mesh_opaque: bauiv1.Mesh | None = None,
423    has_alpha_channel: bool = True,
424    tint_texture: bauiv1.Texture | None = None,
425    tint_color: Sequence[float] | None = None,
426    transition_delay: float | None = None,
427    draw_controller: bauiv1.Widget | None = None,
428    tint2_color: Sequence[float] | None = None,
429    tilt_scale: float | None = None,
430    mask_texture: bauiv1.Texture | None = None,
431    radial_amount: float | None = None,
432    draw_controller_mult: float | None = None,
433) -> bauiv1.Widget:
434    """Create or edit an image widget.
435
436    Category: **User Interface Functions**
437
438    Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise
439    a new one is created and returned. Arguments that are not set to None
440    are applied to the Widget.
441    """
442    import bauiv1  # pylint: disable=cyclic-import
443
444    return bauiv1.Widget()

Create or edit an image widget.

Category: User Interface Functions

Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise a new one is created and returned. Arguments that are not set to None are applied to the Widget.

def is_browser_likely_available() -> bool:
27def is_browser_likely_available() -> bool:
28    """Return whether a browser likely exists on the current device.
29
30    category: General Utility Functions
31
32    If this returns False you may want to avoid calling babase.show_url()
33    with any lengthy addresses. (ba.show_url() will display an address
34    as a string in a window if unable to bring up a browser, but that
35    is only useful for simple URLs.)
36    """
37    app = _babase.app
38
39    if app.classic is None:
40        logging.warning(
41            'is_browser_likely_available() needs to be updated'
42            ' to work without classic.'
43        )
44        return True
45
46    platform = app.classic.platform
47    hastouchscreen = _babase.hastouchscreen()
48
49    # If we're on a vr device or an android device with no touchscreen,
50    # assume no browser.
51    # FIXME: Might not be the case anymore; should make this definable
52    #  at the platform level.
53    if app.env.vr or (platform == 'android' and not hastouchscreen):
54        return False
55
56    # Anywhere else assume we've got one.
57    return True

Return whether a browser likely exists on the current device.

category: General Utility Functions

If this returns False you may want to avoid calling babase.show_url() with any lengthy addresses. (ba.show_url() will display an address as a string in a window if unable to bring up a browser, but that is only useful for simple URLs.)

class Keyboard:
14class Keyboard:
15    """Chars definitions for on-screen keyboard.
16
17    Category: **App Classes**
18
19    Keyboards are discoverable by the meta-tag system
20    and the user can select which one they want to use.
21    On-screen keyboard uses chars from active babase.Keyboard.
22    """
23
24    name: str
25    """Displays when user selecting this keyboard."""
26
27    chars: list[tuple[str, ...]]
28    """Used for row/column lengths."""
29
30    pages: dict[str, tuple[str, ...]]
31    """Extra chars like emojis."""
32
33    nums: tuple[str, ...]
34    """The 'num' page."""

Chars definitions for on-screen keyboard.

Category: App Classes

Keyboards are discoverable by the meta-tag system and the user can select which one they want to use. On-screen keyboard uses chars from active babase.Keyboard.

name: str

Displays when user selecting this keyboard.

chars: list[tuple[str, ...]]

Used for row/column lengths.

pages: dict[str, tuple[str, ...]]

Extra chars like emojis.

nums: tuple[str, ...]

The 'num' page.

class LoginAdapter:
 31class LoginAdapter:
 32    """Allows using implicit login types in an explicit way.
 33
 34    Some login types such as Google Play Game Services or Game Center are
 35    basically always present and often do not provide a way to log out
 36    from within a running app, so this adapter exists to use them in a
 37    flexible manner by 'attaching' and 'detaching' from an always-present
 38    login, allowing for its use alongside other login types. It also
 39    provides common functionality for server-side account verification and
 40    other handy bits.
 41    """
 42
 43    @dataclass
 44    class SignInResult:
 45        """Describes the final result of a sign-in attempt."""
 46
 47        credentials: str
 48
 49    @dataclass
 50    class ImplicitLoginState:
 51        """Describes the current state of an implicit login."""
 52
 53        login_id: str
 54        display_name: str
 55
 56    def __init__(self, login_type: LoginType):
 57        assert _babase.in_logic_thread()
 58        self.login_type = login_type
 59        self._implicit_login_state: LoginAdapter.ImplicitLoginState | None = (
 60            None
 61        )
 62        self._on_app_loading_called = False
 63        self._implicit_login_state_dirty = False
 64        self._back_end_active = False
 65
 66        # Which login of our type (if any) is associated with the
 67        # current active primary account.
 68        self._active_login_id: str | None = None
 69
 70        self._last_sign_in_time: float | None = None
 71        self._last_sign_in_desc: str | None = None
 72
 73    def on_app_loading(self) -> None:
 74        """Should be called for each adapter in on_app_loading."""
 75
 76        assert not self._on_app_loading_called
 77        self._on_app_loading_called = True
 78
 79        # Any implicit state we received up until now needs to be pushed
 80        # to the app account subsystem.
 81        self._update_implicit_login_state()
 82
 83    def set_implicit_login_state(
 84        self, state: ImplicitLoginState | None
 85    ) -> None:
 86        """Keep the adapter informed of implicit login states.
 87
 88        This should be called by the adapter back-end when an account
 89        of their associated type gets logged in or out.
 90        """
 91        assert _babase.in_logic_thread()
 92
 93        # Ignore redundant sets.
 94        if state == self._implicit_login_state:
 95            return
 96
 97        if state is None:
 98            logger.debug(
 99                '%s implicit state changed; now signed out.',
100                self.login_type.name,
101            )
102        else:
103            logger.debug(
104                '%s implicit state changed; now signed in as %s.',
105                self.login_type.name,
106                state.display_name,
107            )
108
109        self._implicit_login_state = state
110        self._implicit_login_state_dirty = True
111
112        # (possibly) push it to the app for handling.
113        self._update_implicit_login_state()
114
115        # This might affect whether we consider that back-end as 'active'.
116        self._update_back_end_active()
117
118    def set_active_logins(self, logins: dict[LoginType, str]) -> None:
119        """Keep the adapter informed of actively used logins.
120
121        This should be called by the app's account subsystem to
122        keep adapters up to date on the full set of logins attached
123        to the currently-in-use account.
124        Note that the logins dict passed in should be immutable as
125        only a reference to it is stored, not a copy.
126        """
127        assert _babase.in_logic_thread()
128        logger.debug(
129            '%s adapter got active logins %s.',
130            self.login_type.name,
131            {k: v[:4] + '...' + v[-4:] for k, v in logins.items()},
132        )
133
134        self._active_login_id = logins.get(self.login_type)
135        self._update_back_end_active()
136
137    def on_back_end_active_change(self, active: bool) -> None:
138        """Called when active state for the back-end is (possibly) changing.
139
140        Meant to be overridden by subclasses.
141        Being active means that the implicit login provided by the back-end
142        is actually being used by the app. It should therefore register
143        unlocked achievements, leaderboard scores, allow viewing native
144        UIs, etc. When not active it should ignore everything and behave
145        as if signed out, even if it technically is still signed in.
146        """
147        assert _babase.in_logic_thread()
148        del active  # Unused.
149
150    @final
151    def sign_in(
152        self,
153        result_cb: Callable[[LoginAdapter, SignInResult | Exception], None],
154        description: str,
155    ) -> None:
156        """Attempt to sign in via this adapter.
157
158        This can be called even if the back-end is not implicitly signed in;
159        the adapter will attempt to sign in if possible. An exception will
160        be returned if the sign-in attempt fails.
161        """
162
163        assert _babase.in_logic_thread()
164
165        # Have been seeing multiple sign-in attempts come through
166        # nearly simultaneously which can be problematic server-side.
167        # Let's error if a sign-in attempt is made within a few seconds
168        # of the last one to try and address this.
169        now = time.monotonic()
170        appnow = _babase.apptime()
171        if self._last_sign_in_time is not None:
172            since_last = now - self._last_sign_in_time
173            if since_last < 1.0:
174                logging.warning(
175                    'LoginAdapter: %s adapter sign_in() called too soon'
176                    ' (%.2fs) after last; this-desc="%s", last-desc="%s",'
177                    ' ba-app-time=%.2f.',
178                    self.login_type.name,
179                    since_last,
180                    description,
181                    self._last_sign_in_desc,
182                    appnow,
183                )
184                _babase.pushcall(
185                    partial(
186                        result_cb,
187                        self,
188                        RuntimeError('sign_in called too soon after last.'),
189                    )
190                )
191                return
192
193        self._last_sign_in_desc = description
194        self._last_sign_in_time = now
195
196        logger.debug(
197            '%s adapter sign_in() called; fetching sign-in-token...',
198            self.login_type.name,
199        )
200
201        def _got_sign_in_token_result(result: str | None) -> None:
202            import bacommon.cloud
203
204            # Failed to get a sign-in-token.
205            if result is None:
206                logger.debug(
207                    '%s adapter sign-in-token fetch failed;'
208                    ' aborting sign-in.',
209                    self.login_type.name,
210                )
211                _babase.pushcall(
212                    partial(
213                        result_cb,
214                        self,
215                        RuntimeError('fetch-sign-in-token failed.'),
216                    )
217                )
218                return
219
220            # Got a sign-in token! Now pass it to the cloud which will use
221            # it to verify our identity and give us app credentials on
222            # success.
223            logger.debug(
224                '%s adapter sign-in-token fetch succeeded;'
225                ' passing to cloud for verification...',
226                self.login_type.name,
227            )
228
229            def _got_sign_in_response(
230                response: bacommon.cloud.SignInResponse | Exception,
231            ) -> None:
232                # This likely means we couldn't communicate with the server.
233                if isinstance(response, Exception):
234                    logger.debug(
235                        '%s adapter got error sign-in response: %s',
236                        self.login_type.name,
237                        response,
238                    )
239                    _babase.pushcall(partial(result_cb, self, response))
240                else:
241                    # This means our credentials were explicitly rejected.
242                    if response.credentials is None:
243                        result2: LoginAdapter.SignInResult | Exception = (
244                            RuntimeError('Sign-in-token was rejected.')
245                        )
246                    else:
247                        logger.debug(
248                            '%s adapter got successful sign-in response',
249                            self.login_type.name,
250                        )
251                        result2 = self.SignInResult(
252                            credentials=response.credentials
253                        )
254                    _babase.pushcall(partial(result_cb, self, result2))
255
256            assert _babase.app.plus is not None
257            _babase.app.plus.cloud.send_message_cb(
258                bacommon.cloud.SignInMessage(
259                    self.login_type,
260                    result,
261                    description=description,
262                    apptime=appnow,
263                ),
264                on_response=_got_sign_in_response,
265            )
266
267        # Kick off the sign-in process by fetching a sign-in token.
268        self.get_sign_in_token(completion_cb=_got_sign_in_token_result)
269
270    def is_back_end_active(self) -> bool:
271        """Is this adapter's back-end currently active?"""
272        return self._back_end_active
273
274    def get_sign_in_token(
275        self, completion_cb: Callable[[str | None], None]
276    ) -> None:
277        """Get a sign-in token from the adapter back end.
278
279        This token is then passed to the master-server to complete the
280        sign-in process. The adapter can use this opportunity to bring
281        up account creation UI, call its internal sign_in function, etc.
282        as needed. The provided completion_cb should then be called with
283        either a token or None if sign in failed or was cancelled.
284        """
285
286        # Default implementation simply fails immediately.
287        _babase.pushcall(partial(completion_cb, None))
288
289    def _update_implicit_login_state(self) -> None:
290        # If we've received an implicit login state, schedule it to be
291        # sent along to the app. We wait until on-app-loading has been
292        # called so that account-client-v2 has had a chance to load
293        # any existing state so it can properly respond to this.
294        if self._implicit_login_state_dirty and self._on_app_loading_called:
295
296            logger.debug(
297                '%s adapter sending implicit-state-changed to app.',
298                self.login_type.name,
299            )
300
301            assert _babase.app.plus is not None
302            _babase.pushcall(
303                partial(
304                    _babase.app.plus.accounts.on_implicit_login_state_changed,
305                    self.login_type,
306                    self._implicit_login_state,
307                )
308            )
309            self._implicit_login_state_dirty = False
310
311    def _update_back_end_active(self) -> None:
312        was_active = self._back_end_active
313        if self._implicit_login_state is None:
314            is_active = False
315        else:
316            is_active = (
317                self._implicit_login_state.login_id == self._active_login_id
318            )
319        if was_active != is_active:
320            logger.debug(
321                '%s adapter back-end-active is now %s.',
322                self.login_type.name,
323                is_active,
324            )
325            self.on_back_end_active_change(is_active)
326            self._back_end_active = is_active

Allows using implicit login types in an explicit way.

Some login types such as Google Play Game Services or Game Center are basically always present and often do not provide a way to log out from within a running app, so this adapter exists to use them in a flexible manner by 'attaching' and 'detaching' from an always-present login, allowing for its use alongside other login types. It also provides common functionality for server-side account verification and other handy bits.

LoginAdapter(login_type: bacommon.login.LoginType)
56    def __init__(self, login_type: LoginType):
57        assert _babase.in_logic_thread()
58        self.login_type = login_type
59        self._implicit_login_state: LoginAdapter.ImplicitLoginState | None = (
60            None
61        )
62        self._on_app_loading_called = False
63        self._implicit_login_state_dirty = False
64        self._back_end_active = False
65
66        # Which login of our type (if any) is associated with the
67        # current active primary account.
68        self._active_login_id: str | None = None
69
70        self._last_sign_in_time: float | None = None
71        self._last_sign_in_desc: str | None = None
login_type
def on_app_loading(self) -> None:
73    def on_app_loading(self) -> None:
74        """Should be called for each adapter in on_app_loading."""
75
76        assert not self._on_app_loading_called
77        self._on_app_loading_called = True
78
79        # Any implicit state we received up until now needs to be pushed
80        # to the app account subsystem.
81        self._update_implicit_login_state()

Should be called for each adapter in on_app_loading.

def set_implicit_login_state( self, state: LoginAdapter.ImplicitLoginState | None) -> None:
 83    def set_implicit_login_state(
 84        self, state: ImplicitLoginState | None
 85    ) -> None:
 86        """Keep the adapter informed of implicit login states.
 87
 88        This should be called by the adapter back-end when an account
 89        of their associated type gets logged in or out.
 90        """
 91        assert _babase.in_logic_thread()
 92
 93        # Ignore redundant sets.
 94        if state == self._implicit_login_state:
 95            return
 96
 97        if state is None:
 98            logger.debug(
 99                '%s implicit state changed; now signed out.',
100                self.login_type.name,
101            )
102        else:
103            logger.debug(
104                '%s implicit state changed; now signed in as %s.',
105                self.login_type.name,
106                state.display_name,
107            )
108
109        self._implicit_login_state = state
110        self._implicit_login_state_dirty = True
111
112        # (possibly) push it to the app for handling.
113        self._update_implicit_login_state()
114
115        # This might affect whether we consider that back-end as 'active'.
116        self._update_back_end_active()

Keep the adapter informed of implicit login states.

This should be called by the adapter back-end when an account of their associated type gets logged in or out.

def set_active_logins(self, logins: dict[bacommon.login.LoginType, str]) -> None:
118    def set_active_logins(self, logins: dict[LoginType, str]) -> None:
119        """Keep the adapter informed of actively used logins.
120
121        This should be called by the app's account subsystem to
122        keep adapters up to date on the full set of logins attached
123        to the currently-in-use account.
124        Note that the logins dict passed in should be immutable as
125        only a reference to it is stored, not a copy.
126        """
127        assert _babase.in_logic_thread()
128        logger.debug(
129            '%s adapter got active logins %s.',
130            self.login_type.name,
131            {k: v[:4] + '...' + v[-4:] for k, v in logins.items()},
132        )
133
134        self._active_login_id = logins.get(self.login_type)
135        self._update_back_end_active()

Keep the adapter informed of actively used logins.

This should be called by the app's account subsystem to keep adapters up to date on the full set of logins attached to the currently-in-use account. Note that the logins dict passed in should be immutable as only a reference to it is stored, not a copy.

def on_back_end_active_change(self, active: bool) -> None:
137    def on_back_end_active_change(self, active: bool) -> None:
138        """Called when active state for the back-end is (possibly) changing.
139
140        Meant to be overridden by subclasses.
141        Being active means that the implicit login provided by the back-end
142        is actually being used by the app. It should therefore register
143        unlocked achievements, leaderboard scores, allow viewing native
144        UIs, etc. When not active it should ignore everything and behave
145        as if signed out, even if it technically is still signed in.
146        """
147        assert _babase.in_logic_thread()
148        del active  # Unused.

Called when active state for the back-end is (possibly) changing.

Meant to be overridden by subclasses. Being active means that the implicit login provided by the back-end is actually being used by the app. It should therefore register unlocked achievements, leaderboard scores, allow viewing native UIs, etc. When not active it should ignore everything and behave as if signed out, even if it technically is still signed in.

@final
def sign_in( self, result_cb: Callable[[LoginAdapter, LoginAdapter.SignInResult | Exception], NoneType], description: str) -> None:
150    @final
151    def sign_in(
152        self,
153        result_cb: Callable[[LoginAdapter, SignInResult | Exception], None],
154        description: str,
155    ) -> None:
156        """Attempt to sign in via this adapter.
157
158        This can be called even if the back-end is not implicitly signed in;
159        the adapter will attempt to sign in if possible. An exception will
160        be returned if the sign-in attempt fails.
161        """
162
163        assert _babase.in_logic_thread()
164
165        # Have been seeing multiple sign-in attempts come through
166        # nearly simultaneously which can be problematic server-side.
167        # Let's error if a sign-in attempt is made within a few seconds
168        # of the last one to try and address this.
169        now = time.monotonic()
170        appnow = _babase.apptime()
171        if self._last_sign_in_time is not None:
172            since_last = now - self._last_sign_in_time
173            if since_last < 1.0:
174                logging.warning(
175                    'LoginAdapter: %s adapter sign_in() called too soon'
176                    ' (%.2fs) after last; this-desc="%s", last-desc="%s",'
177                    ' ba-app-time=%.2f.',
178                    self.login_type.name,
179                    since_last,
180                    description,
181                    self._last_sign_in_desc,
182                    appnow,
183                )
184                _babase.pushcall(
185                    partial(
186                        result_cb,
187                        self,
188                        RuntimeError('sign_in called too soon after last.'),
189                    )
190                )
191                return
192
193        self._last_sign_in_desc = description
194        self._last_sign_in_time = now
195
196        logger.debug(
197            '%s adapter sign_in() called; fetching sign-in-token...',
198            self.login_type.name,
199        )
200
201        def _got_sign_in_token_result(result: str | None) -> None:
202            import bacommon.cloud
203
204            # Failed to get a sign-in-token.
205            if result is None:
206                logger.debug(
207                    '%s adapter sign-in-token fetch failed;'
208                    ' aborting sign-in.',
209                    self.login_type.name,
210                )
211                _babase.pushcall(
212                    partial(
213                        result_cb,
214                        self,
215                        RuntimeError('fetch-sign-in-token failed.'),
216                    )
217                )
218                return
219
220            # Got a sign-in token! Now pass it to the cloud which will use
221            # it to verify our identity and give us app credentials on
222            # success.
223            logger.debug(
224                '%s adapter sign-in-token fetch succeeded;'
225                ' passing to cloud for verification...',
226                self.login_type.name,
227            )
228
229            def _got_sign_in_response(
230                response: bacommon.cloud.SignInResponse | Exception,
231            ) -> None:
232                # This likely means we couldn't communicate with the server.
233                if isinstance(response, Exception):
234                    logger.debug(
235                        '%s adapter got error sign-in response: %s',
236                        self.login_type.name,
237                        response,
238                    )
239                    _babase.pushcall(partial(result_cb, self, response))
240                else:
241                    # This means our credentials were explicitly rejected.
242                    if response.credentials is None:
243                        result2: LoginAdapter.SignInResult | Exception = (
244                            RuntimeError('Sign-in-token was rejected.')
245                        )
246                    else:
247                        logger.debug(
248                            '%s adapter got successful sign-in response',
249                            self.login_type.name,
250                        )
251                        result2 = self.SignInResult(
252                            credentials=response.credentials
253                        )
254                    _babase.pushcall(partial(result_cb, self, result2))
255
256            assert _babase.app.plus is not None
257            _babase.app.plus.cloud.send_message_cb(
258                bacommon.cloud.SignInMessage(
259                    self.login_type,
260                    result,
261                    description=description,
262                    apptime=appnow,
263                ),
264                on_response=_got_sign_in_response,
265            )
266
267        # Kick off the sign-in process by fetching a sign-in token.
268        self.get_sign_in_token(completion_cb=_got_sign_in_token_result)

Attempt to sign in via this adapter.

This can be called even if the back-end is not implicitly signed in; the adapter will attempt to sign in if possible. An exception will be returned if the sign-in attempt fails.

def is_back_end_active(self) -> bool:
270    def is_back_end_active(self) -> bool:
271        """Is this adapter's back-end currently active?"""
272        return self._back_end_active

Is this adapter's back-end currently active?

def get_sign_in_token(self, completion_cb: Callable[[str | None], NoneType]) -> None:
274    def get_sign_in_token(
275        self, completion_cb: Callable[[str | None], None]
276    ) -> None:
277        """Get a sign-in token from the adapter back end.
278
279        This token is then passed to the master-server to complete the
280        sign-in process. The adapter can use this opportunity to bring
281        up account creation UI, call its internal sign_in function, etc.
282        as needed. The provided completion_cb should then be called with
283        either a token or None if sign in failed or was cancelled.
284        """
285
286        # Default implementation simply fails immediately.
287        _babase.pushcall(partial(completion_cb, None))

Get a sign-in token from the adapter back end.

This token is then passed to the master-server to complete the sign-in process. The adapter can use this opportunity to bring up account creation UI, call its internal sign_in function, etc. as needed. The provided completion_cb should then be called with either a token or None if sign in failed or was cancelled.

@dataclass
class LoginAdapter.SignInResult:
43    @dataclass
44    class SignInResult:
45        """Describes the final result of a sign-in attempt."""
46
47        credentials: str

Describes the final result of a sign-in attempt.

LoginAdapter.SignInResult(credentials: str)
credentials: str
@dataclass
class LoginAdapter.ImplicitLoginState:
49    @dataclass
50    class ImplicitLoginState:
51        """Describes the current state of an implicit login."""
52
53        login_id: str
54        display_name: str

Describes the current state of an implicit login.

LoginAdapter.ImplicitLoginState(login_id: str, display_name: str)
login_id: str
display_name: str
@dataclass
class LoginInfo:
24@dataclass
25class LoginInfo:
26    """Basic info about a login available in the app.plus.accounts section."""
27
28    name: str

Basic info about a login available in the app.plus.accounts section.

LoginInfo(name: str)
name: str
class Lstr:
491class Lstr:
492    """Used to define strings in a language-independent way.
493
494    Category: **General Utility Classes**
495
496    These should be used whenever possible in place of hard-coded
497    strings so that in-game or UI elements show up correctly on all
498    clients in their currently-active language.
499
500    To see available resource keys, look at any of the bs_language_*.py
501    files in the game or the translations pages at
502    legacy.ballistica.net/translate.
503
504    ##### Examples
505    EXAMPLE 1: specify a string from a resource path
506    >>> mynode.text = babase.Lstr(resource='audioSettingsWindow.titleText')
507
508    EXAMPLE 2: specify a translated string via a category and english
509    value; if a translated value is available, it will be used; otherwise
510    the english value will be. To see available translation categories,
511    look under the 'translations' resource section.
512    >>> mynode.text = babase.Lstr(translate=('gameDescriptions',
513    ...                                  'Defeat all enemies'))
514
515    EXAMPLE 3: specify a raw value and some substitutions. Substitutions
516    can be used with resource and translate modes as well.
517    >>> mynode.text = babase.Lstr(value='${A} / ${B}',
518    ...               subs=[('${A}', str(score)), ('${B}', str(total))])
519
520    EXAMPLE 4: babase.Lstr's can be nested. This example would display the
521    resource at res_a but replace ${NAME} with the value of the
522    resource at res_b
523    >>> mytextnode.text = babase.Lstr(
524    ...     resource='res_a',
525    ...     subs=[('${NAME}', babase.Lstr(resource='res_b'))])
526    """
527
528    # pylint: disable=dangerous-default-value
529    # noinspection PyDefaultArgument
530    @overload
531    def __init__(
532        self,
533        *,
534        resource: str,
535        fallback_resource: str = '',
536        fallback_value: str = '',
537        subs: Sequence[tuple[str, str | Lstr]] = [],
538    ) -> None:
539        """Create an Lstr from a string resource."""
540
541    # noinspection PyShadowingNames,PyDefaultArgument
542    @overload
543    def __init__(
544        self,
545        *,
546        translate: tuple[str, str],
547        subs: Sequence[tuple[str, str | Lstr]] = [],
548    ) -> None:
549        """Create an Lstr by translating a string in a category."""
550
551    # noinspection PyDefaultArgument
552    @overload
553    def __init__(
554        self, *, value: str, subs: Sequence[tuple[str, str | Lstr]] = []
555    ) -> None:
556        """Create an Lstr from a raw string value."""
557
558    # pylint: enable=redefined-outer-name, dangerous-default-value
559
560    def __init__(self, *args: Any, **keywds: Any) -> None:
561        """Instantiate a Lstr.
562
563        Pass a value for either 'resource', 'translate',
564        or 'value'. (see Lstr help for examples).
565        'subs' can be a sequence of 2-member sequences consisting of values
566        and replacements.
567        'fallback_resource' can be a resource key that will be used if the
568        main one is not present for
569        the current language in place of falling back to the english value
570        ('resource' mode only).
571        'fallback_value' can be a literal string that will be used if neither
572        the resource nor the fallback resource is found ('resource' mode only).
573        """
574        # pylint: disable=too-many-branches
575        if args:
576            raise TypeError('Lstr accepts only keyword arguments')
577
578        # Basically just store the exact args they passed.
579        # However if they passed any Lstr values for subs,
580        # replace them with that Lstr's dict.
581        self.args = keywds
582        our_type = type(self)
583
584        if isinstance(self.args.get('value'), our_type):
585            raise TypeError("'value' must be a regular string; not an Lstr")
586
587        if 'subs' in self.args:
588            subs_new = []
589            for key, value in keywds['subs']:
590                if isinstance(value, our_type):
591                    subs_new.append((key, value.args))
592                else:
593                    subs_new.append((key, value))
594            self.args['subs'] = subs_new
595
596        # As of protocol 31 we support compact key names
597        # ('t' instead of 'translate', etc). Convert as needed.
598        if 'translate' in keywds:
599            keywds['t'] = keywds['translate']
600            del keywds['translate']
601        if 'resource' in keywds:
602            keywds['r'] = keywds['resource']
603            del keywds['resource']
604        if 'value' in keywds:
605            keywds['v'] = keywds['value']
606            del keywds['value']
607        if 'fallback' in keywds:
608            from babase import _error
609
610            _error.print_error(
611                'deprecated "fallback" arg passed to Lstr(); use '
612                'either "fallback_resource" or "fallback_value"',
613                once=True,
614            )
615            keywds['f'] = keywds['fallback']
616            del keywds['fallback']
617        if 'fallback_resource' in keywds:
618            keywds['f'] = keywds['fallback_resource']
619            del keywds['fallback_resource']
620        if 'subs' in keywds:
621            keywds['s'] = keywds['subs']
622            del keywds['subs']
623        if 'fallback_value' in keywds:
624            keywds['fv'] = keywds['fallback_value']
625            del keywds['fallback_value']
626
627    def evaluate(self) -> str:
628        """Evaluate the Lstr and returns a flat string in the current language.
629
630        You should avoid doing this as much as possible and instead pass
631        and store Lstr values.
632        """
633        return _babase.evaluate_lstr(self._get_json())
634
635    def is_flat_value(self) -> bool:
636        """Return whether the Lstr is a 'flat' value.
637
638        This is defined as a simple string value incorporating no
639        translations, resources, or substitutions. In this case it may
640        be reasonable to replace it with a raw string value, perform
641        string manipulation on it, etc.
642        """
643        return bool('v' in self.args and not self.args.get('s', []))
644
645    def _get_json(self) -> str:
646        try:
647            return json.dumps(self.args, separators=(',', ':'))
648        except Exception:
649            from babase import _error
650
651            _error.print_exception('_get_json failed for', self.args)
652            return 'JSON_ERR'
653
654    @override
655    def __str__(self) -> str:
656        return '<ba.Lstr: ' + self._get_json() + '>'
657
658    @override
659    def __repr__(self) -> str:
660        return '<ba.Lstr: ' + self._get_json() + '>'
661
662    @staticmethod
663    def from_json(json_string: str) -> babase.Lstr:
664        """Given a json string, returns a babase.Lstr. Does no validation."""
665        lstr = Lstr(value='')
666        lstr.args = json.loads(json_string)
667        return lstr

Used to define strings in a language-independent way.

Category: General Utility Classes

These should be used whenever possible in place of hard-coded strings so that in-game or UI elements show up correctly on all clients in their currently-active language.

To see available resource keys, look at any of the bs_language_*.py files in the game or the translations pages at legacy.ballistica.net/translate.

Examples

EXAMPLE 1: specify a string from a resource path

>>> mynode.text = babase.Lstr(resource='audioSettingsWindow.titleText')

EXAMPLE 2: specify a translated string via a category and english value; if a translated value is available, it will be used; otherwise the english value will be. To see available translation categories, look under the 'translations' resource section.

>>> mynode.text = babase.Lstr(translate=('gameDescriptions',
...                                  'Defeat all enemies'))

EXAMPLE 3: specify a raw value and some substitutions. Substitutions can be used with resource and translate modes as well.

>>> mynode.text = babase.Lstr(value='${A} / ${B}',
...               subs=[('${A}', str(score)), ('${B}', str(total))])

EXAMPLE 4: babase.Lstr's can be nested. This example would display the resource at res_a but replace ${NAME} with the value of the resource at res_b

>>> mytextnode.text = babase.Lstr(
...     resource='res_a',
...     subs=[('${NAME}', babase.Lstr(resource='res_b'))])
Lstr(*args: Any, **keywds: Any)
560    def __init__(self, *args: Any, **keywds: Any) -> None:
561        """Instantiate a Lstr.
562
563        Pass a value for either 'resource', 'translate',
564        or 'value'. (see Lstr help for examples).
565        'subs' can be a sequence of 2-member sequences consisting of values
566        and replacements.
567        'fallback_resource' can be a resource key that will be used if the
568        main one is not present for
569        the current language in place of falling back to the english value
570        ('resource' mode only).
571        'fallback_value' can be a literal string that will be used if neither
572        the resource nor the fallback resource is found ('resource' mode only).
573        """
574        # pylint: disable=too-many-branches
575        if args:
576            raise TypeError('Lstr accepts only keyword arguments')
577
578        # Basically just store the exact args they passed.
579        # However if they passed any Lstr values for subs,
580        # replace them with that Lstr's dict.
581        self.args = keywds
582        our_type = type(self)
583
584        if isinstance(self.args.get('value'), our_type):
585            raise TypeError("'value' must be a regular string; not an Lstr")
586
587        if 'subs' in self.args:
588            subs_new = []
589            for key, value in keywds['subs']:
590                if isinstance(value, our_type):
591                    subs_new.append((key, value.args))
592                else:
593                    subs_new.append((key, value))
594            self.args['subs'] = subs_new
595
596        # As of protocol 31 we support compact key names
597        # ('t' instead of 'translate', etc). Convert as needed.
598        if 'translate' in keywds:
599            keywds['t'] = keywds['translate']
600            del keywds['translate']
601        if 'resource' in keywds:
602            keywds['r'] = keywds['resource']
603            del keywds['resource']
604        if 'value' in keywds:
605            keywds['v'] = keywds['value']
606            del keywds['value']
607        if 'fallback' in keywds:
608            from babase import _error
609
610            _error.print_error(
611                'deprecated "fallback" arg passed to Lstr(); use '
612                'either "fallback_resource" or "fallback_value"',
613                once=True,
614            )
615            keywds['f'] = keywds['fallback']
616            del keywds['fallback']
617        if 'fallback_resource' in keywds:
618            keywds['f'] = keywds['fallback_resource']
619            del keywds['fallback_resource']
620        if 'subs' in keywds:
621            keywds['s'] = keywds['subs']
622            del keywds['subs']
623        if 'fallback_value' in keywds:
624            keywds['fv'] = keywds['fallback_value']
625            del keywds['fallback_value']

Instantiate a Lstr.

Pass a value for either 'resource', 'translate', or 'value'. (see Lstr help for examples). 'subs' can be a sequence of 2-member sequences consisting of values and replacements. 'fallback_resource' can be a resource key that will be used if the main one is not present for the current language in place of falling back to the english value ('resource' mode only). 'fallback_value' can be a literal string that will be used if neither the resource nor the fallback resource is found ('resource' mode only).

args
def evaluate(self) -> str:
627    def evaluate(self) -> str:
628        """Evaluate the Lstr and returns a flat string in the current language.
629
630        You should avoid doing this as much as possible and instead pass
631        and store Lstr values.
632        """
633        return _babase.evaluate_lstr(self._get_json())

Evaluate the Lstr and returns a flat string in the current language.

You should avoid doing this as much as possible and instead pass and store Lstr values.

def is_flat_value(self) -> bool:
635    def is_flat_value(self) -> bool:
636        """Return whether the Lstr is a 'flat' value.
637
638        This is defined as a simple string value incorporating no
639        translations, resources, or substitutions. In this case it may
640        be reasonable to replace it with a raw string value, perform
641        string manipulation on it, etc.
642        """
643        return bool('v' in self.args and not self.args.get('s', []))

Return whether the Lstr is a 'flat' value.

This is defined as a simple string value incorporating no translations, resources, or substitutions. In this case it may be reasonable to replace it with a raw string value, perform string manipulation on it, etc.

@staticmethod
def from_json(json_string: str) -> Lstr:
662    @staticmethod
663    def from_json(json_string: str) -> babase.Lstr:
664        """Given a json string, returns a babase.Lstr. Does no validation."""
665        lstr = Lstr(value='')
666        lstr.args = json.loads(json_string)
667        return lstr

Given a json string, returns a babase.Lstr. Does no validation.

class MainWindow(bauiv1.Window):
 49class MainWindow(Window):
 50    """A special window that can be used as a main window."""
 51
 52    def __init__(
 53        self,
 54        root_widget: bauiv1.Widget,
 55        transition: str | None,
 56        origin_widget: bauiv1.Widget | None,
 57        cleanupcheck: bool = True,
 58    ):
 59        """Create a MainWindow given a root widget and transition info.
 60
 61        Automatically handles in and out transitions on the provided widget,
 62        so there is no need to set transitions when creating it.
 63        """
 64        # A back-state supplied by the ui system.
 65        self.main_window_back_state: MainWindowState | None = None
 66
 67        self.main_window_is_top_level: bool = False
 68
 69        # Windows can be flagged as auxiliary when not related to the
 70        # main UI task at hand. UI code may choose to handle auxiliary
 71        # windows in special ways, such as by implicitly replacing
 72        # existing auxiliary windows with new ones instead of keeping
 73        # old ones as back targets.
 74        self.main_window_is_auxiliary: bool = False
 75
 76        self._main_window_transition = transition
 77        self._main_window_origin_widget = origin_widget
 78        super().__init__(root_widget, cleanupcheck)
 79
 80        scale_origin: tuple[float, float] | None
 81        if origin_widget is not None:
 82            self._main_window_transition_out = 'out_scale'
 83            scale_origin = origin_widget.get_screen_space_center()
 84            transition = 'in_scale'
 85        else:
 86            self._main_window_transition_out = 'out_right'
 87            scale_origin = None
 88        _bauiv1.containerwidget(
 89            edit=root_widget,
 90            transition=transition,
 91            scale_origin_stack_offset=scale_origin,
 92        )
 93
 94    def main_window_close(self, transition: str | None = None) -> None:
 95        """Get window transitioning out if still alive."""
 96
 97        # no-op if our underlying widget is dead or on its way out.
 98        if not self._root_widget or self._root_widget.transitioning_out:
 99            return
100
101        # Transition ourself out.
102        try:
103            self.on_main_window_close()
104        except Exception:
105            logging.exception('Error in on_main_window_close() for %s.', self)
106
107        # Note: normally transition of None means instant, but we use
108        # that to mean 'do the default' so we support a special
109        # 'instant' string..
110        if transition == 'instant':
111            self._root_widget.delete()
112        else:
113            _bauiv1.containerwidget(
114                edit=self._root_widget,
115                transition=(
116                    self._main_window_transition_out
117                    if transition is None
118                    else transition
119                ),
120            )
121
122    def main_window_has_control(self) -> bool:
123        """Is this MainWindow allowed to change the global main window?
124
125        It is a good idea to make sure this is True before calling
126        main_window_replace(). This prevents fluke UI breakage such as
127        multiple simultaneous events causing a MainWindow to spawn
128        multiple replacements for itself.
129        """
130        # We are allowed to change main windows if we are the current one
131        # AND our underlying widget is still alive and not transitioning out.
132        return (
133            babase.app.ui_v1.get_main_window() is self
134            and bool(self._root_widget)
135            and not self._root_widget.transitioning_out
136        )
137
138    def main_window_back(self) -> None:
139        """Move back in the main window stack.
140
141        Is a no-op if the main window does not have control;
142        no need to check main_window_has_control() first.
143        """
144
145        # Users should always check main_window_has_control() before
146        # calling us. Error if it seems they did not.
147        if not self.main_window_has_control():
148            return
149
150        if not self.main_window_is_top_level:
151
152            # Get the 'back' window coming in.
153            babase.app.ui_v1.auto_set_back_window(self)
154
155        self.main_window_close()
156
157    def main_window_replace(
158        self,
159        new_window: MainWindow,
160        back_state: MainWindowState | None = None,
161        is_auxiliary: bool = False,
162    ) -> None:
163        """Replace ourself with a new MainWindow."""
164
165        # Users should always check main_window_has_control() *before*
166        # creating new MainWindows and passing them in here. Kill the
167        # passed window and Error if it seems they did not.
168        if not self.main_window_has_control():
169            new_window.get_root_widget().delete()
170            raise RuntimeError(
171                f'main_window_replace() called on a not-in-control window'
172                f' ({self}); always check main_window_has_control() before'
173                f' calling main_window_replace().'
174            )
175
176        # Just shove the old out the left to give the feel that we're
177        # adding to the nav stack.
178        transition = 'out_left'
179
180        # Transition ourself out.
181        try:
182            self.on_main_window_close()
183        except Exception:
184            logging.exception('Error in on_main_window_close() for %s.', self)
185
186        _bauiv1.containerwidget(edit=self._root_widget, transition=transition)
187        babase.app.ui_v1.set_main_window(
188            new_window,
189            from_window=self,
190            back_state=back_state,
191            is_auxiliary=is_auxiliary,
192            suppress_warning=True,
193        )
194
195    def on_main_window_close(self) -> None:
196        """Called before transitioning out a main window.
197
198        A good opportunity to save window state/etc.
199        """
200
201    def get_main_window_state(self) -> MainWindowState:
202        """Return a WindowState to recreate this window, if supported."""
203        raise NotImplementedError()

A special window that can be used as a main window.

MainWindow( root_widget: _bauiv1.Widget, transition: str | None, origin_widget: _bauiv1.Widget | None, cleanupcheck: bool = True)
52    def __init__(
53        self,
54        root_widget: bauiv1.Widget,
55        transition: str | None,
56        origin_widget: bauiv1.Widget | None,
57        cleanupcheck: bool = True,
58    ):
59        """Create a MainWindow given a root widget and transition info.
60
61        Automatically handles in and out transitions on the provided widget,
62        so there is no need to set transitions when creating it.
63        """
64        # A back-state supplied by the ui system.
65        self.main_window_back_state: MainWindowState | None = None
66
67        self.main_window_is_top_level: bool = False
68
69        # Windows can be flagged as auxiliary when not related to the
70        # main UI task at hand. UI code may choose to handle auxiliary
71        # windows in special ways, such as by implicitly replacing
72        # existing auxiliary windows with new ones instead of keeping
73        # old ones as back targets.
74        self.main_window_is_auxiliary: bool = False
75
76        self._main_window_transition = transition
77        self._main_window_origin_widget = origin_widget
78        super().__init__(root_widget, cleanupcheck)
79
80        scale_origin: tuple[float, float] | None
81        if origin_widget is not None:
82            self._main_window_transition_out = 'out_scale'
83            scale_origin = origin_widget.get_screen_space_center()
84            transition = 'in_scale'
85        else:
86            self._main_window_transition_out = 'out_right'
87            scale_origin = None
88        _bauiv1.containerwidget(
89            edit=root_widget,
90            transition=transition,
91            scale_origin_stack_offset=scale_origin,
92        )

Create a MainWindow given a root widget and transition info.

Automatically handles in and out transitions on the provided widget, so there is no need to set transitions when creating it.

main_window_back_state: MainWindowState | None
main_window_is_top_level: bool
main_window_is_auxiliary: bool
def main_window_close(self, transition: str | None = None) -> None:
 94    def main_window_close(self, transition: str | None = None) -> None:
 95        """Get window transitioning out if still alive."""
 96
 97        # no-op if our underlying widget is dead or on its way out.
 98        if not self._root_widget or self._root_widget.transitioning_out:
 99            return
100
101        # Transition ourself out.
102        try:
103            self.on_main_window_close()
104        except Exception:
105            logging.exception('Error in on_main_window_close() for %s.', self)
106
107        # Note: normally transition of None means instant, but we use
108        # that to mean 'do the default' so we support a special
109        # 'instant' string..
110        if transition == 'instant':
111            self._root_widget.delete()
112        else:
113            _bauiv1.containerwidget(
114                edit=self._root_widget,
115                transition=(
116                    self._main_window_transition_out
117                    if transition is None
118                    else transition
119                ),
120            )

Get window transitioning out if still alive.

def main_window_has_control(self) -> bool:
122    def main_window_has_control(self) -> bool:
123        """Is this MainWindow allowed to change the global main window?
124
125        It is a good idea to make sure this is True before calling
126        main_window_replace(). This prevents fluke UI breakage such as
127        multiple simultaneous events causing a MainWindow to spawn
128        multiple replacements for itself.
129        """
130        # We are allowed to change main windows if we are the current one
131        # AND our underlying widget is still alive and not transitioning out.
132        return (
133            babase.app.ui_v1.get_main_window() is self
134            and bool(self._root_widget)
135            and not self._root_widget.transitioning_out
136        )

Is this MainWindow allowed to change the global main window?

It is a good idea to make sure this is True before calling main_window_replace(). This prevents fluke UI breakage such as multiple simultaneous events causing a MainWindow to spawn multiple replacements for itself.

def main_window_back(self) -> None:
138    def main_window_back(self) -> None:
139        """Move back in the main window stack.
140
141        Is a no-op if the main window does not have control;
142        no need to check main_window_has_control() first.
143        """
144
145        # Users should always check main_window_has_control() before
146        # calling us. Error if it seems they did not.
147        if not self.main_window_has_control():
148            return
149
150        if not self.main_window_is_top_level:
151
152            # Get the 'back' window coming in.
153            babase.app.ui_v1.auto_set_back_window(self)
154
155        self.main_window_close()

Move back in the main window stack.

Is a no-op if the main window does not have control; no need to check main_window_has_control() first.

def main_window_replace( self, new_window: MainWindow, back_state: MainWindowState | None = None, is_auxiliary: bool = False) -> None:
157    def main_window_replace(
158        self,
159        new_window: MainWindow,
160        back_state: MainWindowState | None = None,
161        is_auxiliary: bool = False,
162    ) -> None:
163        """Replace ourself with a new MainWindow."""
164
165        # Users should always check main_window_has_control() *before*
166        # creating new MainWindows and passing them in here. Kill the
167        # passed window and Error if it seems they did not.
168        if not self.main_window_has_control():
169            new_window.get_root_widget().delete()
170            raise RuntimeError(
171                f'main_window_replace() called on a not-in-control window'
172                f' ({self}); always check main_window_has_control() before'
173                f' calling main_window_replace().'
174            )
175
176        # Just shove the old out the left to give the feel that we're
177        # adding to the nav stack.
178        transition = 'out_left'
179
180        # Transition ourself out.
181        try:
182            self.on_main_window_close()
183        except Exception:
184            logging.exception('Error in on_main_window_close() for %s.', self)
185
186        _bauiv1.containerwidget(edit=self._root_widget, transition=transition)
187        babase.app.ui_v1.set_main_window(
188            new_window,
189            from_window=self,
190            back_state=back_state,
191            is_auxiliary=is_auxiliary,
192            suppress_warning=True,
193        )

Replace ourself with a new MainWindow.

def on_main_window_close(self) -> None:
195    def on_main_window_close(self) -> None:
196        """Called before transitioning out a main window.
197
198        A good opportunity to save window state/etc.
199        """

Called before transitioning out a main window.

A good opportunity to save window state/etc.

def get_main_window_state(self) -> MainWindowState:
201    def get_main_window_state(self) -> MainWindowState:
202        """Return a WindowState to recreate this window, if supported."""
203        raise NotImplementedError()

Return a WindowState to recreate this window, if supported.

class MainWindowState:
206class MainWindowState:
207    """Persistent state for a specific main-window and its ancestors.
208
209    This allows MainWindows to be automatically recreated for back-button
210    purposes, when switching app-modes, etc.
211    """
212
213    def __init__(self) -> None:
214        # The window that back/cancel navigation should take us to.
215        self.parent: MainWindowState | None = None
216        self.is_top_level: bool | None = None
217        self.is_auxiliary: bool | None = None
218        self.window_type: type[MainWindow] | None = None
219
220    def create_window(
221        self,
222        transition: Literal['in_right', 'in_left', 'in_scale'] | None = None,
223        origin_widget: bauiv1.Widget | None = None,
224    ) -> MainWindow:
225        """Create a window based on this state.
226
227        WindowState child classes should override this to recreate their
228        particular type of window.
229        """
230        raise NotImplementedError()

Persistent state for a specific main-window and its ancestors.

This allows MainWindows to be automatically recreated for back-button purposes, when switching app-modes, etc.

parent: MainWindowState | None
is_top_level: bool | None
is_auxiliary: bool | None
window_type: type[MainWindow] | None
def create_window( self, transition: Optional[Literal['in_right', 'in_left', 'in_scale']] = None, origin_widget: _bauiv1.Widget | None = None) -> MainWindow:
220    def create_window(
221        self,
222        transition: Literal['in_right', 'in_left', 'in_scale'] | None = None,
223        origin_widget: bauiv1.Widget | None = None,
224    ) -> MainWindow:
225        """Create a window based on this state.
226
227        WindowState child classes should override this to recreate their
228        particular type of window.
229        """
230        raise NotImplementedError()

Create a window based on this state.

WindowState child classes should override this to recreate their particular type of window.

class Mesh:
51class Mesh:
52    """Category: **User Interface Classes**"""
53
54    pass

Category: User Interface Classes

class NotFoundError(builtins.Exception):
26class NotFoundError(Exception):
27    """Exception raised when a referenced object does not exist.
28
29    Category: **Exception Classes**
30    """

Exception raised when a referenced object does not exist.

Category: Exception Classes

def open_url(address: str, force_fallback: bool = False) -> None:
1329def open_url(address: str, force_fallback: bool = False) -> None:
1330    """Open the provided URL.
1331
1332    Category: **General Utility Functions**
1333
1334    Attempts to open the provided url in a web-browser. If that is not
1335    possible (or force_fallback is True), instead displays the url as
1336    a string and/or qrcode.
1337    """
1338    return None

Open the provided URL.

Category: General Utility Functions

Attempts to open the provided url in a web-browser. If that is not possible (or force_fallback is True), instead displays the url as a string and/or qrcode.

def overlay_web_browser_close() -> bool:
1341def overlay_web_browser_close() -> bool:
1342    """Close any open overlay web browser.
1343
1344    Category: **General Utility Functions**
1345    """
1346    return bool()

Close any open overlay web browser.

Category: General Utility Functions

def overlay_web_browser_is_open() -> bool:
1349def overlay_web_browser_is_open() -> bool:
1350    """Return whether an overlay web browser is open currently.
1351
1352    Category: **General Utility Functions**
1353    """
1354    return bool()

Return whether an overlay web browser is open currently.

Category: General Utility Functions

def overlay_web_browser_is_supported() -> bool:
1357def overlay_web_browser_is_supported() -> bool:
1358    """Return whether an overlay web browser is supported here.
1359
1360    Category: **General Utility Functions**
1361
1362    An overlay web browser is a small dialog that pops up over the top
1363    of the main engine window. It can be used for performing simple
1364    tasks such as sign-ins.
1365    """
1366    return bool()

Return whether an overlay web browser is supported here.

Category: General Utility Functions

An overlay web browser is a small dialog that pops up over the top of the main engine window. It can be used for performing simple tasks such as sign-ins.

def overlay_web_browser_open_url(address: str) -> None:
1369def overlay_web_browser_open_url(address: str) -> None:
1370    """Open the provided URL in an overlayw web browser.
1371
1372    Category: **General Utility Functions**
1373
1374    An overlay web browser is a small dialog that pops up over the top
1375    of the main engine window. It can be used for performing simple
1376    tasks such as sign-ins.
1377    """
1378    return None

Open the provided URL in an overlayw web browser.

Category: General Utility Functions

An overlay web browser is a small dialog that pops up over the top of the main engine window. It can be used for performing simple tasks such as sign-ins.

class Permission(enum.Enum):
89class Permission(Enum):
90    """Permissions that can be requested from the OS.
91
92    Category: Enums
93    """
94
95    STORAGE = 0

Permissions that can be requested from the OS.

Category: Enums

STORAGE = <Permission.STORAGE: 0>
class Plugin:
322class Plugin:
323    """A plugin to alter app behavior in some way.
324
325    Category: **App Classes**
326
327    Plugins are discoverable by the meta-tag system
328    and the user can select which ones they want to enable.
329    Enabled plugins are then called at specific times as the
330    app is running in order to modify its behavior in some way.
331    """
332
333    def on_app_running(self) -> None:
334        """Called when the app reaches the running state."""
335
336    def on_app_suspend(self) -> None:
337        """Called when the app enters the suspended state."""
338
339    def on_app_unsuspend(self) -> None:
340        """Called when the app exits the suspended state."""
341
342    def on_app_shutdown(self) -> None:
343        """Called when the app is beginning the shutdown process."""
344
345    def on_app_shutdown_complete(self) -> None:
346        """Called when the app has completed the shutdown process."""
347
348    def has_settings_ui(self) -> bool:
349        """Called to ask if we have settings UI we can show."""
350        return False
351
352    def show_settings_ui(self, source_widget: Any | None) -> None:
353        """Called to show our settings UI."""

A plugin to alter app behavior in some way.

Category: App Classes

Plugins are discoverable by the meta-tag system and the user can select which ones they want to enable. Enabled plugins are then called at specific times as the app is running in order to modify its behavior in some way.

def on_app_running(self) -> None:
333    def on_app_running(self) -> None:
334        """Called when the app reaches the running state."""

Called when the app reaches the running state.

def on_app_suspend(self) -> None:
336    def on_app_suspend(self) -> None:
337        """Called when the app enters the suspended state."""

Called when the app enters the suspended state.

def on_app_unsuspend(self) -> None:
339    def on_app_unsuspend(self) -> None:
340        """Called when the app exits the suspended state."""

Called when the app exits the suspended state.

def on_app_shutdown(self) -> None:
342    def on_app_shutdown(self) -> None:
343        """Called when the app is beginning the shutdown process."""

Called when the app is beginning the shutdown process.

def on_app_shutdown_complete(self) -> None:
345    def on_app_shutdown_complete(self) -> None:
346        """Called when the app has completed the shutdown process."""

Called when the app has completed the shutdown process.

def has_settings_ui(self) -> bool:
348    def has_settings_ui(self) -> bool:
349        """Called to ask if we have settings UI we can show."""
350        return False

Called to ask if we have settings UI we can show.

def show_settings_ui(self, source_widget: typing.Any | None) -> None:
352    def show_settings_ui(self, source_widget: Any | None) -> None:
353        """Called to show our settings UI."""

Called to show our settings UI.

class PluginSpec:
227class PluginSpec:
228    """Represents a plugin the engine knows about.
229
230    Category: **App Classes**
231
232    The 'enabled' attr represents whether this plugin is set to load.
233    Getting or setting that attr affects the corresponding app-config
234    key. Remember to commit the app-config after making any changes.
235
236    The 'attempted_load' attr will be True if the engine has attempted
237    to load the plugin. If 'attempted_load' is True for a PluginSpec
238    but the 'plugin' attr is None, it means there was an error loading
239    the plugin. If a plugin's api-version does not match the running
240    app, if a new plugin is detected with auto-enable-plugins disabled,
241    or if the user has explicitly disabled a plugin, the engine will not
242    even attempt to load it.
243    """
244
245    def __init__(self, class_path: str, loadable: bool):
246        self.class_path = class_path
247        self.loadable = loadable
248        self.attempted_load = False
249        self.plugin: Plugin | None = None
250
251    @property
252    def enabled(self) -> bool:
253        """Whether the user wants this plugin to load."""
254        plugstates: dict[str, dict] = _babase.app.config.get('Plugins', {})
255        assert isinstance(plugstates, dict)
256        val = plugstates.get(self.class_path, {}).get('enabled', False) is True
257        return val
258
259    @enabled.setter
260    def enabled(self, val: bool) -> None:
261        plugstates: dict[str, dict] = _babase.app.config.setdefault(
262            'Plugins', {}
263        )
264        assert isinstance(plugstates, dict)
265        plugstate = plugstates.setdefault(self.class_path, {})
266        plugstate['enabled'] = val
267
268    def attempt_load_if_enabled(self) -> Plugin | None:
269        """Possibly load the plugin and log any errors."""
270        from babase._general import getclass
271        from babase._language import Lstr
272
273        assert not self.attempted_load
274        assert self.plugin is None
275
276        if not self.enabled:
277            return None
278        self.attempted_load = True
279        if not self.loadable:
280            return None
281        try:
282            cls = getclass(self.class_path, Plugin, True)
283        except Exception as exc:
284            _babase.getsimplesound('error').play()
285            _babase.screenmessage(
286                Lstr(
287                    resource='pluginClassLoadErrorText',
288                    subs=[
289                        ('${PLUGIN}', self.class_path),
290                        ('${ERROR}', str(exc)),
291                    ],
292                ),
293                color=(1, 0, 0),
294            )
295            logging.exception(
296                "Error loading plugin class '%s'.", self.class_path
297            )
298            return None
299        try:
300            self.plugin = cls()
301            return self.plugin
302        except Exception as exc:
303            from babase import _error
304
305            _babase.getsimplesound('error').play()
306            _babase.screenmessage(
307                Lstr(
308                    resource='pluginInitErrorText',
309                    subs=[
310                        ('${PLUGIN}', self.class_path),
311                        ('${ERROR}', str(exc)),
312                    ],
313                ),
314                color=(1, 0, 0),
315            )
316            logging.exception(
317                "Error initing plugin class: '%s'.", self.class_path
318            )
319        return None

Represents a plugin the engine knows about.

Category: App Classes

The 'enabled' attr represents whether this plugin is set to load. Getting or setting that attr affects the corresponding app-config key. Remember to commit the app-config after making any changes.

The 'attempted_load' attr will be True if the engine has attempted to load the plugin. If 'attempted_load' is True for a PluginSpec but the 'plugin' attr is None, it means there was an error loading the plugin. If a plugin's api-version does not match the running app, if a new plugin is detected with auto-enable-plugins disabled, or if the user has explicitly disabled a plugin, the engine will not even attempt to load it.

PluginSpec(class_path: str, loadable: bool)
245    def __init__(self, class_path: str, loadable: bool):
246        self.class_path = class_path
247        self.loadable = loadable
248        self.attempted_load = False
249        self.plugin: Plugin | None = None
class_path
loadable
attempted_load
plugin: Plugin | None
enabled: bool
251    @property
252    def enabled(self) -> bool:
253        """Whether the user wants this plugin to load."""
254        plugstates: dict[str, dict] = _babase.app.config.get('Plugins', {})
255        assert isinstance(plugstates, dict)
256        val = plugstates.get(self.class_path, {}).get('enabled', False) is True
257        return val

Whether the user wants this plugin to load.

def attempt_load_if_enabled(self) -> Plugin | None:
268    def attempt_load_if_enabled(self) -> Plugin | None:
269        """Possibly load the plugin and log any errors."""
270        from babase._general import getclass
271        from babase._language import Lstr
272
273        assert not self.attempted_load
274        assert self.plugin is None
275
276        if not self.enabled:
277            return None
278        self.attempted_load = True
279        if not self.loadable:
280            return None
281        try:
282            cls = getclass(self.class_path, Plugin, True)
283        except Exception as exc:
284            _babase.getsimplesound('error').play()
285            _babase.screenmessage(
286                Lstr(
287                    resource='pluginClassLoadErrorText',
288                    subs=[
289                        ('${PLUGIN}', self.class_path),
290                        ('${ERROR}', str(exc)),
291                    ],
292                ),
293                color=(1, 0, 0),
294            )
295            logging.exception(
296                "Error loading plugin class '%s'.", self.class_path
297            )
298            return None
299        try:
300            self.plugin = cls()
301            return self.plugin
302        except Exception as exc:
303            from babase import _error
304
305            _babase.getsimplesound('error').play()
306            _babase.screenmessage(
307                Lstr(
308                    resource='pluginInitErrorText',
309                    subs=[
310                        ('${PLUGIN}', self.class_path),
311                        ('${ERROR}', str(exc)),
312                    ],
313                ),
314                color=(1, 0, 0),
315            )
316            logging.exception(
317                "Error initing plugin class: '%s'.", self.class_path
318            )
319        return None

Possibly load the plugin and log any errors.

def pushcall( call: Callable, from_other_thread: bool = False, suppress_other_thread_warning: bool = False, other_thread_use_fg_context: bool = False, raw: bool = False) -> None:
1413def pushcall(
1414    call: Callable,
1415    from_other_thread: bool = False,
1416    suppress_other_thread_warning: bool = False,
1417    other_thread_use_fg_context: bool = False,
1418    raw: bool = False,
1419) -> None:
1420    """Push a call to the logic event-loop.
1421    Category: **General Utility Functions**
1422
1423    This call expects to be used in the logic thread, and will automatically
1424    save and restore the babase.Context to behave seamlessly.
1425
1426    If you want to push a call from outside of the logic thread,
1427    however, you can pass 'from_other_thread' as True. In this case
1428    the call will always run in the UI context_ref on the logic thread
1429    or whichever context_ref is in the foreground if
1430    other_thread_use_fg_context is True.
1431    Passing raw=True will disable thread checks and context_ref sets/restores.
1432    """
1433    return None

Push a call to the logic event-loop. Category: General Utility Functions

This call expects to be used in the logic thread, and will automatically save and restore the babase.Context to behave seamlessly.

If you want to push a call from outside of the logic thread, however, you can pass 'from_other_thread' as True. In this case the call will always run in the UI context_ref on the logic thread or whichever context_ref is in the foreground if other_thread_use_fg_context is True. Passing raw=True will disable thread checks and context_ref sets/restores.

def quit( confirm: bool = False, quit_type: QuitType | None = None) -> None:
1437def quit(
1438    confirm: bool = False, quit_type: babase.QuitType | None = None
1439) -> None:
1440    """Quit the app.
1441
1442    Category: **General Utility Functions**
1443
1444    If 'confirm' is True, a confirm dialog will be presented if conditions
1445    allow; otherwise the quit will still be immediate.
1446    See docs for babase.QuitType for explanations of the optional
1447    'quit_type' arg.
1448    """
1449    return None

Quit the app.

Category: General Utility Functions

If 'confirm' is True, a confirm dialog will be presented if conditions allow; otherwise the quit will still be immediate. See docs for babase.QuitType for explanations of the optional 'quit_type' arg.

class QuitType(enum.Enum):
42class QuitType(Enum):
43    """Types of input a controller can send to the game.
44
45    Category: Enums
46
47    'soft' may hide/reset the app but keep the process running, depending
48       on the platform.
49
50    'back' is a variant of 'soft' which may give 'back-button-pressed'
51       behavior depending on the platform. (returning to some previous
52       activity instead of dumping to the home screen, etc.)
53
54    'hard' leads to the process exiting. This generally should be avoided
55       on platforms such as mobile.
56    """
57
58    SOFT = 0
59    BACK = 1
60    HARD = 2

Types of input a controller can send to the game.

Category: Enums

'soft' may hide/reset the app but keep the process running, depending on the platform.

'back' is a variant of 'soft' which may give 'back-button-pressed' behavior depending on the platform. (returning to some previous activity instead of dumping to the home screen, etc.)

'hard' leads to the process exiting. This generally should be avoided on platforms such as mobile.

SOFT = <QuitType.SOFT: 0>
BACK = <QuitType.BACK: 1>
HARD = <QuitType.HARD: 2>
def rowwidget( edit: _bauiv1.Widget | None = None, parent: _bauiv1.Widget | None = None, size: Optional[Sequence[float]] = None, position: Optional[Sequence[float]] = None, background: bool | None = None, selected_child: _bauiv1.Widget | None = None, visible_child: _bauiv1.Widget | None = None, claims_left_right: bool | None = None, claims_tab: bool | None = None, selection_loops_to_parent: bool | None = None) -> _bauiv1.Widget:
462def rowwidget(
463    edit: bauiv1.Widget | None = None,
464    parent: bauiv1.Widget | None = None,
465    size: Sequence[float] | None = None,
466    position: Sequence[float] | None = None,
467    background: bool | None = None,
468    selected_child: bauiv1.Widget | None = None,
469    visible_child: bauiv1.Widget | None = None,
470    claims_left_right: bool | None = None,
471    claims_tab: bool | None = None,
472    selection_loops_to_parent: bool | None = None,
473) -> bauiv1.Widget:
474    """Create or edit a row widget.
475
476    Category: **User Interface Functions**
477
478    Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise
479    a new one is created and returned. Arguments that are not set to None
480    are applied to the Widget.
481    """
482    import bauiv1  # pylint: disable=cyclic-import
483
484    return bauiv1.Widget()

Create or edit a row widget.

Category: User Interface Functions

Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise a new one is created and returned. Arguments that are not set to None are applied to the Widget.

def safecolor( color: Sequence[float], target_intensity: float = 0.6) -> tuple[float, ...]:
1487def safecolor(
1488    color: Sequence[float], target_intensity: float = 0.6
1489) -> tuple[float, ...]:
1490    """Given a color tuple, return a color safe to display as text.
1491
1492    Category: **General Utility Functions**
1493
1494    Accepts tuples of length 3 or 4. This will slightly brighten very
1495    dark colors, etc.
1496    """
1497    return (0.0, 0.0, 0.0)

Given a color tuple, return a color safe to display as text.

Category: General Utility Functions

Accepts tuples of length 3 or 4. This will slightly brighten very dark colors, etc.

def screenmessage( message: str | Lstr, color: Optional[Sequence[float]] = None, log: bool = False) -> None:
1500def screenmessage(
1501    message: str | babase.Lstr,
1502    color: Sequence[float] | None = None,
1503    log: bool = False,
1504) -> None:
1505    """Print a message to the local client's screen, in a given color.
1506
1507    Category: **General Utility Functions**
1508
1509    Note that this version of the function is purely for local display.
1510    To broadcast screen messages in network play, look for methods such as
1511    broadcastmessage() provided by the scene-version packages.
1512    """
1513    return None

Print a message to the local client's screen, in a given color.

Category: General Utility Functions

Note that this version of the function is purely for local display. To broadcast screen messages in network play, look for methods such as broadcastmessage() provided by the scene-version packages.

def scrollwidget( *, edit: _bauiv1.Widget | None = None, parent: _bauiv1.Widget | None = None, size: Optional[Sequence[float]] = None, position: Optional[Sequence[float]] = None, background: bool | None = None, selected_child: _bauiv1.Widget | None = None, capture_arrows: bool = False, on_select_call: Optional[Callable] = None, center_small_content: bool | None = None, color: Optional[Sequence[float]] = None, highlight: bool | None = None, border_opacity: float | None = None, simple_culling_v: float | None = None, selection_loops_to_parent: bool | None = None, claims_left_right: bool | None = None, claims_up_down: bool | None = None, claims_tab: bool | None = None, autoselect: bool | None = None) -> _bauiv1.Widget:
487def scrollwidget(
488    *,
489    edit: bauiv1.Widget | None = None,
490    parent: bauiv1.Widget | None = None,
491    size: Sequence[float] | None = None,
492    position: Sequence[float] | None = None,
493    background: bool | None = None,
494    selected_child: bauiv1.Widget | None = None,
495    capture_arrows: bool = False,
496    on_select_call: Callable | None = None,
497    center_small_content: bool | None = None,
498    color: Sequence[float] | None = None,
499    highlight: bool | None = None,
500    border_opacity: float | None = None,
501    simple_culling_v: float | None = None,
502    selection_loops_to_parent: bool | None = None,
503    claims_left_right: bool | None = None,
504    claims_up_down: bool | None = None,
505    claims_tab: bool | None = None,
506    autoselect: bool | None = None,
507) -> bauiv1.Widget:
508    """Create or edit a scroll widget.
509
510    Category: **User Interface Functions**
511
512    Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise
513    a new one is created and returned. Arguments that are not set to None
514    are applied to the Widget.
515    """
516    import bauiv1  # pylint: disable=cyclic-import
517
518    return bauiv1.Widget()

Create or edit a scroll widget.

Category: User Interface Functions

Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise a new one is created and returned. Arguments that are not set to None are applied to the Widget.

def set_analytics_screen(screen: str) -> None:
1516def set_analytics_screen(screen: str) -> None:
1517    """Used for analytics to see where in the app players spend their time.
1518
1519    Category: **General Utility Functions**
1520
1521    Generally called when opening a new window or entering some UI.
1522    'screen' should be a string description of an app location
1523    ('Main Menu', etc.)
1524    """
1525    return None

Used for analytics to see where in the app players spend their time.

Category: General Utility Functions

Generally called when opening a new window or entering some UI. 'screen' should be a string description of an app location ('Main Menu', etc.)

class Sound:
57class Sound:
58    """Category: **User Interface Classes**"""
59
60    def play(self) -> None:
61        """Play the sound locally."""
62        return None
63
64    def stop(self) -> None:
65        """Stop the sound if it is playing."""
66        return None

Category: User Interface Classes

def play(self) -> None:
60    def play(self) -> None:
61        """Play the sound locally."""
62        return None

Play the sound locally.

def stop(self) -> None:
64    def stop(self) -> None:
65        """Stop the sound if it is playing."""
66        return None

Stop the sound if it is playing.

class SpecialChar(enum.Enum):
 98class SpecialChar(Enum):
 99    """Special characters the game can print.
100
101    Category: Enums
102    """
103
104    DOWN_ARROW = 0
105    UP_ARROW = 1
106    LEFT_ARROW = 2
107    RIGHT_ARROW = 3
108    TOP_BUTTON = 4
109    LEFT_BUTTON = 5
110    RIGHT_BUTTON = 6
111    BOTTOM_BUTTON = 7
112    DELETE = 8
113    SHIFT = 9
114    BACK = 10
115    LOGO_FLAT = 11
116    REWIND_BUTTON = 12
117    PLAY_PAUSE_BUTTON = 13
118    FAST_FORWARD_BUTTON = 14
119    DPAD_CENTER_BUTTON = 15
120    PLAY_STATION_CROSS_BUTTON = 16
121    PLAY_STATION_CIRCLE_BUTTON = 17
122    PLAY_STATION_TRIANGLE_BUTTON = 18
123    PLAY_STATION_SQUARE_BUTTON = 19
124    PLAY_BUTTON = 20
125    PAUSE_BUTTON = 21
126    OUYA_BUTTON_O = 22
127    OUYA_BUTTON_U = 23
128    OUYA_BUTTON_Y = 24
129    OUYA_BUTTON_A = 25
130    TOKEN = 26
131    LOGO = 27
132    TICKET = 28
133    GOOGLE_PLAY_GAMES_LOGO = 29
134    GAME_CENTER_LOGO = 30
135    DICE_BUTTON1 = 31
136    DICE_BUTTON2 = 32
137    DICE_BUTTON3 = 33
138    DICE_BUTTON4 = 34
139    GAME_CIRCLE_LOGO = 35
140    PARTY_ICON = 36
141    TEST_ACCOUNT = 37
142    TICKET_BACKING = 38
143    TROPHY1 = 39
144    TROPHY2 = 40
145    TROPHY3 = 41
146    TROPHY0A = 42
147    TROPHY0B = 43
148    TROPHY4 = 44
149    LOCAL_ACCOUNT = 45
150    EXPLODINARY_LOGO = 46
151    FLAG_UNITED_STATES = 47
152    FLAG_MEXICO = 48
153    FLAG_GERMANY = 49
154    FLAG_BRAZIL = 50
155    FLAG_RUSSIA = 51
156    FLAG_CHINA = 52
157    FLAG_UNITED_KINGDOM = 53
158    FLAG_CANADA = 54
159    FLAG_INDIA = 55
160    FLAG_JAPAN = 56
161    FLAG_FRANCE = 57
162    FLAG_INDONESIA = 58
163    FLAG_ITALY = 59
164    FLAG_SOUTH_KOREA = 60
165    FLAG_NETHERLANDS = 61
166    FEDORA = 62
167    HAL = 63
168    CROWN = 64
169    YIN_YANG = 65
170    EYE_BALL = 66
171    SKULL = 67
172    HEART = 68
173    DRAGON = 69
174    HELMET = 70
175    MUSHROOM = 71
176    NINJA_STAR = 72
177    VIKING_HELMET = 73
178    MOON = 74
179    SPIDER = 75
180    FIREBALL = 76
181    FLAG_UNITED_ARAB_EMIRATES = 77
182    FLAG_QATAR = 78
183    FLAG_EGYPT = 79
184    FLAG_KUWAIT = 80
185    FLAG_ALGERIA = 81
186    FLAG_SAUDI_ARABIA = 82
187    FLAG_MALAYSIA = 83
188    FLAG_CZECH_REPUBLIC = 84
189    FLAG_AUSTRALIA = 85
190    FLAG_SINGAPORE = 86
191    OCULUS_LOGO = 87
192    STEAM_LOGO = 88
193    NVIDIA_LOGO = 89
194    FLAG_IRAN = 90
195    FLAG_POLAND = 91
196    FLAG_ARGENTINA = 92
197    FLAG_PHILIPPINES = 93
198    FLAG_CHILE = 94
199    MIKIROG = 95
200    V2_LOGO = 96

Special characters the game can print.

Category: Enums

DOWN_ARROW = <SpecialChar.DOWN_ARROW: 0>
UP_ARROW = <SpecialChar.UP_ARROW: 1>
LEFT_ARROW = <SpecialChar.LEFT_ARROW: 2>
RIGHT_ARROW = <SpecialChar.RIGHT_ARROW: 3>
TOP_BUTTON = <SpecialChar.TOP_BUTTON: 4>
LEFT_BUTTON = <SpecialChar.LEFT_BUTTON: 5>
RIGHT_BUTTON = <SpecialChar.RIGHT_BUTTON: 6>
BOTTOM_BUTTON = <SpecialChar.BOTTOM_BUTTON: 7>
DELETE = <SpecialChar.DELETE: 8>
SHIFT = <SpecialChar.SHIFT: 9>
BACK = <SpecialChar.BACK: 10>
LOGO_FLAT = <SpecialChar.LOGO_FLAT: 11>
REWIND_BUTTON = <SpecialChar.REWIND_BUTTON: 12>
PLAY_PAUSE_BUTTON = <SpecialChar.PLAY_PAUSE_BUTTON: 13>
FAST_FORWARD_BUTTON = <SpecialChar.FAST_FORWARD_BUTTON: 14>
DPAD_CENTER_BUTTON = <SpecialChar.DPAD_CENTER_BUTTON: 15>
PLAY_STATION_CROSS_BUTTON = <SpecialChar.PLAY_STATION_CROSS_BUTTON: 16>
PLAY_STATION_CIRCLE_BUTTON = <SpecialChar.PLAY_STATION_CIRCLE_BUTTON: 17>
PLAY_STATION_TRIANGLE_BUTTON = <SpecialChar.PLAY_STATION_TRIANGLE_BUTTON: 18>
PLAY_STATION_SQUARE_BUTTON = <SpecialChar.PLAY_STATION_SQUARE_BUTTON: 19>
PLAY_BUTTON = <SpecialChar.PLAY_BUTTON: 20>
PAUSE_BUTTON = <SpecialChar.PAUSE_BUTTON: 21>
OUYA_BUTTON_O = <SpecialChar.OUYA_BUTTON_O: 22>
OUYA_BUTTON_U = <SpecialChar.OUYA_BUTTON_U: 23>
OUYA_BUTTON_Y = <SpecialChar.OUYA_BUTTON_Y: 24>
OUYA_BUTTON_A = <SpecialChar.OUYA_BUTTON_A: 25>
TOKEN = <SpecialChar.TOKEN: 26>
TICKET = <SpecialChar.TICKET: 28>
DICE_BUTTON1 = <SpecialChar.DICE_BUTTON1: 31>
DICE_BUTTON2 = <SpecialChar.DICE_BUTTON2: 32>
DICE_BUTTON3 = <SpecialChar.DICE_BUTTON3: 33>
DICE_BUTTON4 = <SpecialChar.DICE_BUTTON4: 34>
PARTY_ICON = <SpecialChar.PARTY_ICON: 36>
TEST_ACCOUNT = <SpecialChar.TEST_ACCOUNT: 37>
TICKET_BACKING = <SpecialChar.TICKET_BACKING: 38>
TROPHY1 = <SpecialChar.TROPHY1: 39>
TROPHY2 = <SpecialChar.TROPHY2: 40>
TROPHY3 = <SpecialChar.TROPHY3: 41>
TROPHY0A = <SpecialChar.TROPHY0A: 42>
TROPHY0B = <SpecialChar.TROPHY0B: 43>
TROPHY4 = <SpecialChar.TROPHY4: 44>
LOCAL_ACCOUNT = <SpecialChar.LOCAL_ACCOUNT: 45>
FLAG_UNITED_STATES = <SpecialChar.FLAG_UNITED_STATES: 47>
FLAG_MEXICO = <SpecialChar.FLAG_MEXICO: 48>
FLAG_GERMANY = <SpecialChar.FLAG_GERMANY: 49>
FLAG_BRAZIL = <SpecialChar.FLAG_BRAZIL: 50>
FLAG_RUSSIA = <SpecialChar.FLAG_RUSSIA: 51>
FLAG_CHINA = <SpecialChar.FLAG_CHINA: 52>
FLAG_UNITED_KINGDOM = <SpecialChar.FLAG_UNITED_KINGDOM: 53>
FLAG_CANADA = <SpecialChar.FLAG_CANADA: 54>
FLAG_INDIA = <SpecialChar.FLAG_INDIA: 55>
FLAG_JAPAN = <SpecialChar.FLAG_JAPAN: 56>
FLAG_FRANCE = <SpecialChar.FLAG_FRANCE: 57>
FLAG_INDONESIA = <SpecialChar.FLAG_INDONESIA: 58>
FLAG_ITALY = <SpecialChar.FLAG_ITALY: 59>
FLAG_SOUTH_KOREA = <SpecialChar.FLAG_SOUTH_KOREA: 60>
FLAG_NETHERLANDS = <SpecialChar.FLAG_NETHERLANDS: 61>
FEDORA = <SpecialChar.FEDORA: 62>
HAL = <SpecialChar.HAL: 63>
CROWN = <SpecialChar.CROWN: 64>
YIN_YANG = <SpecialChar.YIN_YANG: 65>
EYE_BALL = <SpecialChar.EYE_BALL: 66>
SKULL = <SpecialChar.SKULL: 67>
HEART = <SpecialChar.HEART: 68>
DRAGON = <SpecialChar.DRAGON: 69>
HELMET = <SpecialChar.HELMET: 70>
MUSHROOM = <SpecialChar.MUSHROOM: 71>
NINJA_STAR = <SpecialChar.NINJA_STAR: 72>
VIKING_HELMET = <SpecialChar.VIKING_HELMET: 73>
MOON = <SpecialChar.MOON: 74>
SPIDER = <SpecialChar.SPIDER: 75>
FIREBALL = <SpecialChar.FIREBALL: 76>
FLAG_UNITED_ARAB_EMIRATES = <SpecialChar.FLAG_UNITED_ARAB_EMIRATES: 77>
FLAG_QATAR = <SpecialChar.FLAG_QATAR: 78>
FLAG_EGYPT = <SpecialChar.FLAG_EGYPT: 79>
FLAG_KUWAIT = <SpecialChar.FLAG_KUWAIT: 80>
FLAG_ALGERIA = <SpecialChar.FLAG_ALGERIA: 81>
FLAG_SAUDI_ARABIA = <SpecialChar.FLAG_SAUDI_ARABIA: 82>
FLAG_MALAYSIA = <SpecialChar.FLAG_MALAYSIA: 83>
FLAG_CZECH_REPUBLIC = <SpecialChar.FLAG_CZECH_REPUBLIC: 84>
FLAG_AUSTRALIA = <SpecialChar.FLAG_AUSTRALIA: 85>
FLAG_SINGAPORE = <SpecialChar.FLAG_SINGAPORE: 86>
FLAG_IRAN = <SpecialChar.FLAG_IRAN: 90>
FLAG_POLAND = <SpecialChar.FLAG_POLAND: 91>
FLAG_ARGENTINA = <SpecialChar.FLAG_ARGENTINA: 92>
FLAG_PHILIPPINES = <SpecialChar.FLAG_PHILIPPINES: 93>
FLAG_CHILE = <SpecialChar.FLAG_CHILE: 94>
MIKIROG = <SpecialChar.MIKIROG: 95>
class Texture:
69class Texture:
70    """Category: **User Interface Classes**"""
71
72    pass

Category: User Interface Classes

def textwidget( *, edit: _bauiv1.Widget | None = None, parent: _bauiv1.Widget | None = None, size: Optional[Sequence[float]] = None, position: Optional[Sequence[float]] = None, text: str | Lstr | None = None, v_align: str | None = None, h_align: str | None = None, editable: bool | None = None, padding: float | None = None, on_return_press_call: Optional[Callable[[], NoneType]] = None, on_activate_call: Optional[Callable[[], NoneType]] = None, selectable: bool | None = None, query: _bauiv1.Widget | None = None, max_chars: int | None = None, color: Optional[Sequence[float]] = None, click_activate: bool | None = None, on_select_call: Optional[Callable[[], NoneType]] = None, always_highlight: bool | None = None, draw_controller: _bauiv1.Widget | None = None, scale: float | None = None, corner_scale: float | None = None, description: str | Lstr | None = None, transition_delay: float | None = None, maxwidth: float | None = None, max_height: float | None = None, flatness: float | None = None, shadow: float | None = None, autoselect: bool | None = None, rotate: float | None = None, enabled: bool | None = None, force_internal_editing: bool | None = None, always_show_carat: bool | None = None, big: bool | None = None, extra_touch_border_scale: float | None = None, res_scale: float | None = None, query_max_chars: _bauiv1.Widget | None = None, query_description: _bauiv1.Widget | None = None, adapter_finished: bool | None = None, glow_type: str | None = None, allow_clear_button: bool | None = None) -> _bauiv1.Widget:
526def textwidget(
527    *,
528    edit: bauiv1.Widget | None = None,
529    parent: bauiv1.Widget | None = None,
530    size: Sequence[float] | None = None,
531    position: Sequence[float] | None = None,
532    text: str | bauiv1.Lstr | None = None,
533    v_align: str | None = None,
534    h_align: str | None = None,
535    editable: bool | None = None,
536    padding: float | None = None,
537    on_return_press_call: Callable[[], None] | None = None,
538    on_activate_call: Callable[[], None] | None = None,
539    selectable: bool | None = None,
540    query: bauiv1.Widget | None = None,
541    max_chars: int | None = None,
542    color: Sequence[float] | None = None,
543    click_activate: bool | None = None,
544    on_select_call: Callable[[], None] | None = None,
545    always_highlight: bool | None = None,
546    draw_controller: bauiv1.Widget | None = None,
547    scale: float | None = None,
548    corner_scale: float | None = None,
549    description: str | bauiv1.Lstr | None = None,
550    transition_delay: float | None = None,
551    maxwidth: float | None = None,
552    max_height: float | None = None,
553    flatness: float | None = None,
554    shadow: float | None = None,
555    autoselect: bool | None = None,
556    rotate: float | None = None,
557    enabled: bool | None = None,
558    force_internal_editing: bool | None = None,
559    always_show_carat: bool | None = None,
560    big: bool | None = None,
561    extra_touch_border_scale: float | None = None,
562    res_scale: float | None = None,
563    query_max_chars: bauiv1.Widget | None = None,
564    query_description: bauiv1.Widget | None = None,
565    adapter_finished: bool | None = None,
566    glow_type: str | None = None,
567    allow_clear_button: bool | None = None,
568) -> bauiv1.Widget:
569    """Create or edit a text widget.
570
571    Category: **User Interface Functions**
572
573    Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise
574    a new one is created and returned. Arguments that are not set to None
575    are applied to the Widget.
576    """
577    import bauiv1  # pylint: disable=cyclic-import
578
579    return bauiv1.Widget()

Create or edit a text widget.

Category: User Interface Functions

Pass a valid existing bauiv1.Widget as 'edit' to modify it; otherwise a new one is created and returned. Arguments that are not set to None are applied to the Widget.

def timestring(timeval: float | int, centi: bool = True) -> Lstr:
15def timestring(
16    timeval: float | int,
17    centi: bool = True,
18) -> babase.Lstr:
19    """Generate a babase.Lstr for displaying a time value.
20
21    Category: **General Utility Functions**
22
23    Given a time value, returns a babase.Lstr with:
24    (hours if > 0 ) : minutes : seconds : (centiseconds if centi=True).
25
26    WARNING: the underlying Lstr value is somewhat large so don't use this
27    to rapidly update Node text values for an onscreen timer or you may
28    consume significant network bandwidth.  For that purpose you should
29    use a 'timedisplay' Node and attribute connections.
30
31    """
32    from babase._language import Lstr
33
34    # We take float seconds but operate on int milliseconds internally.
35    timeval = int(1000 * timeval)
36    bits = []
37    subs = []
38    hval = (timeval // 1000) // (60 * 60)
39    if hval != 0:
40        bits.append('${H}')
41        subs.append(
42            (
43                '${H}',
44                Lstr(
45                    resource='timeSuffixHoursText',
46                    subs=[('${COUNT}', str(hval))],
47                ),
48            )
49        )
50    mval = ((timeval // 1000) // 60) % 60
51    if mval != 0:
52        bits.append('${M}')
53        subs.append(
54            (
55                '${M}',
56                Lstr(
57                    resource='timeSuffixMinutesText',
58                    subs=[('${COUNT}', str(mval))],
59                ),
60            )
61        )
62
63    # We add seconds if its non-zero *or* we haven't added anything else.
64    if centi:
65        # pylint: disable=consider-using-f-string
66        sval = timeval / 1000.0 % 60.0
67        if sval >= 0.005 or not bits:
68            bits.append('${S}')
69            subs.append(
70                (
71                    '${S}',
72                    Lstr(
73                        resource='timeSuffixSecondsText',
74                        subs=[('${COUNT}', ('%.2f' % sval))],
75                    ),
76                )
77            )
78    else:
79        sval = timeval // 1000 % 60
80        if sval != 0 or not bits:
81            bits.append('${S}')
82            subs.append(
83                (
84                    '${S}',
85                    Lstr(
86                        resource='timeSuffixSecondsText',
87                        subs=[('${COUNT}', str(sval))],
88                    ),
89                )
90            )
91    return Lstr(value=' '.join(bits), subs=subs)

Generate a babase.Lstr for displaying a time value.

Category: General Utility Functions

Given a time value, returns a babase.Lstr with: (hours if > 0 ) : minutes : seconds : (centiseconds if centi=True).

WARNING: the underlying Lstr value is somewhat large so don't use this to rapidly update Node text values for an onscreen timer or you may consume significant network bandwidth. For that purpose you should use a 'timedisplay' Node and attribute connections.

def uicleanupcheck(obj: Any, widget: _bauiv1.Widget) -> None:
267def uicleanupcheck(obj: Any, widget: bauiv1.Widget) -> None:
268    """Checks to ensure a widget-owning object gets cleaned up properly.
269
270    Category: User Interface Functions
271
272    This adds a check which will print an error message if the provided
273    object still exists ~5 seconds after the provided bauiv1.Widget dies.
274
275    This is a good sanity check for any sort of object that wraps or
276    controls a bauiv1.Widget. For instance, a 'Window' class instance has
277    no reason to still exist once its root container bauiv1.Widget has fully
278    transitioned out and been destroyed. Circular references or careless
279    strong referencing can lead to such objects never getting destroyed,
280    however, and this helps detect such cases to avoid memory leaks.
281    """
282    if DEBUG_UI_CLEANUP_CHECKS:
283        print(f'adding uicleanup to {obj}')
284    if not isinstance(widget, _bauiv1.Widget):
285        raise TypeError('widget arg is not a bauiv1.Widget')
286
287    if bool(False):
288
289        def foobar() -> None:
290            """Just testing."""
291            if DEBUG_UI_CLEANUP_CHECKS:
292                print('uicleanupcheck widget dying...')
293
294        widget.add_delete_callback(foobar)
295
296    assert babase.app.classic is not None
297    babase.app.ui_v1.cleanupchecks.append(
298        UICleanupCheck(
299            obj=weakref.ref(obj), widget=widget, widget_death_time=None
300        )
301    )

Checks to ensure a widget-owning object gets cleaned up properly.

Category: User Interface Functions

This adds a check which will print an error message if the provided object still exists ~5 seconds after the provided bauiv1.Widget dies.

This is a good sanity check for any sort of object that wraps or controls a bauiv1.Widget. For instance, a 'Window' class instance has no reason to still exist once its root container bauiv1.Widget has fully transitioned out and been destroyed. Circular references or careless strong referencing can lead to such objects never getting destroyed, however, and this helps detect such cases to avoid memory leaks.

class UIScale(enum.Enum):
63class UIScale(Enum):
64    """The overall scale the UI is being rendered for. Note that this is
65    independent of pixel resolution. For example, a phone and a desktop PC
66    might render the game at similar pixel resolutions but the size they
67    display content at will vary significantly.
68
69    Category: Enums
70
71    'large' is used for devices such as desktop PCs where fine details can
72       be clearly seen. UI elements are generally smaller on the screen
73       and more content can be seen at once.
74
75    'medium' is used for devices such as tablets, TVs, or VR headsets.
76       This mode strikes a balance between clean readability and amount of
77       content visible.
78
79    'small' is used primarily for phones or other small devices where
80       content needs to be presented as large and clear in order to remain
81       readable from an average distance.
82    """
83
84    SMALL = 0
85    MEDIUM = 1
86    LARGE = 2

The overall scale the UI is being rendered for. Note that this is independent of pixel resolution. For example, a phone and a desktop PC might render the game at similar pixel resolutions but the size they display content at will vary significantly.

Category: Enums

'large' is used for devices such as desktop PCs where fine details can be clearly seen. UI elements are generally smaller on the screen and more content can be seen at once.

'medium' is used for devices such as tablets, TVs, or VR headsets. This mode strikes a balance between clean readability and amount of content visible.

'small' is used primarily for phones or other small devices where content needs to be presented as large and clear in order to remain readable from an average distance.

SMALL = <UIScale.SMALL: 0>
MEDIUM = <UIScale.MEDIUM: 1>
LARGE = <UIScale.LARGE: 2>
class UIV1AppSubsystem(babase._appsubsystem.AppSubsystem):
 32class UIV1AppSubsystem(babase.AppSubsystem):
 33    """Consolidated UI functionality for the app.
 34
 35    Category: **App Classes**
 36
 37    To use this class, access the single instance of it at 'ba.app.ui'.
 38    """
 39
 40    class RootUIElement(Enum):
 41        """Stuff provided by the root ui."""
 42
 43        MENU_BUTTON = 'menu_button'
 44        SQUAD_BUTTON = 'squad_button'
 45        ACCOUNT_BUTTON = 'account_button'
 46        SETTINGS_BUTTON = 'settings_button'
 47        INBOX_BUTTON = 'inbox_button'
 48        STORE_BUTTON = 'store_button'
 49        INVENTORY_BUTTON = 'inventory_button'
 50        ACHIEVEMENTS_BUTTON = 'achievements_button'
 51        GET_TOKENS_BUTTON = 'get_tokens_button'
 52        TICKETS_METER = 'tickets_meter'
 53        TOKENS_METER = 'tokens_meter'
 54        TROPHY_METER = 'trophy_meter'
 55        LEVEL_METER = 'level_meter'
 56        CHEST_SLOT_1 = 'chest_slot_1'
 57        CHEST_SLOT_2 = 'chest_slot_2'
 58        CHEST_SLOT_3 = 'chest_slot_3'
 59        CHEST_SLOT_4 = 'chest_slot_4'
 60
 61    def __init__(self) -> None:
 62        from bauiv1._uitypes import MainWindow
 63
 64        super().__init__()
 65
 66        # We hold only a weak ref to the current main Window; we want it
 67        # to be able to disappear on its own. That being said, we do
 68        # expect MainWindows to keep themselves alive until replaced by
 69        # another MainWindow and we complain if they don't.
 70        self._main_window = empty_weakref(MainWindow)
 71        self._main_window_widget: bauiv1.Widget | None = None
 72
 73        self.quit_window: bauiv1.Widget | None = None
 74
 75        # For storing arbitrary class-level state data for Windows or
 76        # other UI related classes.
 77        self.window_states: dict[type, Any] = {}
 78
 79        self._uiscale: babase.UIScale
 80        self._update_ui_scale()
 81
 82        self.cleanupchecks: list[UICleanupCheck] = []
 83        self.upkeeptimer: babase.AppTimer | None = None
 84
 85        self.title_color = (0.72, 0.7, 0.75)
 86        self.heading_color = (0.72, 0.7, 0.75)
 87        self.infotextcolor = (0.7, 0.9, 0.7)
 88
 89        # Elements in our root UI will call anything here when
 90        # activated.
 91        self.root_ui_calls: dict[
 92            UIV1AppSubsystem.RootUIElement, Callable[[], None]
 93        ] = {}
 94
 95    def _update_ui_scale(self) -> None:
 96        uiscalestr = babase.get_ui_scale()
 97        if uiscalestr == 'large':
 98            self._uiscale = babase.UIScale.LARGE
 99        elif uiscalestr == 'medium':
100            self._uiscale = babase.UIScale.MEDIUM
101        elif uiscalestr == 'small':
102            self._uiscale = babase.UIScale.SMALL
103        else:
104            logging.error("Invalid UIScale '%s'.", uiscalestr)
105            self._uiscale = babase.UIScale.MEDIUM
106
107    @property
108    def available(self) -> bool:
109        """Can uiv1 currently be used?
110
111        Code that may run in headless mode, before the UI has been spun up,
112        while other ui systems are active, etc. can check this to avoid
113        likely erroring.
114        """
115        return _bauiv1.is_available()
116
117    @override
118    def reset(self) -> None:
119        from bauiv1._uitypes import MainWindow
120
121        self.root_ui_calls.clear()
122        self._main_window = empty_weakref(MainWindow)
123        self._main_window_widget = None
124
125    @property
126    def uiscale(self) -> babase.UIScale:
127        """Current ui scale for the app."""
128        return self._uiscale
129
130    @override
131    def on_app_loading(self) -> None:
132        from bauiv1._uitypes import ui_upkeep
133
134        # IMPORTANT: If tweaking UI stuff, make sure it behaves for
135        # small, medium, and large UI modes. (doesn't run off screen,
136        # etc). The overrides below can be used to test with different
137        # sizes. Generally small is used on phones, medium is used on
138        # tablets/tvs, and large is on desktop computers or perhaps
139        # large tablets. When possible, run in windowed mode and resize
140        # the window to assure this holds true at all aspect ratios.
141
142        # UPDATE: A better way to test this is now by setting the
143        # environment variable BA_UI_SCALE to "small", "medium", or
144        # "large". This will affect system UIs not covered by the values
145        # below such as screen-messages. The below values remain
146        # functional, however, for cases such as Android where
147        # environment variables can't be set easily.
148
149        if bool(False):  # force-test ui scale
150            self._uiscale = babase.UIScale.SMALL
151            with babase.ContextRef.empty():
152                babase.pushcall(
153                    lambda: babase.screenmessage(
154                        f'FORCING UISCALE {self._uiscale.name} FOR TESTING',
155                        color=(1, 0, 1),
156                        log=True,
157                    )
158                )
159
160        # Kick off our periodic UI upkeep.
161
162        # FIXME: Can probably kill this if we do immediate UI death
163        # checks.
164        self.upkeeptimer = babase.AppTimer(2.6543, ui_upkeep, repeat=True)
165
166    def auto_set_back_window(self, from_window: MainWindow) -> None:
167        """Sets the main menu window automatically from a parent WindowState."""
168
169        main_window = self._main_window()
170
171        # This should never get called for top-level main-windows.
172        assert (
173            main_window is None or main_window.main_window_is_top_level is False
174        )
175
176        back_state = (
177            None if main_window is None else main_window.main_window_back_state
178        )
179        if back_state is None:
180            raise RuntimeError(
181                f'Main window {main_window} provides no back-state;'
182                f' cannot use auto-back.'
183            )
184
185        # Valid states should have values here.
186        assert back_state.is_top_level is not None
187        assert back_state.is_auxiliary is not None
188        assert back_state.window_type is not None
189
190        backwin = back_state.create_window(transition='in_left')
191
192        self.set_main_window(
193            backwin,
194            from_window=from_window,
195            is_back=True,
196            back_state=back_state,
197            suppress_warning=True,
198        )
199
200    def get_main_window(self) -> bauiv1.MainWindow | None:
201        """Return main window, if any."""
202        return self._main_window()
203
204    def set_main_window(
205        self,
206        window: bauiv1.MainWindow,
207        *,
208        from_window: bauiv1.MainWindow | None | bool = True,
209        is_back: bool = False,
210        is_top_level: bool = False,
211        is_auxiliary: bool = False,
212        back_state: MainWindowState | None = None,
213        suppress_warning: bool = False,
214    ) -> None:
215        """Set the current 'main' window, replacing any existing.
216
217        Generally this should not be called directly; The high level
218        MainWindow methods main_window_replace() and main_window_back()
219        should be used when possible for navigation.
220        """
221        # pylint: disable=too-many-locals
222        # pylint: disable=too-many-branches
223        # pylint: disable=too-many-statements
224        from bauiv1._uitypes import MainWindow
225
226        # Encourage migration to the new higher level nav calls.
227        if not suppress_warning:
228            warnings.warn(
229                'set_main_window() should usually not be called directly;'
230                ' use the main_window_replace() or main_window_back()'
231                ' methods on MainWindow objects for navigation instead.'
232                ' If you truly need to use set_main_window(),'
233                ' pass suppress_warning=True to silence this warning.',
234                DeprecationWarning,
235                stacklevel=2,
236            )
237
238        # We used to accept Widgets but now want MainWindows.
239        if not isinstance(window, MainWindow):
240            raise RuntimeError(
241                f'set_main_window() now takes a MainWindow as its "window" arg.'
242                f' You passed a {type(window)}.',
243            )
244        window_weakref = weakref.ref(window)
245        window_widget = window.get_root_widget()
246
247        if isinstance(from_window, MainWindow):
248            # from_window_widget = from_window.get_root_widget()
249            pass
250        else:
251            if from_window is not None and not isinstance(from_window, bool):
252                raise RuntimeError(
253                    f'set_main_window() now takes a MainWindow or bool or None'
254                    f'as its "from_window" arg.'
255                    f' You passed a {type(from_window)}.',
256                )
257
258        existing = self._main_window()
259
260        # If they passed a back-state, make sure it is fully filled out.
261        if back_state is not None:
262            if (
263                back_state.is_top_level is None
264                or back_state.is_auxiliary is None
265                or back_state.window_type is None
266            ):
267                raise RuntimeError(
268                    'Provided back_state is incomplete.'
269                    ' Make sure to only pass fully-filled-out MainWindowStates.'
270                )
271        # If a top-level main-window is being set, complain if there already
272        # is a main-window.
273        if is_top_level:
274            if existing:
275                logging.warning(
276                    'set_main_window() called with top-level window %s'
277                    ' but found existing main-window %s.',
278                    window,
279                    existing,
280                )
281        else:
282            # In other cases, sanity-check that the window ordering this
283            # switch is the one we're switching away from.
284            try:
285                if isinstance(from_window, bool):
286                    # For default val True we warn that the arg wasn't
287                    # passed. False can be explicitly passed to disable
288                    # this check.
289                    if from_window is True:
290                        caller_frame = inspect.stack()[1]
291                        caller_filename = caller_frame.filename
292                        caller_line_number = caller_frame.lineno
293                        logging.warning(
294                            'set_main_window() should be passed a'
295                            " 'from_window' value to help ensure proper"
296                            ' UI behavior (%s line %i).',
297                            caller_filename,
298                            caller_line_number,
299                        )
300                else:
301                    # For everything else, warn if what they passed
302                    # wasn't the previous main menu widget.
303                    if from_window is not existing:
304                        caller_frame = inspect.stack()[1]
305                        caller_filename = caller_frame.filename
306                        caller_line_number = caller_frame.lineno
307                        logging.warning(
308                            "set_main_window() was passed 'from_window' %s"
309                            ' but existing main-menu-window is %s.'
310                            ' (%s line %i).',
311                            from_window,
312                            existing,
313                            caller_filename,
314                            caller_line_number,
315                        )
316            except Exception:
317                # Prevent any bugs in these checks from causing problems.
318                logging.exception('Error checking from_window')
319
320        if is_back:
321            # These values should only be passed for forward navigation.
322            assert not is_top_level
323            assert not is_auxiliary
324            # Make sure back state is complete.
325            assert back_state is not None
326            assert back_state.is_top_level is not None
327            assert back_state.is_auxiliary is not None
328            assert back_state.window_type is type(window)
329            window.main_window_back_state = back_state.parent
330            window.main_window_is_top_level = back_state.is_top_level
331            window.main_window_is_auxiliary = back_state.is_auxiliary
332        else:
333            # Store if the window is top-level so we won't complain later if
334            # we go back from it and there's nowhere to go to.
335            window.main_window_is_top_level = is_top_level
336
337            window.main_window_is_auxiliary = is_auxiliary
338
339            # When navigating forward, generate a back-window-state from
340            # the outgoing window.
341            if is_top_level:
342                # Top level windows don't have or expect anywhere to
343                # go back to.
344                window.main_window_back_state = None
345            elif back_state is not None:
346                window.main_window_back_state = back_state
347            else:
348                oldwin = self._main_window()
349                if oldwin is None:
350                    # We currenty only hold weak refs to windows so that
351                    # they are free to die on their own, but we expect
352                    # the main menu window to keep itself alive as long
353                    # as its the main one. Holler if that seems to not
354                    # be happening.
355                    logging.warning(
356                        'set_main_window: No old MainWindow found'
357                        ' and is_top_level is False;'
358                        ' this should not happen.'
359                    )
360                    window.main_window_back_state = None
361                else:
362                    window.main_window_back_state = self.save_main_window_state(
363                        oldwin
364                    )
365
366        self._main_window = window_weakref
367        self._main_window_widget = window_widget
368
369    def has_main_window(self) -> bool:
370        """Return whether a main menu window is present."""
371        return bool(self._main_window_widget)
372
373    def clear_main_window(self, transition: str | None = None) -> None:
374        """Clear any existing main window."""
375        from bauiv1._uitypes import MainWindow
376
377        main_window = self._main_window()
378        if main_window:
379            main_window.main_window_close(transition=transition)
380        else:
381            # Fallback; if we have a widget but no window, nuke the widget.
382            if self._main_window_widget:
383                logging.error(
384                    'Have _main_window_widget but no main_window'
385                    ' on clear_main_window; unexpected.'
386                )
387                self._main_window_widget.delete()
388
389        self._main_window = empty_weakref(MainWindow)
390        self._main_window_widget = None
391
392    def save_main_window_state(self, window: MainWindow) -> MainWindowState:
393        """Fully initialize a window-state from a window.
394
395        Use this to get a state for later restoration purposes.
396        Calling the window's get_main_window_state() directly is
397        insufficient.
398        """
399        winstate = window.get_main_window_state()
400
401        # Store some common window stuff on its state.
402        winstate.parent = window.main_window_back_state
403        winstate.is_top_level = window.main_window_is_top_level
404        winstate.is_auxiliary = window.main_window_is_auxiliary
405        winstate.window_type = type(window)
406
407        return winstate
408
409    def restore_main_window_state(self, state: MainWindowState) -> None:
410        """Restore UI to a saved state."""
411        existing = self.get_main_window()
412        if existing is not None:
413            raise RuntimeError('There is already a MainWindow.')
414
415        # Valid states should have a value here.
416        assert state.is_top_level is not None
417        assert state.is_auxiliary is not None
418        assert state.window_type is not None
419
420        win = state.create_window(transition=None)
421        self.set_main_window(
422            win,
423            from_window=False,  # disable check
424            is_top_level=state.is_top_level,
425            is_auxiliary=state.is_auxiliary,
426            back_state=state.parent,
427            suppress_warning=True,
428        )
429
430    @override
431    def on_screen_change(self) -> None:
432        # Update our stored UIScale.
433        self._update_ui_scale()
434
435        # Update native bits (allow root widget to rebuild itself/etc.)
436        _bauiv1.on_screen_change()
437
438        # Lastly, if we have a main window, recreate it to pick up the
439        # new UIScale/etc.
440        mainwindow = self.get_main_window()
441        if mainwindow is not None:
442            winstate = self.save_main_window_state(mainwindow)
443            self.clear_main_window(transition='instant')
444            self.restore_main_window_state(winstate)

Consolidated UI functionality for the app.

Category: App Classes

To use this class, access the single instance of it at 'ba.app.ui'.

quit_window: _bauiv1.Widget | None
window_states: dict[type, typing.Any]
cleanupchecks: list[bauiv1._uitypes.UICleanupCheck]
upkeeptimer: _babase.AppTimer | None
title_color
heading_color
infotextcolor
root_ui_calls: dict[UIV1AppSubsystem.RootUIElement, typing.Callable[[], NoneType]]
available: bool
107    @property
108    def available(self) -> bool:
109        """Can uiv1 currently be used?
110
111        Code that may run in headless mode, before the UI has been spun up,
112        while other ui systems are active, etc. can check this to avoid
113        likely erroring.
114        """
115        return _bauiv1.is_available()

Can uiv1 currently be used?

Code that may run in headless mode, before the UI has been spun up, while other ui systems are active, etc. can check this to avoid likely erroring.

@override
def reset(self) -> None:
117    @override
118    def reset(self) -> None:
119        from bauiv1._uitypes import MainWindow
120
121        self.root_ui_calls.clear()
122        self._main_window = empty_weakref(MainWindow)
123        self._main_window_widget = None

Reset the subsystem to a default state.

This is called when switching app modes, but may be called at other times too.

uiscale: UIScale
125    @property
126    def uiscale(self) -> babase.UIScale:
127        """Current ui scale for the app."""
128        return self._uiscale

Current ui scale for the app.

@override
def on_app_loading(self) -> None:
130    @override
131    def on_app_loading(self) -> None:
132        from bauiv1._uitypes import ui_upkeep
133
134        # IMPORTANT: If tweaking UI stuff, make sure it behaves for
135        # small, medium, and large UI modes. (doesn't run off screen,
136        # etc). The overrides below can be used to test with different
137        # sizes. Generally small is used on phones, medium is used on
138        # tablets/tvs, and large is on desktop computers or perhaps
139        # large tablets. When possible, run in windowed mode and resize
140        # the window to assure this holds true at all aspect ratios.
141
142        # UPDATE: A better way to test this is now by setting the
143        # environment variable BA_UI_SCALE to "small", "medium", or
144        # "large". This will affect system UIs not covered by the values
145        # below such as screen-messages. The below values remain
146        # functional, however, for cases such as Android where
147        # environment variables can't be set easily.
148
149        if bool(False):  # force-test ui scale
150            self._uiscale = babase.UIScale.SMALL
151            with babase.ContextRef.empty():
152                babase.pushcall(
153                    lambda: babase.screenmessage(
154                        f'FORCING UISCALE {self._uiscale.name} FOR TESTING',
155                        color=(1, 0, 1),
156                        log=True,
157                    )
158                )
159
160        # Kick off our periodic UI upkeep.
161
162        # FIXME: Can probably kill this if we do immediate UI death
163        # checks.
164        self.upkeeptimer = babase.AppTimer(2.6543, ui_upkeep, repeat=True)

Called when the app reaches the loading state.

Note that subsystems created after the app switches to the loading state will not receive this callback. Subsystems created by plugins are an example of this.

def auto_set_back_window(self, from_window: MainWindow) -> None:
166    def auto_set_back_window(self, from_window: MainWindow) -> None:
167        """Sets the main menu window automatically from a parent WindowState."""
168
169        main_window = self._main_window()
170
171        # This should never get called for top-level main-windows.
172        assert (
173            main_window is None or main_window.main_window_is_top_level is False
174        )
175
176        back_state = (
177            None if main_window is None else main_window.main_window_back_state
178        )
179        if back_state is None:
180            raise RuntimeError(
181                f'Main window {main_window} provides no back-state;'
182                f' cannot use auto-back.'
183            )
184
185        # Valid states should have values here.
186        assert back_state.is_top_level is not None
187        assert back_state.is_auxiliary is not None
188        assert back_state.window_type is not None
189
190        backwin = back_state.create_window(transition='in_left')
191
192        self.set_main_window(
193            backwin,
194            from_window=from_window,
195            is_back=True,
196            back_state=back_state,
197            suppress_warning=True,
198        )

Sets the main menu window automatically from a parent WindowState.

def get_main_window(self) -> MainWindow | None:
200    def get_main_window(self) -> bauiv1.MainWindow | None:
201        """Return main window, if any."""
202        return self._main_window()

Return main window, if any.

def set_main_window( self, window: MainWindow, *, from_window: MainWindow | None | bool = True, is_back: bool = False, is_top_level: bool = False, is_auxiliary: bool = False, back_state: MainWindowState | None = None, suppress_warning: bool = False) -> None:
204    def set_main_window(
205        self,
206        window: bauiv1.MainWindow,
207        *,
208        from_window: bauiv1.MainWindow | None | bool = True,
209        is_back: bool = False,
210        is_top_level: bool = False,
211        is_auxiliary: bool = False,
212        back_state: MainWindowState | None = None,
213        suppress_warning: bool = False,
214    ) -> None:
215        """Set the current 'main' window, replacing any existing.
216
217        Generally this should not be called directly; The high level
218        MainWindow methods main_window_replace() and main_window_back()
219        should be used when possible for navigation.
220        """
221        # pylint: disable=too-many-locals
222        # pylint: disable=too-many-branches
223        # pylint: disable=too-many-statements
224        from bauiv1._uitypes import MainWindow
225
226        # Encourage migration to the new higher level nav calls.
227        if not suppress_warning:
228            warnings.warn(
229                'set_main_window() should usually not be called directly;'
230                ' use the main_window_replace() or main_window_back()'
231                ' methods on MainWindow objects for navigation instead.'
232                ' If you truly need to use set_main_window(),'
233                ' pass suppress_warning=True to silence this warning.',
234                DeprecationWarning,
235                stacklevel=2,
236            )
237
238        # We used to accept Widgets but now want MainWindows.
239        if not isinstance(window, MainWindow):
240            raise RuntimeError(
241                f'set_main_window() now takes a MainWindow as its "window" arg.'
242                f' You passed a {type(window)}.',
243            )
244        window_weakref = weakref.ref(window)
245        window_widget = window.get_root_widget()
246
247        if isinstance(from_window, MainWindow):
248            # from_window_widget = from_window.get_root_widget()
249            pass
250        else:
251            if from_window is not None and not isinstance(from_window, bool):
252                raise RuntimeError(
253                    f'set_main_window() now takes a MainWindow or bool or None'
254                    f'as its "from_window" arg.'
255                    f' You passed a {type(from_window)}.',
256                )
257
258        existing = self._main_window()
259
260        # If they passed a back-state, make sure it is fully filled out.
261        if back_state is not None:
262            if (
263                back_state.is_top_level is None
264                or back_state.is_auxiliary is None
265                or back_state.window_type is None
266            ):
267                raise RuntimeError(
268                    'Provided back_state is incomplete.'
269                    ' Make sure to only pass fully-filled-out MainWindowStates.'
270                )
271        # If a top-level main-window is being set, complain if there already
272        # is a main-window.
273        if is_top_level:
274            if existing:
275                logging.warning(
276                    'set_main_window() called with top-level window %s'
277                    ' but found existing main-window %s.',
278                    window,
279                    existing,
280                )
281        else:
282            # In other cases, sanity-check that the window ordering this
283            # switch is the one we're switching away from.
284            try:
285                if isinstance(from_window, bool):
286                    # For default val True we warn that the arg wasn't
287                    # passed. False can be explicitly passed to disable
288                    # this check.
289                    if from_window is True:
290                        caller_frame = inspect.stack()[1]
291                        caller_filename = caller_frame.filename
292                        caller_line_number = caller_frame.lineno
293                        logging.warning(
294                            'set_main_window() should be passed a'
295                            " 'from_window' value to help ensure proper"
296                            ' UI behavior (%s line %i).',
297                            caller_filename,
298                            caller_line_number,
299                        )
300                else:
301                    # For everything else, warn if what they passed
302                    # wasn't the previous main menu widget.
303                    if from_window is not existing:
304                        caller_frame = inspect.stack()[1]
305                        caller_filename = caller_frame.filename
306                        caller_line_number = caller_frame.lineno
307                        logging.warning(
308                            "set_main_window() was passed 'from_window' %s"
309                            ' but existing main-menu-window is %s.'
310                            ' (%s line %i).',
311                            from_window,
312                            existing,
313                            caller_filename,
314                            caller_line_number,
315                        )
316            except Exception:
317                # Prevent any bugs in these checks from causing problems.
318                logging.exception('Error checking from_window')
319
320        if is_back:
321            # These values should only be passed for forward navigation.
322            assert not is_top_level
323            assert not is_auxiliary
324            # Make sure back state is complete.
325            assert back_state is not None
326            assert back_state.is_top_level is not None
327            assert back_state.is_auxiliary is not None
328            assert back_state.window_type is type(window)
329            window.main_window_back_state = back_state.parent
330            window.main_window_is_top_level = back_state.is_top_level
331            window.main_window_is_auxiliary = back_state.is_auxiliary
332        else:
333            # Store if the window is top-level so we won't complain later if
334            # we go back from it and there's nowhere to go to.
335            window.main_window_is_top_level = is_top_level
336
337            window.main_window_is_auxiliary = is_auxiliary
338
339            # When navigating forward, generate a back-window-state from
340            # the outgoing window.
341            if is_top_level:
342                # Top level windows don't have or expect anywhere to
343                # go back to.
344                window.main_window_back_state = None
345            elif back_state is not None:
346                window.main_window_back_state = back_state
347            else:
348                oldwin = self._main_window()
349                if oldwin is None:
350                    # We currenty only hold weak refs to windows so that
351                    # they are free to die on their own, but we expect
352                    # the main menu window to keep itself alive as long
353                    # as its the main one. Holler if that seems to not
354                    # be happening.
355                    logging.warning(
356                        'set_main_window: No old MainWindow found'
357                        ' and is_top_level is False;'
358                        ' this should not happen.'
359                    )
360                    window.main_window_back_state = None
361                else:
362                    window.main_window_back_state = self.save_main_window_state(
363                        oldwin
364                    )
365
366        self._main_window = window_weakref
367        self._main_window_widget = window_widget

Set the current 'main' window, replacing any existing.

Generally this should not be called directly; The high level MainWindow methods main_window_replace() and main_window_back() should be used when possible for navigation.

def has_main_window(self) -> bool:
369    def has_main_window(self) -> bool:
370        """Return whether a main menu window is present."""
371        return bool(self._main_window_widget)

Return whether a main menu window is present.

def clear_main_window(self, transition: str | None = None) -> None:
373    def clear_main_window(self, transition: str | None = None) -> None:
374        """Clear any existing main window."""
375        from bauiv1._uitypes import MainWindow
376
377        main_window = self._main_window()
378        if main_window:
379            main_window.main_window_close(transition=transition)
380        else:
381            # Fallback; if we have a widget but no window, nuke the widget.
382            if self._main_window_widget:
383                logging.error(
384                    'Have _main_window_widget but no main_window'
385                    ' on clear_main_window; unexpected.'
386                )
387                self._main_window_widget.delete()
388
389        self._main_window = empty_weakref(MainWindow)
390        self._main_window_widget = None

Clear any existing main window.

def save_main_window_state( self, window: MainWindow) -> MainWindowState:
392    def save_main_window_state(self, window: MainWindow) -> MainWindowState:
393        """Fully initialize a window-state from a window.
394
395        Use this to get a state for later restoration purposes.
396        Calling the window's get_main_window_state() directly is
397        insufficient.
398        """
399        winstate = window.get_main_window_state()
400
401        # Store some common window stuff on its state.
402        winstate.parent = window.main_window_back_state
403        winstate.is_top_level = window.main_window_is_top_level
404        winstate.is_auxiliary = window.main_window_is_auxiliary
405        winstate.window_type = type(window)
406
407        return winstate

Fully initialize a window-state from a window.

Use this to get a state for later restoration purposes. Calling the window's get_main_window_state() directly is insufficient.

def restore_main_window_state(self, state: MainWindowState) -> None:
409    def restore_main_window_state(self, state: MainWindowState) -> None:
410        """Restore UI to a saved state."""
411        existing = self.get_main_window()
412        if existing is not None:
413            raise RuntimeError('There is already a MainWindow.')
414
415        # Valid states should have a value here.
416        assert state.is_top_level is not None
417        assert state.is_auxiliary is not None
418        assert state.window_type is not None
419
420        win = state.create_window(transition=None)
421        self.set_main_window(
422            win,
423            from_window=False,  # disable check
424            is_top_level=state.is_top_level,
425            is_auxiliary=state.is_auxiliary,
426            back_state=state.parent,
427            suppress_warning=True,
428        )

Restore UI to a saved state.

@override
def on_screen_change(self) -> None:
430    @override
431    def on_screen_change(self) -> None:
432        # Update our stored UIScale.
433        self._update_ui_scale()
434
435        # Update native bits (allow root widget to rebuild itself/etc.)
436        _bauiv1.on_screen_change()
437
438        # Lastly, if we have a main window, recreate it to pick up the
439        # new UIScale/etc.
440        mainwindow = self.get_main_window()
441        if mainwindow is not None:
442            winstate = self.save_main_window_state(mainwindow)
443            self.clear_main_window(transition='instant')
444            self.restore_main_window_state(winstate)

Called when screen dimensions or ui-scale changes.

class UIV1AppSubsystem.RootUIElement(enum.Enum):
40    class RootUIElement(Enum):
41        """Stuff provided by the root ui."""
42
43        MENU_BUTTON = 'menu_button'
44        SQUAD_BUTTON = 'squad_button'
45        ACCOUNT_BUTTON = 'account_button'
46        SETTINGS_BUTTON = 'settings_button'
47        INBOX_BUTTON = 'inbox_button'
48        STORE_BUTTON = 'store_button'
49        INVENTORY_BUTTON = 'inventory_button'
50        ACHIEVEMENTS_BUTTON = 'achievements_button'
51        GET_TOKENS_BUTTON = 'get_tokens_button'
52        TICKETS_METER = 'tickets_meter'
53        TOKENS_METER = 'tokens_meter'
54        TROPHY_METER = 'trophy_meter'
55        LEVEL_METER = 'level_meter'
56        CHEST_SLOT_1 = 'chest_slot_1'
57        CHEST_SLOT_2 = 'chest_slot_2'
58        CHEST_SLOT_3 = 'chest_slot_3'
59        CHEST_SLOT_4 = 'chest_slot_4'

Stuff provided by the root ui.

MENU_BUTTON = <RootUIElement.MENU_BUTTON: 'menu_button'>
SQUAD_BUTTON = <RootUIElement.SQUAD_BUTTON: 'squad_button'>
ACCOUNT_BUTTON = <RootUIElement.ACCOUNT_BUTTON: 'account_button'>
SETTINGS_BUTTON = <RootUIElement.SETTINGS_BUTTON: 'settings_button'>
INBOX_BUTTON = <RootUIElement.INBOX_BUTTON: 'inbox_button'>
STORE_BUTTON = <RootUIElement.STORE_BUTTON: 'store_button'>
INVENTORY_BUTTON = <RootUIElement.INVENTORY_BUTTON: 'inventory_button'>
ACHIEVEMENTS_BUTTON = <RootUIElement.ACHIEVEMENTS_BUTTON: 'achievements_button'>
GET_TOKENS_BUTTON = <RootUIElement.GET_TOKENS_BUTTON: 'get_tokens_button'>
TICKETS_METER = <RootUIElement.TICKETS_METER: 'tickets_meter'>
TOKENS_METER = <RootUIElement.TOKENS_METER: 'tokens_meter'>
TROPHY_METER = <RootUIElement.TROPHY_METER: 'trophy_meter'>
LEVEL_METER = <RootUIElement.LEVEL_METER: 'level_meter'>
CHEST_SLOT_1 = <RootUIElement.CHEST_SLOT_1: 'chest_slot_1'>
CHEST_SLOT_2 = <RootUIElement.CHEST_SLOT_2: 'chest_slot_2'>
CHEST_SLOT_3 = <RootUIElement.CHEST_SLOT_3: 'chest_slot_3'>
CHEST_SLOT_4 = <RootUIElement.CHEST_SLOT_4: 'chest_slot_4'>
WeakCall = <class 'babase._general._WeakCall'>
def widget( *, edit: _bauiv1.Widget, up_widget: _bauiv1.Widget | None = None, down_widget: _bauiv1.Widget | None = None, left_widget: _bauiv1.Widget | None = None, right_widget: _bauiv1.Widget | None = None, show_buffer_top: float | None = None, show_buffer_bottom: float | None = None, show_buffer_left: float | None = None, show_buffer_right: float | None = None, autoselect: bool | None = None) -> None:
593def widget(
594    *,
595    edit: bauiv1.Widget,
596    up_widget: bauiv1.Widget | None = None,
597    down_widget: bauiv1.Widget | None = None,
598    left_widget: bauiv1.Widget | None = None,
599    right_widget: bauiv1.Widget | None = None,
600    show_buffer_top: float | None = None,
601    show_buffer_bottom: float | None = None,
602    show_buffer_left: float | None = None,
603    show_buffer_right: float | None = None,
604    autoselect: bool | None = None,
605) -> None:
606    """Edit common attributes of any widget.
607
608    Category: **User Interface Functions**
609
610    Unlike other UI calls, this can only be used to edit, not to create.
611    """
612    return None

Edit common attributes of any widget.

Category: User Interface Functions

Unlike other UI calls, this can only be used to edit, not to create.

class Widget:
 75class Widget:
 76    """Internal type for low level UI elements; buttons, windows, etc.
 77
 78    Category: **User Interface Classes**
 79
 80    This class represents a weak reference to a widget object
 81    in the internal C++ layer. Currently, functions such as
 82    bauiv1.buttonwidget() must be used to instantiate or edit these.
 83    """
 84
 85    transitioning_out: bool
 86    """Whether this widget is in the process of dying (read only).
 87
 88       It can be useful to check this on a window's root widget to
 89       prevent multiple window actions from firing simultaneously,
 90       potentially leaving the UI in a broken state."""
 91
 92    def __bool__(self) -> bool:
 93        """Support for bool evaluation."""
 94        return bool(True)  # Slight obfuscation.
 95
 96    def activate(self) -> None:
 97        """Activates a widget; the same as if it had been clicked."""
 98        return None
 99
100    def add_delete_callback(self, call: Callable) -> None:
101        """Add a call to be run immediately after this widget is destroyed."""
102        return None
103
104    def delete(self, ignore_missing: bool = True) -> None:
105        """Delete the Widget. Ignores already-deleted Widgets if ignore_missing
106        is True; otherwise an Exception is thrown.
107        """
108        return None
109
110    def exists(self) -> bool:
111        """Returns whether the Widget still exists.
112        Most functionality will fail on a nonexistent widget.
113
114        Note that you can also use the boolean operator for this same
115        functionality, so a statement such as "if mywidget" will do
116        the right thing both for Widget objects and values of None.
117        """
118        return bool()
119
120    def get_children(self) -> list[bauiv1.Widget]:
121        """Returns any child Widgets of this Widget."""
122        import bauiv1
123
124        return [bauiv1.Widget()]
125
126    def get_screen_space_center(self) -> tuple[float, float]:
127        """Returns the coords of the bauiv1.Widget center relative to the center
128        of the screen. This can be useful for placing pop-up windows and other
129        special cases.
130        """
131        return (0.0, 0.0)
132
133    def get_selected_child(self) -> bauiv1.Widget | None:
134        """Returns the selected child Widget or None if nothing is selected."""
135        import bauiv1
136
137        return bauiv1.Widget()
138
139    def get_widget_type(self) -> str:
140        """Return the internal type of the Widget as a string. Note that this
141        is different from the Python bauiv1.Widget type, which is the same for
142        all widgets.
143        """
144        return str()

Internal type for low level UI elements; buttons, windows, etc.

Category: User Interface Classes

This class represents a weak reference to a widget object in the internal C++ layer. Currently, functions such as bauiv1.buttonwidget() must be used to instantiate or edit these.

transitioning_out: bool

Whether this widget is in the process of dying (read only).

It can be useful to check this on a window's root widget to prevent multiple window actions from firing simultaneously, potentially leaving the UI in a broken state.

def activate(self) -> None:
96    def activate(self) -> None:
97        """Activates a widget; the same as if it had been clicked."""
98        return None

Activates a widget; the same as if it had been clicked.

def add_delete_callback(self, call: Callable) -> None:
100    def add_delete_callback(self, call: Callable) -> None:
101        """Add a call to be run immediately after this widget is destroyed."""
102        return None

Add a call to be run immediately after this widget is destroyed.

def delete(self, ignore_missing: bool = True) -> None:
104    def delete(self, ignore_missing: bool = True) -> None:
105        """Delete the Widget. Ignores already-deleted Widgets if ignore_missing
106        is True; otherwise an Exception is thrown.
107        """
108        return None

Delete the Widget. Ignores already-deleted Widgets if ignore_missing is True; otherwise an Exception is thrown.

def exists(self) -> bool:
110    def exists(self) -> bool:
111        """Returns whether the Widget still exists.
112        Most functionality will fail on a nonexistent widget.
113
114        Note that you can also use the boolean operator for this same
115        functionality, so a statement such as "if mywidget" will do
116        the right thing both for Widget objects and values of None.
117        """
118        return bool()

Returns whether the Widget still exists. Most functionality will fail on a nonexistent widget.

Note that you can also use the boolean operator for this same functionality, so a statement such as "if mywidget" will do the right thing both for Widget objects and values of None.

def get_children(self) -> list[_bauiv1.Widget]:
120    def get_children(self) -> list[bauiv1.Widget]:
121        """Returns any child Widgets of this Widget."""
122        import bauiv1
123
124        return [bauiv1.Widget()]

Returns any child Widgets of this Widget.

def get_screen_space_center(self) -> tuple[float, float]:
126    def get_screen_space_center(self) -> tuple[float, float]:
127        """Returns the coords of the bauiv1.Widget center relative to the center
128        of the screen. This can be useful for placing pop-up windows and other
129        special cases.
130        """
131        return (0.0, 0.0)

Returns the coords of the bauiv1.Widget center relative to the center of the screen. This can be useful for placing pop-up windows and other special cases.

def get_selected_child(self) -> _bauiv1.Widget | None:
133    def get_selected_child(self) -> bauiv1.Widget | None:
134        """Returns the selected child Widget or None if nothing is selected."""
135        import bauiv1
136
137        return bauiv1.Widget()

Returns the selected child Widget or None if nothing is selected.

def get_widget_type(self) -> str:
139    def get_widget_type(self) -> str:
140        """Return the internal type of the Widget as a string. Note that this
141        is different from the Python bauiv1.Widget type, which is the same for
142        all widgets.
143        """
144        return str()

Return the internal type of the Widget as a string. Note that this is different from the Python bauiv1.Widget type, which is the same for all widgets.

class Window:
28class Window:
29    """A basic window.
30
31    Category: User Interface Classes
32
33    Essentially wraps a ContainerWidget with some higher level
34    functionality.
35    """
36
37    def __init__(self, root_widget: bauiv1.Widget, cleanupcheck: bool = True):
38        self._root_widget = root_widget
39
40        # Complain if we outlive our root widget.
41        if cleanupcheck:
42            uicleanupcheck(self, root_widget)
43
44    def get_root_widget(self) -> bauiv1.Widget:
45        """Return the root widget."""
46        return self._root_widget

A basic window.

Category: User Interface Classes

Essentially wraps a ContainerWidget with some higher level functionality.

Window(root_widget: _bauiv1.Widget, cleanupcheck: bool = True)
37    def __init__(self, root_widget: bauiv1.Widget, cleanupcheck: bool = True):
38        self._root_widget = root_widget
39
40        # Complain if we outlive our root widget.
41        if cleanupcheck:
42            uicleanupcheck(self, root_widget)
def get_root_widget(self) -> _bauiv1.Widget:
44    def get_root_widget(self) -> bauiv1.Widget:
45        """Return the root widget."""
46        return self._root_widget

Return the root widget.