Source code for bauiv1lib.settings.advanced

# Released under the MIT License. See LICENSE for details.
#
# pylint: disable=too-many-lines

"""UI functionality for advanced settings."""

from __future__ import annotations

import os
import logging
from typing import TYPE_CHECKING, override

from bacommon.locale import LocaleResolved
from bauiv1lib.popup import PopupMenu
import bauiv1 as bui

if TYPE_CHECKING:
    from typing import Any


[docs] class AdvancedSettingsWindow(bui.MainWindow): """Window for editing advanced app settings.""" def __init__( self, transition: str | None = 'in_right', origin_widget: bui.Widget | None = None, ): # pylint: disable=too-many-statements if bui.app.classic is None: raise RuntimeError('This requires classic support.') # Preload some modules we use in a background thread so we won't # have a visual hitch when the user taps them. bui.app.threadpool.submit_no_wait(self._preload_modules) app = bui.app assert app.classic is not None uiscale = bui.app.ui_v1.uiscale self._width = 1030.0 if uiscale is bui.UIScale.SMALL else 670.0 self._height = ( 490.0 if uiscale is bui.UIScale.SMALL else 450.0 if uiscale is bui.UIScale.MEDIUM else 600.0 ) self._lang_status_text: bui.Widget | None = None self._spacing = 32 self._menu_open = False # Do some fancy math to fill all available screen area up to the # size of our backing container. This lets us fit to the exact # screen shape at small ui scale. screensize = bui.get_virtual_screen_size() scale = ( 2.2 if uiscale is bui.UIScale.SMALL else 1.3 if uiscale is bui.UIScale.MEDIUM else 0.9 ) # Calc screen size in our local container space and clamp to a # bit smaller than our container size. target_width = min(self._width - 80, screensize[0] / scale) target_height = min(self._height - 80, screensize[1] / scale) # To get top/left coords, go to the center of our window and # offset by half the width/height of our target area. yoffs = 0.5 * self._height + 0.5 * target_height + 30.0 self._scroll_width = target_width self._scroll_height = target_height - 25 scroll_bottom = yoffs - 56 - self._scroll_height super().__init__( root_widget=bui.containerwidget( size=(self._width, self._height), toolbar_visibility=( 'menu_minimal' if uiscale is bui.UIScale.SMALL else 'menu_full' ), scale=scale, ), transition=transition, origin_widget=origin_widget, # We're affected by screen size only at small ui-scale. refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, ) self._prev_lang: str | None = '' self._prev_lang_list: list[str] = [] self._complete_langs_list: list | None = None self._complete_langs_error = False self._language_popup: PopupMenu | None = None # In vr-mode, the internal keyboard is currently the *only* option, # so no need to show this. self._show_always_use_internal_keyboard = not app.env.vr self._sub_width = min(550, self._scroll_width * 0.95) self._sub_height = 870.0 if self._show_always_use_internal_keyboard: self._sub_height += 62 self._show_disable_gyro = app.classic.platform in {'ios', 'android'} if self._show_disable_gyro: self._sub_height += 42 self._show_use_insecure_connections = True if self._show_use_insecure_connections: self._sub_height += 82 self._do_vr_test_button = app.env.vr self._do_net_test_button = True self._extra_button_spacing = self._spacing * 2.5 if self._do_vr_test_button: self._sub_height += self._extra_button_spacing if self._do_net_test_button: self._sub_height += self._extra_button_spacing self._sub_height += self._spacing * 2.0 # plugins self._sub_height += self._spacing * 2.0 # dev tools self._r = 'settingsWindowAdvanced' if uiscale is bui.UIScale.SMALL: bui.containerwidget( edit=self._root_widget, on_cancel_call=self.main_window_back ) self._back_button = None else: self._back_button = bui.buttonwidget( parent=self._root_widget, position=(50, yoffs - 48), size=(60, 60), scale=0.8, autoselect=True, label=bui.charstr(bui.SpecialChar.BACK), button_type='backSmall', on_activate_call=self.main_window_back, ) bui.containerwidget( edit=self._root_widget, cancel_button=self._back_button ) self._title_text = bui.textwidget( parent=self._root_widget, position=( self._width * 0.5, yoffs - (43 if uiscale is bui.UIScale.SMALL else 25), ), size=(0, 0), scale=0.75 if uiscale is bui.UIScale.SMALL else 1.0, text=bui.Lstr(resource=f'{self._r}.titleText'), color=app.ui_v1.title_color, h_align='center', v_align='center', ) self._scrollwidget = bui.scrollwidget( parent=self._root_widget, size=(self._scroll_width, self._scroll_height), position=( self._width * 0.5 - self._scroll_width * 0.5, scroll_bottom, ), simple_culling_v=20.0, highlight=False, center_small_content_horizontally=True, selection_loops_to_parent=True, border_opacity=0.4, ) bui.widget(edit=self._scrollwidget, right_widget=self._scrollwidget) self._subcontainer = bui.containerwidget( parent=self._scrollwidget, size=(self._sub_width, self._sub_height), background=False, selection_loops_to_parent=True, ) self._rebuild() # Rebuild periodically to pick up language changes/additions/etc. self._rebuild_timer = bui.AppTimer( 1.0, bui.WeakCall(self._rebuild), repeat=True ) # Fetch the list of completed languages. bui.app.classic.master_server_v1_get( 'bsLangGetCompleted', {'b': app.env.engine_build_number}, callback=bui.WeakCall(self._completed_langs_cb), )
[docs] @override def get_main_window_state(self) -> bui.MainWindowState: # Support recreating our window for back/refresh purposes. cls = type(self) return bui.BasicMainWindowState( create_call=lambda transition, origin_widget: cls( transition=transition, origin_widget=origin_widget ) )
[docs] @override def on_main_window_close(self) -> None: self._save_state()
@staticmethod def _preload_modules() -> None: """Preload stuff in bg thread to avoid hitches in logic thread""" from babase import modutils as _unused2 from bauiv1lib import config as _unused1 from bauiv1lib.settings import vrtesting as _unused3 from bauiv1lib.settings import nettesting as _unused4 from bauiv1lib import appinvite as _unused5 from bauiv1lib import account as _unused6 from bauiv1lib import sendinfo as _unused7 from bauiv1lib.settings import benchmarks as _unused8 from bauiv1lib.settings import plugins as _unused9 from bauiv1lib.settings import devtools as _unused10 def _update_lang_status(self) -> None: if self._complete_langs_list is not None: up_to_date = bui.app.lang.language in self._complete_langs_list bui.textwidget( edit=self._lang_status_text, text=( '' if bui.app.lang.language == 'Test' else ( bui.Lstr( resource=f'{self._r}.translationNoUpdateNeededText' ) if up_to_date else bui.Lstr( resource=f'{self._r}.translationUpdateNeededText' ) ) ), color=( (0.2, 1.0, 0.2, 0.8) if up_to_date else (1.0, 0.2, 0.2, 0.8) ), ) else: bui.textwidget( edit=self._lang_status_text, text=( bui.Lstr(resource=f'{self._r}.translationFetchErrorText') if self._complete_langs_error else bui.Lstr( resource=f'{self._r}.translationFetchingStatusText' ) ), color=( (1.0, 0.5, 0.2) if self._complete_langs_error else (0.7, 0.7, 0.7) ), ) def _rebuild(self) -> None: # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals from bauiv1lib.config import ConfigCheckBox from babase.modutils import show_user_scripts plus = bui.app.plus assert plus is not None locale_ss = bui.app.locale # Build a list of long-values for locales we are able to display. available_languages = sorted( lr.locale.long_value for lr in LocaleResolved if (locale_ss.can_display_locale(lr.locale)) ) # Don't rebuild if the menu is open or if our language and # language-list hasn't changed. # NOTE - although we now support widgets updating their own # translations, we still change the label formatting on the # language menu based on the language so still need this. # ...however we could make this more limited to it only rebuilds # that one menu instead of everything. if self._menu_open or ( self._prev_lang == bui.app.config.get('Lang', None) and self._prev_lang_list == available_languages ): return self._prev_lang = bui.app.config.get('Lang', None) self._prev_lang_list = available_languages # Clear out our sub-container. children = self._subcontainer.get_children() for child in children: child.delete() v = self._sub_height - 35 v -= self._spacing * 1.2 # Update our existing back button and title. if self._back_button is not None: bui.buttonwidget( edit=self._back_button, label=bui.Lstr(resource='backText') ) bui.buttonwidget( edit=self._back_button, label=bui.charstr(bui.SpecialChar.BACK) ) bui.textwidget( edit=self._title_text, text=bui.Lstr(resource=f'{self._r}.titleText'), ) this_button_width = 410 assert bui.app.classic is not None bui.textwidget( parent=self._subcontainer, position=(70, v + 10), size=(0, 0), text=bui.Lstr(resource=f'{self._r}.languageText'), maxwidth=150, scale=1.2, color=bui.app.ui_v1.title_color, h_align='left', v_align='center', ) cur_lang = bui.app.locale.current_locale.long_value # We have a special dict of language names in that language so # we don't have to go digging through each full language. try: import json with open( os.path.join( bui.app.env.data_directory, 'ba_data', 'data', 'langdata.json', ), encoding='utf-8', ) as infile: lang_names_translated = json.loads(infile.read())[ 'lang_names_translated' ] except Exception: logging.exception('Error reading lang data.') lang_names_translated = {} langs_translated = {} for lang in available_languages: langs_translated[lang] = lang_names_translated.get(lang, lang) langs_full = {} for lang in available_languages: lang_translated = bui.Lstr(translate=('languages', lang)).evaluate() if langs_translated[lang] == lang_translated: langs_full[lang] = lang_translated else: langs_full[lang] = ( langs_translated[lang] + ' (' + lang_translated + ')' ) self._language_popup = PopupMenu( parent=self._subcontainer, position=(210, v - 19), width=250, opening_call=bui.WeakCall(self._on_menu_open), closing_call=bui.WeakCall(self._on_menu_close), autoselect=False, on_value_change_call=bui.WeakCall(self._on_menu_choice), choices=['Auto'] + available_languages, button_size=(300, 60), choices_display=( [ bui.Lstr( value=( bui.Lstr(resource='autoText').evaluate() + ' (' + bui.Lstr( translate=( 'languages', bui.app.locale.default_locale.long_value, ) ).evaluate() + ')' ) ) ] + [bui.Lstr(value=langs_full[l]) for l in available_languages] ), current_choice=cur_lang, ) v -= self._spacing * 1.8 bui.textwidget( parent=self._subcontainer, position=(90, v + 10), size=(0, 0), text=bui.Lstr( resource=f'{self._r}.helpTranslateText', subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))], ), maxwidth=self._sub_width * 0.9, max_height=55, flatness=1.0, scale=0.65, color=(0.4, 0.9, 0.4, 0.8), h_align='left', v_align='center', ) v -= self._spacing * 1.9 this_button_width = 410 self._translation_editor_button = bui.buttonwidget( parent=self._subcontainer, position=(self._sub_width / 2 - this_button_width / 2, v - 24), size=(this_button_width, 60), label=bui.Lstr( resource=f'{self._r}.translationEditorButtonText', subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))], ), autoselect=True, on_activate_call=bui.Call( bui.open_url, 'https://legacy.ballistica.net/translate' ), ) self._lang_status_text = bui.textwidget( parent=self._subcontainer, position=(self._sub_width * 0.5, v - 40), size=(0, 0), text='', flatness=1.0, scale=0.63, h_align='center', v_align='center', maxwidth=400.0, ) self._update_lang_status() v -= 50 lang_inform = plus.get_v1_account_misc_val('langInform', False) self._language_inform_checkbox = cbw = bui.checkboxwidget( parent=self._subcontainer, position=(50, v - 50), size=(self._sub_width - 100, 30), autoselect=True, maxwidth=430, textcolor=(0.8, 0.8, 0.8), value=lang_inform, text=bui.Lstr(resource=f'{self._r}.translationInformMe'), on_value_change_call=bui.WeakCall( self._on_lang_inform_value_change ), ) bui.widget( edit=self._translation_editor_button, down_widget=cbw, up_widget=self._language_popup.get_button(), ) v -= self._spacing * 3.0 self._kick_idle_players_check_box = ConfigCheckBox( parent=self._subcontainer, position=(50, v), size=(self._sub_width - 100, 30), configkey='Kick Idle Players', displayname=bui.Lstr(resource=f'{self._r}.kickIdlePlayersText'), scale=1.0, maxwidth=430, ) v -= 42 self._show_game_ping_check_box = ConfigCheckBox( parent=self._subcontainer, position=(50, v), size=(self._sub_width - 100, 30), configkey='Show Ping', displayname=bui.Lstr(resource=f'{self._r}.showInGamePingText'), scale=1.0, maxwidth=430, ) v -= 42 self._show_demos_when_idle_check_box = ConfigCheckBox( parent=self._subcontainer, position=(50, v), size=(self._sub_width - 100, 30), configkey='Show Demos When Idle', displayname=bui.Lstr(resource=f'{self._r}.showDemosWhenIdleText'), scale=1.0, maxwidth=430, ) v -= 42 self._show_deprecated_login_types_check_box = ConfigCheckBox( parent=self._subcontainer, position=(50, v), size=(self._sub_width - 100, 30), configkey='Show Deprecated Login Types', displayname=bui.Lstr( resource=f'{self._r}.showDeprecatedLoginTypesText' ), scale=1.0, maxwidth=430, ) v -= 42 self._disable_camera_shake_check_box = ConfigCheckBox( parent=self._subcontainer, position=(50, v), size=(self._sub_width - 100, 30), configkey='Disable Camera Shake', displayname=bui.Lstr(resource=f'{self._r}.disableCameraShakeText'), scale=1.0, maxwidth=430, ) self._disable_gyro_check_box: ConfigCheckBox | None = None if self._show_disable_gyro: v -= 42 self._disable_gyro_check_box = ConfigCheckBox( parent=self._subcontainer, position=(50, v), size=(self._sub_width - 100, 30), configkey='Disable Camera Gyro', displayname=bui.Lstr( resource=f'{self._r}.disableCameraGyroscopeMotionText' ), scale=1.0, maxwidth=430, ) self._use_insecure_connections_check_box: ConfigCheckBox | None if self._show_use_insecure_connections: v -= 42 self._use_insecure_connections_check_box = ConfigCheckBox( parent=self._subcontainer, position=(50, v), size=(self._sub_width - 100, 30), configkey='Use Insecure Connections', autoselect=True, # displayname='USE INSECURE CONNECTIONS', displayname=bui.Lstr( resource=(f'{self._r}.insecureConnectionsText') ), # displayname=bui.Lstr( # resource=f'{self._r}.alwaysUseInternalKeyboardText' # ), scale=1.0, maxwidth=430, ) bui.textwidget( parent=self._subcontainer, position=(90, v - 20), size=(0, 0), # text=( # 'not recommended, but may allow online play\n' # 'from restricted countries or networks' # ), text=bui.Lstr( resource=(f'{self._r}.insecureConnectionsDescriptionText') ), maxwidth=400, flatness=1.0, scale=0.65, color=(0.4, 0.9, 0.4, 0.8), h_align='left', v_align='center', ) v -= 40 else: self._use_insecure_connections_check_box = None self._always_use_internal_keyboard_check_box: ConfigCheckBox | None if self._show_always_use_internal_keyboard: v -= 42 self._always_use_internal_keyboard_check_box = ConfigCheckBox( parent=self._subcontainer, position=(50, v), size=(self._sub_width - 100, 30), configkey='Always Use Internal Keyboard', autoselect=True, displayname=bui.Lstr( resource=f'{self._r}.alwaysUseInternalKeyboardText' ), scale=1.0, maxwidth=430, ) bui.textwidget( parent=self._subcontainer, position=(90, v - 10), size=(0, 0), text=bui.Lstr( resource=( f'{self._r}.alwaysUseInternalKeyboardDescriptionText' ) ), maxwidth=400, flatness=1.0, scale=0.65, color=(0.4, 0.9, 0.4, 0.8), h_align='left', v_align='center', ) v -= 20 else: self._always_use_internal_keyboard_check_box = None v -= self._spacing * 2.1 this_button_width = 410 self._modding_guide_button = bui.buttonwidget( parent=self._subcontainer, position=(self._sub_width / 2 - this_button_width / 2, v - 10), size=(this_button_width, 60), autoselect=True, label=bui.Lstr(resource=f'{self._r}.moddingGuideText'), text_scale=1.0, on_activate_call=bui.Call( bui.open_url, 'https://ballistica.net/wiki/modding-guide' ), ) v -= self._spacing * 2.0 self._dev_tools_button = bui.buttonwidget( parent=self._subcontainer, position=(self._sub_width / 2 - this_button_width / 2, v - 10), size=(this_button_width, 60), autoselect=True, label=bui.Lstr(resource=f'{self._r}.devToolsText'), text_scale=1.0, on_activate_call=self._on_dev_tools_button_press, ) if self._show_always_use_internal_keyboard: assert self._always_use_internal_keyboard_check_box is not None bui.widget( edit=self._always_use_internal_keyboard_check_box.widget, down_widget=self._modding_guide_button, ) bui.widget( edit=self._modding_guide_button, up_widget=self._always_use_internal_keyboard_check_box.widget, ) else: # ew. next_widget_up = ( self._disable_gyro_check_box.widget if self._disable_gyro_check_box is not None else self._disable_camera_shake_check_box.widget ) bui.widget( edit=self._modding_guide_button, up_widget=next_widget_up, ) bui.widget( edit=next_widget_up, down_widget=self._modding_guide_button, ) v -= self._spacing * 2.0 self._show_user_mods_button = bui.buttonwidget( parent=self._subcontainer, position=(self._sub_width / 2 - this_button_width / 2, v - 10), size=(this_button_width, 60), autoselect=True, label=bui.Lstr(resource=f'{self._r}.showUserModsText'), text_scale=1.0, on_activate_call=show_user_scripts, ) v -= self._spacing * 2.0 self._plugins_button = bui.buttonwidget( parent=self._subcontainer, position=(self._sub_width / 2 - this_button_width / 2, v - 10), size=(this_button_width, 60), autoselect=True, label=bui.Lstr(resource='pluginsText'), text_scale=1.0, on_activate_call=self._on_plugins_button_press, ) v -= self._spacing * 0.6 self._vr_test_button: bui.Widget | None if self._do_vr_test_button: v -= self._extra_button_spacing self._vr_test_button = bui.buttonwidget( parent=self._subcontainer, position=(self._sub_width / 2 - this_button_width / 2, v - 14), size=(this_button_width, 60), autoselect=True, label=bui.Lstr(resource=f'{self._r}.vrTestingText'), text_scale=1.0, on_activate_call=self._on_vr_test_press, ) else: self._vr_test_button = None self._net_test_button: bui.Widget | None if self._do_net_test_button: v -= self._extra_button_spacing self._net_test_button = bui.buttonwidget( parent=self._subcontainer, position=(self._sub_width / 2 - this_button_width / 2, v - 14), size=(this_button_width, 60), autoselect=True, label=bui.Lstr(resource=f'{self._r}.netTestingText'), text_scale=1.0, on_activate_call=self._on_net_test_press, ) else: self._net_test_button = None v -= 70 self._benchmarks_button = bui.buttonwidget( parent=self._subcontainer, position=(self._sub_width / 2 - this_button_width / 2, v - 14), size=(this_button_width, 60), autoselect=True, label=bui.Lstr(resource=f'{self._r}.benchmarksText'), text_scale=1.0, on_activate_call=self._on_benchmark_press, ) v -= 100 self._send_info_button = bui.buttonwidget( parent=self._subcontainer, position=(self._sub_width / 2 - this_button_width / 2, v - 14), size=(this_button_width, 60), autoselect=True, label=bui.Lstr(resource=f'{self._r}.sendInfoText'), text_scale=1.0, on_activate_call=self._on_send_info_press, ) for child in self._subcontainer.get_children(): bui.widget(edit=child, show_buffer_bottom=30, show_buffer_top=20) pbtn = bui.get_special_widget('squad_button') bui.widget(edit=self._scrollwidget, right_widget=pbtn) if self._back_button is None: bui.widget( edit=self._scrollwidget, left_widget=bui.get_special_widget('back_button'), ) self._restore_state() def _show_restart_needed(self, value: Any) -> None: del value # Unused. bui.screenmessage( bui.Lstr(resource=f'{self._r}.mustRestartText'), color=(1, 1, 0) ) def _on_lang_inform_value_change(self, val: bool) -> None: plus = bui.app.plus assert plus is not None plus.add_v1_account_transaction( {'type': 'SET_MISC_VAL', 'name': 'langInform', 'value': val} ) plus.run_v1_account_transactions() def _on_vr_test_press(self) -> None: from bauiv1lib.settings.vrtesting import VRTestingWindow # no-op if we're not in control. if not self.main_window_has_control(): return self.main_window_replace(VRTestingWindow(transition='in_right')) def _on_net_test_press(self) -> None: from bauiv1lib.settings.nettesting import NetTestingWindow # no-op if we're not in control. if not self.main_window_has_control(): return self.main_window_replace(NetTestingWindow(transition='in_right')) def _on_friend_promo_code_press(self) -> None: from bauiv1lib import appinvite from bauiv1lib.account.signin import show_sign_in_prompt plus = bui.app.plus assert plus is not None if plus.get_v1_account_state() != 'signed_in': show_sign_in_prompt() return appinvite.handle_app_invites_press() def _on_plugins_button_press(self) -> None: from bauiv1lib.settings.plugins import PluginWindow # no-op if we're not in control. if not self.main_window_has_control(): return self.main_window_replace( PluginWindow(origin_widget=self._plugins_button) ) def _on_dev_tools_button_press(self) -> None: # pylint: disable=cyclic-import from bauiv1lib.settings.devtools import DevToolsWindow # no-op if we're not in control. if not self.main_window_has_control(): return self.main_window_replace( DevToolsWindow(origin_widget=self._dev_tools_button) ) def _on_send_info_press(self) -> None: from bauiv1lib.sendinfo import SendInfoWindow # no-op if we're not in control. if not self.main_window_has_control(): return self.main_window_replace( SendInfoWindow(origin_widget=self._send_info_button) ) def _on_benchmark_press(self) -> None: from bauiv1lib.settings.benchmarks import BenchmarksAndStressTestsWindow # no-op if we're not in control. if not self.main_window_has_control(): return self.main_window_replace( BenchmarksAndStressTestsWindow(transition='in_right') ) def _save_state(self) -> None: # pylint: disable=too-many-branches # pylint: disable=too-many-statements try: sel = self._root_widget.get_selected_child() if sel == self._scrollwidget: sel = self._subcontainer.get_selected_child() if sel == self._vr_test_button: sel_name = 'VRTest' elif sel == self._net_test_button: sel_name = 'NetTest' elif sel == self._send_info_button: sel_name = 'SendInfo' elif sel == self._benchmarks_button: sel_name = 'Benchmarks' elif sel == self._kick_idle_players_check_box.widget: sel_name = 'KickIdlePlayers' elif sel == self._show_demos_when_idle_check_box.widget: sel_name = 'ShowDemosWhenIdle' elif sel == self._show_deprecated_login_types_check_box.widget: sel_name = 'ShowDeprecatedLoginTypes' elif sel == self._show_game_ping_check_box.widget: sel_name = 'ShowPing' elif sel == self._disable_camera_shake_check_box.widget: sel_name = 'DisableCameraShake' elif ( self._always_use_internal_keyboard_check_box is not None and sel == self._always_use_internal_keyboard_check_box.widget ): sel_name = 'AlwaysUseInternalKeyboard' elif ( self._use_insecure_connections_check_box is not None and sel == self._use_insecure_connections_check_box.widget ): sel_name = 'UseInsecureConnections' elif ( self._disable_gyro_check_box is not None and sel == self._disable_gyro_check_box.widget ): sel_name = 'DisableGyro' elif ( self._language_popup is not None and sel == self._language_popup.get_button() ): sel_name = 'Languages' elif sel == self._translation_editor_button: sel_name = 'TranslationEditor' elif sel == self._show_user_mods_button: sel_name = 'ShowUserMods' elif sel == self._plugins_button: sel_name = 'Plugins' elif sel == self._dev_tools_button: sel_name = 'DevTools' elif sel == self._modding_guide_button: sel_name = 'ModdingGuide' elif sel == self._language_inform_checkbox: sel_name = 'LangInform' else: raise ValueError(f'unrecognized selection \'{sel}\'') elif sel == self._back_button: sel_name = 'Back' else: raise ValueError(f'unrecognized selection \'{sel}\'') assert bui.app.classic is not None bui.app.ui_v1.window_states[type(self)] = {'sel_name': sel_name} except Exception: logging.exception('Error saving state for %s.', self) def _restore_state(self) -> None: # pylint: disable=too-many-branches # pylint: disable=too-many-statements try: assert bui.app.classic is not None sel_name = bui.app.ui_v1.window_states.get(type(self), {}).get( 'sel_name' ) if sel_name == 'Back': sel = self._back_button else: bui.containerwidget( edit=self._root_widget, selected_child=self._scrollwidget ) if sel_name == 'VRTest': sel = self._vr_test_button elif sel_name == 'NetTest': sel = self._net_test_button elif sel_name == 'SendInfo': sel = self._send_info_button elif sel_name == 'Benchmarks': sel = self._benchmarks_button elif sel_name == 'KickIdlePlayers': sel = self._kick_idle_players_check_box.widget elif sel_name == 'ShowDemosWhenIdle': sel = self._show_demos_when_idle_check_box.widget elif sel_name == 'ShowDeprecatedLoginTypes': sel = self._show_deprecated_login_types_check_box.widget elif sel_name == 'ShowPing': sel = self._show_game_ping_check_box.widget elif sel_name == 'DisableCameraShake': sel = self._disable_camera_shake_check_box.widget elif ( sel_name == 'AlwaysUseInternalKeyboard' and self._always_use_internal_keyboard_check_box is not None ): sel = self._always_use_internal_keyboard_check_box.widget elif ( sel_name == 'UseInsecureConnections' and self._use_insecure_connections_check_box is not None ): sel = self._use_insecure_connections_check_box.widget elif ( sel_name == 'DisableGyro' and self._disable_gyro_check_box is not None ): sel = self._disable_gyro_check_box.widget elif ( sel_name == 'Languages' and self._language_popup is not None ): sel = self._language_popup.get_button() elif sel_name == 'TranslationEditor': sel = self._translation_editor_button elif sel_name == 'ShowUserMods': sel = self._show_user_mods_button elif sel_name == 'Plugins': sel = self._plugins_button elif sel_name == 'DevTools': sel = self._dev_tools_button elif sel_name == 'ModdingGuide': sel = self._modding_guide_button elif sel_name == 'LangInform': sel = self._language_inform_checkbox else: sel = None if sel is not None: bui.containerwidget( edit=self._subcontainer, selected_child=sel, visible_child=sel, ) except Exception: logging.exception('Error restoring state for %s.', self) def _on_menu_open(self) -> None: self._menu_open = True def _on_menu_close(self) -> None: self._menu_open = False def _on_menu_choice(self, choice: str) -> None: cfg = bui.app.config cfgkey = 'Lang' if choice == 'Auto': if cfgkey in cfg: del cfg[cfgkey] else: cfg[cfgkey] = choice cfg.apply_and_commit() self._save_state() # bui.app.lang.setlanguage(None if choice == 'Auto' else choice) bui.apptimer(0.1, bui.WeakCall(self._rebuild)) def _completed_langs_cb(self, results: dict[str, Any] | None) -> None: if results is not None and results['langs'] is not None: self._complete_langs_list = results['langs'] self._complete_langs_error = False else: self._complete_langs_list = None self._complete_langs_error = True bui.apptimer(0.001, bui.WeakCall(self._update_lang_status))
# Docs-generation hack; import some stuff that we likely only forward-declared # in our actual source code so that docs tools can find it. from typing import (Coroutine, Any, Literal, Callable, Generator, Awaitable, Sequence, Self) import asyncio from concurrent.futures import Future from pathlib import Path from enum import Enum