bauiv1lib.settings.audio
Provides audio settings UI.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Provides audio settings UI.""" 4 5from __future__ import annotations 6 7from typing import TYPE_CHECKING, override 8import logging 9 10import bauiv1 as bui 11 12if TYPE_CHECKING: 13 pass 14 15 16class AudioSettingsWindow(bui.MainWindow): 17 """Window for editing audio settings.""" 18 19 def __init__( 20 self, 21 transition: str | None = 'in_right', 22 origin_widget: bui.Widget | None = None, 23 ): 24 # pylint: disable=too-many-locals 25 # pylint: disable=cyclic-import 26 from bauiv1lib.config import ConfigNumberEdit 27 28 assert bui.app.classic is not None 29 music = bui.app.classic.music 30 31 self._r = 'audioSettingsWindow' 32 33 spacing = 50.0 34 uiscale = bui.app.ui_v1.uiscale 35 36 width = 1200.0 if uiscale is bui.UIScale.SMALL else 500.0 37 height = 800.0 if uiscale is bui.UIScale.SMALL else 350.0 38 39 show_soundtracks = False 40 if music.have_music_player(): 41 show_soundtracks = True 42 43 # Do some fancy math to fill all available screen area up to the 44 # size of our backing container. This lets us fit to the exact 45 # screen shape at small ui scale. 46 screensize = bui.get_virtual_screen_size() 47 scale = ( 48 2.2 49 if uiscale is bui.UIScale.SMALL 50 else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0 51 ) 52 # Calc screen size in our local container space and clamp to a 53 # bit smaller than our container size. 54 # target_width = min(width - 60, screensize[0] / scale) 55 target_height = min(height - 70, screensize[1] / scale) 56 57 # To get top/left coords, go to the center of our window and 58 # offset by half the width/height of our target area. 59 yoffs = 0.5 * height + 0.5 * target_height + 30.0 60 61 super().__init__( 62 root_widget=bui.containerwidget( 63 size=(width, height), 64 scale=scale, 65 toolbar_visibility=( 66 'menu_minimal' 67 if uiscale is bui.UIScale.SMALL 68 else 'menu_full' 69 ), 70 ), 71 transition=transition, 72 origin_widget=origin_widget, 73 # We're affected by screen size only at small ui-scale. 74 refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, 75 ) 76 77 if uiscale is bui.UIScale.SMALL: 78 bui.containerwidget( 79 edit=self._root_widget, on_cancel_call=self.main_window_back 80 ) 81 self._back_button = None 82 else: 83 self._back_button = bui.buttonwidget( 84 parent=self._root_widget, 85 position=(35, yoffs - 55), 86 size=(60, 60), 87 scale=0.8, 88 text_scale=1.2, 89 label=bui.charstr(bui.SpecialChar.BACK), 90 button_type='backSmall', 91 on_activate_call=self.main_window_back, 92 autoselect=True, 93 ) 94 bui.containerwidget( 95 edit=self._root_widget, cancel_button=self._back_button 96 ) 97 98 bui.textwidget( 99 parent=self._root_widget, 100 position=( 101 width * 0.5, 102 yoffs - (48 if uiscale is bui.UIScale.SMALL else 32), 103 ), 104 size=(0, 0), 105 text=bui.Lstr(resource=f'{self._r}.titleText'), 106 color=bui.app.ui_v1.title_color, 107 maxwidth=180, 108 h_align='center', 109 v_align='center', 110 ) 111 112 # Roughly center everything else in our window. 113 x = width * 0.5 - 160 114 y = height * 0.5 + (100 if show_soundtracks else 70) 115 y -= spacing * 1.0 116 117 self._sound_volume_numedit = svne = ConfigNumberEdit( 118 parent=self._root_widget, 119 position=(x, y), 120 xoffset=10, 121 configkey='Sound Volume', 122 displayname=bui.Lstr(resource=f'{self._r}.soundVolumeText'), 123 minval=0.0, 124 maxval=1.0, 125 increment=0.05, 126 as_percent=True, 127 ) 128 bui.widget( 129 edit=svne.plusbutton, 130 right_widget=bui.get_special_widget('squad_button'), 131 ) 132 y -= spacing 133 self._music_volume_numedit = ConfigNumberEdit( 134 parent=self._root_widget, 135 position=(x, y), 136 xoffset=10, 137 configkey='Music Volume', 138 displayname=bui.Lstr(resource=f'{self._r}.musicVolumeText'), 139 minval=0.0, 140 maxval=1.0, 141 increment=0.05, 142 callback=music.music_volume_changed, 143 changesound=False, 144 as_percent=True, 145 ) 146 147 y -= 0.5 * spacing 148 149 self._soundtrack_button: bui.Widget | None 150 if show_soundtracks: 151 y -= 1.2 * spacing 152 self._soundtrack_button = bui.buttonwidget( 153 parent=self._root_widget, 154 position=(width * 0.5 - 155, y), 155 size=(310, 50), 156 autoselect=True, 157 label=bui.Lstr(resource=f'{self._r}.soundtrackButtonText'), 158 on_activate_call=self._do_soundtracks, 159 ) 160 y -= spacing * 0.3 161 bui.textwidget( 162 parent=self._root_widget, 163 position=(0.5 * width, y), 164 size=(0.0, 0.0), 165 text=bui.Lstr(resource=f'{self._r}.soundtrackDescriptionText'), 166 flatness=1.0, 167 h_align='center', 168 v_align='center', 169 maxwidth=400, 170 scale=0.5, 171 color=(0.7, 0.8, 0.7, 1.0), 172 ) 173 else: 174 self._soundtrack_button = None 175 176 # Tweak a few navigation bits. 177 if self._back_button is not None: 178 bui.widget(edit=self._back_button, down_widget=svne.minusbutton) 179 else: 180 spback = bui.get_special_widget('back_button') 181 bui.widget( 182 edit=svne.minusbutton, up_widget=spback, left_widget=spback 183 ) 184 185 self._restore_state() 186 187 @override 188 def get_main_window_state(self) -> bui.MainWindowState: 189 # Support recreating our window for back/refresh purposes. 190 cls = type(self) 191 return bui.BasicMainWindowState( 192 create_call=lambda transition, origin_widget: cls( 193 transition=transition, origin_widget=origin_widget 194 ) 195 ) 196 197 @override 198 def on_main_window_close(self) -> None: 199 self._save_state() 200 201 def _do_soundtracks(self) -> None: 202 # pylint: disable=cyclic-import 203 from bauiv1lib.soundtrack.browser import SoundtrackBrowserWindow 204 205 # no-op if we're not in control. 206 if not self.main_window_has_control(): 207 return 208 209 # We require disk access for soundtracks; request it if we don't 210 # have it. 211 if not bui.have_permission(bui.Permission.STORAGE): 212 bui.getsound('ding').play() 213 bui.screenmessage( 214 bui.Lstr(resource='storagePermissionAccessText'), 215 color=(0.5, 1, 0.5), 216 ) 217 bui.apptimer( 218 1.0, bui.Call(bui.request_permission, bui.Permission.STORAGE) 219 ) 220 return 221 222 self.main_window_replace( 223 SoundtrackBrowserWindow(origin_widget=self._soundtrack_button) 224 ) 225 226 def _save_state(self) -> None: 227 try: 228 sel = self._root_widget.get_selected_child() 229 if sel == self._sound_volume_numedit.minusbutton: 230 sel_name = 'SoundMinus' 231 elif sel == self._sound_volume_numedit.plusbutton: 232 sel_name = 'SoundPlus' 233 elif sel == self._music_volume_numedit.minusbutton: 234 sel_name = 'MusicMinus' 235 elif sel == self._music_volume_numedit.plusbutton: 236 sel_name = 'MusicPlus' 237 elif sel == self._soundtrack_button: 238 sel_name = 'Soundtrack' 239 elif sel == self._back_button: 240 sel_name = 'Back' 241 else: 242 raise ValueError(f'unrecognized selection \'{sel}\'') 243 assert bui.app.classic is not None 244 bui.app.ui_v1.window_states[type(self)] = sel_name 245 except Exception: 246 logging.exception('Error saving state for %s.', self) 247 248 def _restore_state(self) -> None: 249 try: 250 assert bui.app.classic is not None 251 sel_name = bui.app.ui_v1.window_states.get(type(self)) 252 sel: bui.Widget | None 253 if sel_name == 'SoundMinus': 254 sel = self._sound_volume_numedit.minusbutton 255 elif sel_name == 'SoundPlus': 256 sel = self._sound_volume_numedit.plusbutton 257 elif sel_name == 'MusicMinus': 258 sel = self._music_volume_numedit.minusbutton 259 elif sel_name == 'MusicPlus': 260 sel = self._music_volume_numedit.plusbutton 261 elif sel_name == 'Soundtrack': 262 sel = self._soundtrack_button 263 elif sel_name == 'Back': 264 sel = self._back_button 265 else: 266 sel = self._back_button 267 if sel: 268 bui.containerwidget(edit=self._root_widget, selected_child=sel) 269 except Exception: 270 logging.exception('Error restoring state for %s.', self)
class
AudioSettingsWindow(bauiv1._uitypes.MainWindow):
17class AudioSettingsWindow(bui.MainWindow): 18 """Window for editing audio settings.""" 19 20 def __init__( 21 self, 22 transition: str | None = 'in_right', 23 origin_widget: bui.Widget | None = None, 24 ): 25 # pylint: disable=too-many-locals 26 # pylint: disable=cyclic-import 27 from bauiv1lib.config import ConfigNumberEdit 28 29 assert bui.app.classic is not None 30 music = bui.app.classic.music 31 32 self._r = 'audioSettingsWindow' 33 34 spacing = 50.0 35 uiscale = bui.app.ui_v1.uiscale 36 37 width = 1200.0 if uiscale is bui.UIScale.SMALL else 500.0 38 height = 800.0 if uiscale is bui.UIScale.SMALL else 350.0 39 40 show_soundtracks = False 41 if music.have_music_player(): 42 show_soundtracks = True 43 44 # Do some fancy math to fill all available screen area up to the 45 # size of our backing container. This lets us fit to the exact 46 # screen shape at small ui scale. 47 screensize = bui.get_virtual_screen_size() 48 scale = ( 49 2.2 50 if uiscale is bui.UIScale.SMALL 51 else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0 52 ) 53 # Calc screen size in our local container space and clamp to a 54 # bit smaller than our container size. 55 # target_width = min(width - 60, screensize[0] / scale) 56 target_height = min(height - 70, screensize[1] / scale) 57 58 # To get top/left coords, go to the center of our window and 59 # offset by half the width/height of our target area. 60 yoffs = 0.5 * height + 0.5 * target_height + 30.0 61 62 super().__init__( 63 root_widget=bui.containerwidget( 64 size=(width, height), 65 scale=scale, 66 toolbar_visibility=( 67 'menu_minimal' 68 if uiscale is bui.UIScale.SMALL 69 else 'menu_full' 70 ), 71 ), 72 transition=transition, 73 origin_widget=origin_widget, 74 # We're affected by screen size only at small ui-scale. 75 refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, 76 ) 77 78 if uiscale is bui.UIScale.SMALL: 79 bui.containerwidget( 80 edit=self._root_widget, on_cancel_call=self.main_window_back 81 ) 82 self._back_button = None 83 else: 84 self._back_button = bui.buttonwidget( 85 parent=self._root_widget, 86 position=(35, yoffs - 55), 87 size=(60, 60), 88 scale=0.8, 89 text_scale=1.2, 90 label=bui.charstr(bui.SpecialChar.BACK), 91 button_type='backSmall', 92 on_activate_call=self.main_window_back, 93 autoselect=True, 94 ) 95 bui.containerwidget( 96 edit=self._root_widget, cancel_button=self._back_button 97 ) 98 99 bui.textwidget( 100 parent=self._root_widget, 101 position=( 102 width * 0.5, 103 yoffs - (48 if uiscale is bui.UIScale.SMALL else 32), 104 ), 105 size=(0, 0), 106 text=bui.Lstr(resource=f'{self._r}.titleText'), 107 color=bui.app.ui_v1.title_color, 108 maxwidth=180, 109 h_align='center', 110 v_align='center', 111 ) 112 113 # Roughly center everything else in our window. 114 x = width * 0.5 - 160 115 y = height * 0.5 + (100 if show_soundtracks else 70) 116 y -= spacing * 1.0 117 118 self._sound_volume_numedit = svne = ConfigNumberEdit( 119 parent=self._root_widget, 120 position=(x, y), 121 xoffset=10, 122 configkey='Sound Volume', 123 displayname=bui.Lstr(resource=f'{self._r}.soundVolumeText'), 124 minval=0.0, 125 maxval=1.0, 126 increment=0.05, 127 as_percent=True, 128 ) 129 bui.widget( 130 edit=svne.plusbutton, 131 right_widget=bui.get_special_widget('squad_button'), 132 ) 133 y -= spacing 134 self._music_volume_numedit = ConfigNumberEdit( 135 parent=self._root_widget, 136 position=(x, y), 137 xoffset=10, 138 configkey='Music Volume', 139 displayname=bui.Lstr(resource=f'{self._r}.musicVolumeText'), 140 minval=0.0, 141 maxval=1.0, 142 increment=0.05, 143 callback=music.music_volume_changed, 144 changesound=False, 145 as_percent=True, 146 ) 147 148 y -= 0.5 * spacing 149 150 self._soundtrack_button: bui.Widget | None 151 if show_soundtracks: 152 y -= 1.2 * spacing 153 self._soundtrack_button = bui.buttonwidget( 154 parent=self._root_widget, 155 position=(width * 0.5 - 155, y), 156 size=(310, 50), 157 autoselect=True, 158 label=bui.Lstr(resource=f'{self._r}.soundtrackButtonText'), 159 on_activate_call=self._do_soundtracks, 160 ) 161 y -= spacing * 0.3 162 bui.textwidget( 163 parent=self._root_widget, 164 position=(0.5 * width, y), 165 size=(0.0, 0.0), 166 text=bui.Lstr(resource=f'{self._r}.soundtrackDescriptionText'), 167 flatness=1.0, 168 h_align='center', 169 v_align='center', 170 maxwidth=400, 171 scale=0.5, 172 color=(0.7, 0.8, 0.7, 1.0), 173 ) 174 else: 175 self._soundtrack_button = None 176 177 # Tweak a few navigation bits. 178 if self._back_button is not None: 179 bui.widget(edit=self._back_button, down_widget=svne.minusbutton) 180 else: 181 spback = bui.get_special_widget('back_button') 182 bui.widget( 183 edit=svne.minusbutton, up_widget=spback, left_widget=spback 184 ) 185 186 self._restore_state() 187 188 @override 189 def get_main_window_state(self) -> bui.MainWindowState: 190 # Support recreating our window for back/refresh purposes. 191 cls = type(self) 192 return bui.BasicMainWindowState( 193 create_call=lambda transition, origin_widget: cls( 194 transition=transition, origin_widget=origin_widget 195 ) 196 ) 197 198 @override 199 def on_main_window_close(self) -> None: 200 self._save_state() 201 202 def _do_soundtracks(self) -> None: 203 # pylint: disable=cyclic-import 204 from bauiv1lib.soundtrack.browser import SoundtrackBrowserWindow 205 206 # no-op if we're not in control. 207 if not self.main_window_has_control(): 208 return 209 210 # We require disk access for soundtracks; request it if we don't 211 # have it. 212 if not bui.have_permission(bui.Permission.STORAGE): 213 bui.getsound('ding').play() 214 bui.screenmessage( 215 bui.Lstr(resource='storagePermissionAccessText'), 216 color=(0.5, 1, 0.5), 217 ) 218 bui.apptimer( 219 1.0, bui.Call(bui.request_permission, bui.Permission.STORAGE) 220 ) 221 return 222 223 self.main_window_replace( 224 SoundtrackBrowserWindow(origin_widget=self._soundtrack_button) 225 ) 226 227 def _save_state(self) -> None: 228 try: 229 sel = self._root_widget.get_selected_child() 230 if sel == self._sound_volume_numedit.minusbutton: 231 sel_name = 'SoundMinus' 232 elif sel == self._sound_volume_numedit.plusbutton: 233 sel_name = 'SoundPlus' 234 elif sel == self._music_volume_numedit.minusbutton: 235 sel_name = 'MusicMinus' 236 elif sel == self._music_volume_numedit.plusbutton: 237 sel_name = 'MusicPlus' 238 elif sel == self._soundtrack_button: 239 sel_name = 'Soundtrack' 240 elif sel == self._back_button: 241 sel_name = 'Back' 242 else: 243 raise ValueError(f'unrecognized selection \'{sel}\'') 244 assert bui.app.classic is not None 245 bui.app.ui_v1.window_states[type(self)] = sel_name 246 except Exception: 247 logging.exception('Error saving state for %s.', self) 248 249 def _restore_state(self) -> None: 250 try: 251 assert bui.app.classic is not None 252 sel_name = bui.app.ui_v1.window_states.get(type(self)) 253 sel: bui.Widget | None 254 if sel_name == 'SoundMinus': 255 sel = self._sound_volume_numedit.minusbutton 256 elif sel_name == 'SoundPlus': 257 sel = self._sound_volume_numedit.plusbutton 258 elif sel_name == 'MusicMinus': 259 sel = self._music_volume_numedit.minusbutton 260 elif sel_name == 'MusicPlus': 261 sel = self._music_volume_numedit.plusbutton 262 elif sel_name == 'Soundtrack': 263 sel = self._soundtrack_button 264 elif sel_name == 'Back': 265 sel = self._back_button 266 else: 267 sel = self._back_button 268 if sel: 269 bui.containerwidget(edit=self._root_widget, selected_child=sel) 270 except Exception: 271 logging.exception('Error restoring state for %s.', self)
Window for editing audio settings.
AudioSettingsWindow( transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
20 def __init__( 21 self, 22 transition: str | None = 'in_right', 23 origin_widget: bui.Widget | None = None, 24 ): 25 # pylint: disable=too-many-locals 26 # pylint: disable=cyclic-import 27 from bauiv1lib.config import ConfigNumberEdit 28 29 assert bui.app.classic is not None 30 music = bui.app.classic.music 31 32 self._r = 'audioSettingsWindow' 33 34 spacing = 50.0 35 uiscale = bui.app.ui_v1.uiscale 36 37 width = 1200.0 if uiscale is bui.UIScale.SMALL else 500.0 38 height = 800.0 if uiscale is bui.UIScale.SMALL else 350.0 39 40 show_soundtracks = False 41 if music.have_music_player(): 42 show_soundtracks = True 43 44 # Do some fancy math to fill all available screen area up to the 45 # size of our backing container. This lets us fit to the exact 46 # screen shape at small ui scale. 47 screensize = bui.get_virtual_screen_size() 48 scale = ( 49 2.2 50 if uiscale is bui.UIScale.SMALL 51 else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0 52 ) 53 # Calc screen size in our local container space and clamp to a 54 # bit smaller than our container size. 55 # target_width = min(width - 60, screensize[0] / scale) 56 target_height = min(height - 70, screensize[1] / scale) 57 58 # To get top/left coords, go to the center of our window and 59 # offset by half the width/height of our target area. 60 yoffs = 0.5 * height + 0.5 * target_height + 30.0 61 62 super().__init__( 63 root_widget=bui.containerwidget( 64 size=(width, height), 65 scale=scale, 66 toolbar_visibility=( 67 'menu_minimal' 68 if uiscale is bui.UIScale.SMALL 69 else 'menu_full' 70 ), 71 ), 72 transition=transition, 73 origin_widget=origin_widget, 74 # We're affected by screen size only at small ui-scale. 75 refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, 76 ) 77 78 if uiscale is bui.UIScale.SMALL: 79 bui.containerwidget( 80 edit=self._root_widget, on_cancel_call=self.main_window_back 81 ) 82 self._back_button = None 83 else: 84 self._back_button = bui.buttonwidget( 85 parent=self._root_widget, 86 position=(35, yoffs - 55), 87 size=(60, 60), 88 scale=0.8, 89 text_scale=1.2, 90 label=bui.charstr(bui.SpecialChar.BACK), 91 button_type='backSmall', 92 on_activate_call=self.main_window_back, 93 autoselect=True, 94 ) 95 bui.containerwidget( 96 edit=self._root_widget, cancel_button=self._back_button 97 ) 98 99 bui.textwidget( 100 parent=self._root_widget, 101 position=( 102 width * 0.5, 103 yoffs - (48 if uiscale is bui.UIScale.SMALL else 32), 104 ), 105 size=(0, 0), 106 text=bui.Lstr(resource=f'{self._r}.titleText'), 107 color=bui.app.ui_v1.title_color, 108 maxwidth=180, 109 h_align='center', 110 v_align='center', 111 ) 112 113 # Roughly center everything else in our window. 114 x = width * 0.5 - 160 115 y = height * 0.5 + (100 if show_soundtracks else 70) 116 y -= spacing * 1.0 117 118 self._sound_volume_numedit = svne = ConfigNumberEdit( 119 parent=self._root_widget, 120 position=(x, y), 121 xoffset=10, 122 configkey='Sound Volume', 123 displayname=bui.Lstr(resource=f'{self._r}.soundVolumeText'), 124 minval=0.0, 125 maxval=1.0, 126 increment=0.05, 127 as_percent=True, 128 ) 129 bui.widget( 130 edit=svne.plusbutton, 131 right_widget=bui.get_special_widget('squad_button'), 132 ) 133 y -= spacing 134 self._music_volume_numedit = ConfigNumberEdit( 135 parent=self._root_widget, 136 position=(x, y), 137 xoffset=10, 138 configkey='Music Volume', 139 displayname=bui.Lstr(resource=f'{self._r}.musicVolumeText'), 140 minval=0.0, 141 maxval=1.0, 142 increment=0.05, 143 callback=music.music_volume_changed, 144 changesound=False, 145 as_percent=True, 146 ) 147 148 y -= 0.5 * spacing 149 150 self._soundtrack_button: bui.Widget | None 151 if show_soundtracks: 152 y -= 1.2 * spacing 153 self._soundtrack_button = bui.buttonwidget( 154 parent=self._root_widget, 155 position=(width * 0.5 - 155, y), 156 size=(310, 50), 157 autoselect=True, 158 label=bui.Lstr(resource=f'{self._r}.soundtrackButtonText'), 159 on_activate_call=self._do_soundtracks, 160 ) 161 y -= spacing * 0.3 162 bui.textwidget( 163 parent=self._root_widget, 164 position=(0.5 * width, y), 165 size=(0.0, 0.0), 166 text=bui.Lstr(resource=f'{self._r}.soundtrackDescriptionText'), 167 flatness=1.0, 168 h_align='center', 169 v_align='center', 170 maxwidth=400, 171 scale=0.5, 172 color=(0.7, 0.8, 0.7, 1.0), 173 ) 174 else: 175 self._soundtrack_button = None 176 177 # Tweak a few navigation bits. 178 if self._back_button is not None: 179 bui.widget(edit=self._back_button, down_widget=svne.minusbutton) 180 else: 181 spback = bui.get_special_widget('back_button') 182 bui.widget( 183 edit=svne.minusbutton, up_widget=spback, left_widget=spback 184 ) 185 186 self._restore_state()
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.
188 @override 189 def get_main_window_state(self) -> bui.MainWindowState: 190 # Support recreating our window for back/refresh purposes. 191 cls = type(self) 192 return bui.BasicMainWindowState( 193 create_call=lambda transition, origin_widget: cls( 194 transition=transition, origin_widget=origin_widget 195 ) 196 )
Return a WindowState to recreate this window, if supported.