bauiv1lib.credits
Provides a window to display game credits.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Provides a window to display game credits.""" 4 5from __future__ import annotations 6 7import os 8import json 9import logging 10from typing import TYPE_CHECKING, override 11 12import bauiv1 as bui 13 14if TYPE_CHECKING: 15 from typing import Sequence 16 17 18class CreditsWindow(bui.MainWindow): 19 """Window for displaying game credits.""" 20 21 def __init__( 22 self, 23 transition: str | None = 'in_right', 24 origin_widget: bui.Widget | None = None, 25 ): 26 # pylint: disable=too-many-locals 27 # pylint: disable=too-many-statements 28 29 bui.set_analytics_screen('Credits Window') 30 31 assert bui.app.classic is not None 32 uiscale = bui.app.ui_v1.uiscale 33 width = 990 if uiscale is bui.UIScale.SMALL else 670 34 x_inset = 100 if uiscale is bui.UIScale.SMALL else 0 35 height = 450 if uiscale is bui.UIScale.SMALL else 500 36 37 self._r = 'creditsWindow' 38 super().__init__( 39 root_widget=bui.containerwidget( 40 size=(width, height), 41 toolbar_visibility=( 42 'menu_minimal' 43 if uiscale is bui.UIScale.SMALL 44 else 'menu_full' 45 ), 46 scale=( 47 1.8 48 if uiscale is bui.UIScale.SMALL 49 else 1.2 if uiscale is bui.UIScale.MEDIUM else 1.0 50 ), 51 stack_offset=( 52 (0, 0) if uiscale is bui.UIScale.SMALL else (0, 0) 53 ), 54 ), 55 transition=transition, 56 origin_widget=origin_widget, 57 ) 58 59 if uiscale is bui.UIScale.SMALL: 60 bui.containerwidget( 61 edit=self._root_widget, on_cancel_call=self.main_window_back 62 ) 63 else: 64 btn = bui.buttonwidget( 65 parent=self._root_widget, 66 position=(40 + x_inset, height - 62), 67 size=(140, 60), 68 scale=0.8, 69 label=bui.Lstr(resource='backText'), 70 button_type='back', 71 on_activate_call=self.main_window_back, 72 autoselect=True, 73 ) 74 bui.containerwidget(edit=self._root_widget, cancel_button=btn) 75 76 bui.buttonwidget( 77 edit=btn, 78 button_type='backSmall', 79 position=( 80 40 + x_inset, 81 height - 62 + 5, 82 ), 83 size=(60, 48), 84 label=bui.charstr(bui.SpecialChar.BACK), 85 ) 86 87 bui.textwidget( 88 parent=self._root_widget, 89 position=(0, height - (93 if uiscale is bui.UIScale.SMALL else 54)), 90 size=(width, 30), 91 scale=0.8 if uiscale is bui.UIScale.SMALL else 1.0, 92 text=bui.Lstr( 93 resource=f'{self._r}.titleText', 94 subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))], 95 ), 96 h_align='center', 97 color=bui.app.ui_v1.title_color, 98 maxwidth=330, 99 v_align='center', 100 ) 101 102 scroll = bui.scrollwidget( 103 parent=self._root_widget, 104 position=(40 + x_inset, 62 if uiscale is bui.UIScale.SMALL else 35), 105 size=( 106 width - (80 + 2 * x_inset), 107 height - (160 if uiscale is bui.UIScale.SMALL else 100), 108 ), 109 capture_arrows=True, 110 border_opacity=0.4, 111 ) 112 113 bui.widget( 114 edit=scroll, 115 right_widget=bui.get_special_widget('squad_button'), 116 ) 117 if uiscale is bui.UIScale.SMALL: 118 bui.widget( 119 edit=scroll, 120 left_widget=bui.get_special_widget('back_button'), 121 ) 122 123 def _format_names(names2: Sequence[str], inset: float) -> str: 124 sval = '' 125 # measure a series since there's overlaps and stuff.. 126 space_width = ( 127 bui.get_string_width(' ' * 10, suppress_warning=True) / 10.0 128 ) 129 spacing = 330.0 130 col1 = inset 131 col2 = col1 + spacing 132 col3 = col2 + spacing 133 line_width = 0.0 134 nline = '' 135 for name in names2: 136 # move to the next column (or row) and print 137 if line_width > col3: 138 sval += nline + '\n' 139 nline = '' 140 line_width = 0 141 142 if line_width > col2: 143 target = col3 144 elif line_width > col1: 145 target = col2 146 else: 147 target = col1 148 spacingstr = ' ' * int((target - line_width) / space_width) 149 nline += spacingstr 150 nline += name 151 line_width = bui.get_string_width(nline, suppress_warning=True) 152 if nline != '': 153 sval += nline + '\n' 154 return sval 155 156 sound_and_music = bui.Lstr( 157 resource=f'{self._r}.songCreditText' 158 ).evaluate() 159 sound_and_music = sound_and_music.replace( 160 '${TITLE}', "'William Tell (Trumpet Entry)'" 161 ) 162 sound_and_music = sound_and_music.replace( 163 '${PERFORMER}', 'The Apollo Symphony Orchestra' 164 ) 165 sound_and_music = sound_and_music.replace( 166 '${PERFORMER}', 'The Apollo Symphony Orchestra' 167 ) 168 sound_and_music = sound_and_music.replace( 169 '${COMPOSER}', 'Gioacchino Rossini' 170 ) 171 sound_and_music = sound_and_music.replace('${ARRANGER}', 'Chris Worth') 172 sound_and_music = sound_and_music.replace('${PUBLISHER}', 'BMI') 173 sound_and_music = sound_and_music.replace( 174 '${SOURCE}', 'www.AudioSparx.com' 175 ) 176 spc = ' ' 177 sound_and_music = spc + sound_and_music.replace('\n', '\n' + spc) 178 names = [ 179 'HubOfTheUniverseProd', 180 'Jovica', 181 'LG', 182 'Leady', 183 'Percy Duke', 184 'PhreaKsAccount', 185 'Pogotron', 186 'Rock Savage', 187 'anamorphosis', 188 'benboncan', 189 'cdrk', 190 'chipfork', 191 'guitarguy1985', 192 'jascha', 193 'joedeshon', 194 'loofa', 195 'm_O_m', 196 'mich3d', 197 'sandyrb', 198 'shakaharu', 199 'sirplus', 200 'stickman', 201 'thanvannispen', 202 'virotic', 203 'zimbot', 204 ] 205 names.sort(key=lambda x: x.lower()) 206 freesound_names = _format_names(names, 90) 207 208 try: 209 with open( 210 os.path.join( 211 bui.app.env.data_directory, 212 'ba_data', 213 'data', 214 'langdata.json', 215 ), 216 encoding='utf-8', 217 ) as infile: 218 translation_contributors = json.loads(infile.read())[ 219 'translation_contributors' 220 ] 221 except Exception: 222 logging.exception('Error reading translation contributors.') 223 translation_contributors = [] 224 225 translation_names = _format_names(translation_contributors, 60) 226 227 # Need to bake this out and chop it up since we're passing our 228 # 65535 vertex limit for meshes.. 229 # We can remove that limit once we drop support for GL ES2.. :-/ 230 # (or add mesh splitting under the hood) 231 credits_text = ( 232 ' ' 233 + bui.Lstr(resource=f'{self._r}.codingGraphicsAudioText') 234 .evaluate() 235 .replace('${NAME}', 'Eric Froemling') 236 + '\n' 237 '\n' 238 ' ' 239 + bui.Lstr(resource=f'{self._r}.additionalAudioArtIdeasText') 240 .evaluate() 241 .replace('${NAME}', 'Raphael Suter') 242 + '\n' 243 '\n' 244 ' ' 245 + bui.Lstr(resource=f'{self._r}.soundAndMusicText').evaluate() 246 + '\n' 247 '\n' + sound_and_music + '\n' 248 '\n' 249 ' ' 250 + bui.Lstr(resource=f'{self._r}.publicDomainMusicViaText') 251 .evaluate() 252 .replace('${NAME}', 'Musopen.com') 253 + '\n' 254 ' ' 255 + bui.Lstr(resource=f'{self._r}.thanksEspeciallyToText') 256 .evaluate() 257 .replace('${NAME}', 'the US Army, Navy, and Marine Bands') 258 + '\n' 259 '\n' 260 ' ' 261 + bui.Lstr(resource=f'{self._r}.additionalMusicFromText') 262 .evaluate() 263 .replace('${NAME}', 'The YouTube Audio Library') 264 + '\n' 265 '\n' 266 ' ' 267 + bui.Lstr(resource=f'{self._r}.soundsText') 268 .evaluate() 269 .replace('${SOURCE}', 'Freesound.org') 270 + '\n' 271 '\n' + freesound_names + '\n' 272 '\n' 273 ' ' 274 + bui.Lstr( 275 resource=f'{self._r}.languageTranslationsText' 276 ).evaluate() 277 + '\n' 278 '\n' 279 + '\n'.join(translation_names.splitlines()[:146]) 280 + '\n'.join(translation_names.splitlines()[146:]) 281 + '\n' 282 '\n' 283 ' Shout Out to Awesome Mods / Modders / Contributors:\n\n' 284 ' BombDash ModPack\n' 285 ' TheMikirog & SoK - BombSquad Joyride Modpack\n' 286 ' Mrmaxmeier - BombSquad-Community-Mod-Manager\n' 287 ' Ritiek Malhotra \n' 288 ' Dliwk\n' 289 ' vishal332008\n' 290 ' itsre3\n' 291 ' Drooopyyy\n' 292 '\n' 293 ' Holiday theme vector art designed by Freepik\n' 294 '\n' 295 ' ' 296 + bui.Lstr(resource=f'{self._r}.specialThanksText').evaluate() 297 + '\n' 298 '\n' 299 ' Todd, Laura, and Robert Froemling\n' 300 ' ' 301 + bui.Lstr(resource=f'{self._r}.allMyFamilyText') 302 .evaluate() 303 .replace('\n', '\n ') 304 + '\n' 305 ' ' 306 + bui.Lstr( 307 resource=f'{self._r}.whoeverInventedCoffeeText' 308 ).evaluate() 309 + '\n' 310 '\n' 311 ' ' + bui.Lstr(resource=f'{self._r}.legalText').evaluate() + '\n' 312 '\n' 313 ' ' 314 + bui.Lstr(resource=f'{self._r}.softwareBasedOnText') 315 .evaluate() 316 .replace('${NAME}', 'the Khronos Group') 317 + '\n' 318 '\n' 319 ' ' 320 ' www.ballistica.net\n' 321 ) 322 323 txt = credits_text 324 lines = txt.splitlines() 325 line_height = 20 326 327 scale = 0.55 328 self._sub_width = width - 80 329 self._sub_height = line_height * len(lines) + 40 330 331 container = self._subcontainer = bui.containerwidget( 332 parent=scroll, 333 size=(self._sub_width, self._sub_height), 334 background=False, 335 claims_left_right=False, 336 ) 337 338 voffs = 0 339 for line in lines: 340 bui.textwidget( 341 parent=container, 342 padding=4, 343 color=(0.7, 0.9, 0.7, 1.0), 344 scale=scale, 345 flatness=1.0, 346 size=(0, 0), 347 position=(0, self._sub_height - 20 + voffs), 348 h_align='left', 349 v_align='top', 350 text=bui.Lstr(value=line), 351 ) 352 voffs -= line_height 353 354 @override 355 def get_main_window_state(self) -> bui.MainWindowState: 356 # Support recreating our window for back/refresh purposes. 357 cls = type(self) 358 return bui.BasicMainWindowState( 359 create_call=lambda transition, origin_widget: cls( 360 transition=transition, origin_widget=origin_widget 361 ) 362 )
class
CreditsWindow(bauiv1._uitypes.MainWindow):
19class CreditsWindow(bui.MainWindow): 20 """Window for displaying game credits.""" 21 22 def __init__( 23 self, 24 transition: str | None = 'in_right', 25 origin_widget: bui.Widget | None = None, 26 ): 27 # pylint: disable=too-many-locals 28 # pylint: disable=too-many-statements 29 30 bui.set_analytics_screen('Credits Window') 31 32 assert bui.app.classic is not None 33 uiscale = bui.app.ui_v1.uiscale 34 width = 990 if uiscale is bui.UIScale.SMALL else 670 35 x_inset = 100 if uiscale is bui.UIScale.SMALL else 0 36 height = 450 if uiscale is bui.UIScale.SMALL else 500 37 38 self._r = 'creditsWindow' 39 super().__init__( 40 root_widget=bui.containerwidget( 41 size=(width, height), 42 toolbar_visibility=( 43 'menu_minimal' 44 if uiscale is bui.UIScale.SMALL 45 else 'menu_full' 46 ), 47 scale=( 48 1.8 49 if uiscale is bui.UIScale.SMALL 50 else 1.2 if uiscale is bui.UIScale.MEDIUM else 1.0 51 ), 52 stack_offset=( 53 (0, 0) if uiscale is bui.UIScale.SMALL else (0, 0) 54 ), 55 ), 56 transition=transition, 57 origin_widget=origin_widget, 58 ) 59 60 if uiscale is bui.UIScale.SMALL: 61 bui.containerwidget( 62 edit=self._root_widget, on_cancel_call=self.main_window_back 63 ) 64 else: 65 btn = bui.buttonwidget( 66 parent=self._root_widget, 67 position=(40 + x_inset, height - 62), 68 size=(140, 60), 69 scale=0.8, 70 label=bui.Lstr(resource='backText'), 71 button_type='back', 72 on_activate_call=self.main_window_back, 73 autoselect=True, 74 ) 75 bui.containerwidget(edit=self._root_widget, cancel_button=btn) 76 77 bui.buttonwidget( 78 edit=btn, 79 button_type='backSmall', 80 position=( 81 40 + x_inset, 82 height - 62 + 5, 83 ), 84 size=(60, 48), 85 label=bui.charstr(bui.SpecialChar.BACK), 86 ) 87 88 bui.textwidget( 89 parent=self._root_widget, 90 position=(0, height - (93 if uiscale is bui.UIScale.SMALL else 54)), 91 size=(width, 30), 92 scale=0.8 if uiscale is bui.UIScale.SMALL else 1.0, 93 text=bui.Lstr( 94 resource=f'{self._r}.titleText', 95 subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))], 96 ), 97 h_align='center', 98 color=bui.app.ui_v1.title_color, 99 maxwidth=330, 100 v_align='center', 101 ) 102 103 scroll = bui.scrollwidget( 104 parent=self._root_widget, 105 position=(40 + x_inset, 62 if uiscale is bui.UIScale.SMALL else 35), 106 size=( 107 width - (80 + 2 * x_inset), 108 height - (160 if uiscale is bui.UIScale.SMALL else 100), 109 ), 110 capture_arrows=True, 111 border_opacity=0.4, 112 ) 113 114 bui.widget( 115 edit=scroll, 116 right_widget=bui.get_special_widget('squad_button'), 117 ) 118 if uiscale is bui.UIScale.SMALL: 119 bui.widget( 120 edit=scroll, 121 left_widget=bui.get_special_widget('back_button'), 122 ) 123 124 def _format_names(names2: Sequence[str], inset: float) -> str: 125 sval = '' 126 # measure a series since there's overlaps and stuff.. 127 space_width = ( 128 bui.get_string_width(' ' * 10, suppress_warning=True) / 10.0 129 ) 130 spacing = 330.0 131 col1 = inset 132 col2 = col1 + spacing 133 col3 = col2 + spacing 134 line_width = 0.0 135 nline = '' 136 for name in names2: 137 # move to the next column (or row) and print 138 if line_width > col3: 139 sval += nline + '\n' 140 nline = '' 141 line_width = 0 142 143 if line_width > col2: 144 target = col3 145 elif line_width > col1: 146 target = col2 147 else: 148 target = col1 149 spacingstr = ' ' * int((target - line_width) / space_width) 150 nline += spacingstr 151 nline += name 152 line_width = bui.get_string_width(nline, suppress_warning=True) 153 if nline != '': 154 sval += nline + '\n' 155 return sval 156 157 sound_and_music = bui.Lstr( 158 resource=f'{self._r}.songCreditText' 159 ).evaluate() 160 sound_and_music = sound_and_music.replace( 161 '${TITLE}', "'William Tell (Trumpet Entry)'" 162 ) 163 sound_and_music = sound_and_music.replace( 164 '${PERFORMER}', 'The Apollo Symphony Orchestra' 165 ) 166 sound_and_music = sound_and_music.replace( 167 '${PERFORMER}', 'The Apollo Symphony Orchestra' 168 ) 169 sound_and_music = sound_and_music.replace( 170 '${COMPOSER}', 'Gioacchino Rossini' 171 ) 172 sound_and_music = sound_and_music.replace('${ARRANGER}', 'Chris Worth') 173 sound_and_music = sound_and_music.replace('${PUBLISHER}', 'BMI') 174 sound_and_music = sound_and_music.replace( 175 '${SOURCE}', 'www.AudioSparx.com' 176 ) 177 spc = ' ' 178 sound_and_music = spc + sound_and_music.replace('\n', '\n' + spc) 179 names = [ 180 'HubOfTheUniverseProd', 181 'Jovica', 182 'LG', 183 'Leady', 184 'Percy Duke', 185 'PhreaKsAccount', 186 'Pogotron', 187 'Rock Savage', 188 'anamorphosis', 189 'benboncan', 190 'cdrk', 191 'chipfork', 192 'guitarguy1985', 193 'jascha', 194 'joedeshon', 195 'loofa', 196 'm_O_m', 197 'mich3d', 198 'sandyrb', 199 'shakaharu', 200 'sirplus', 201 'stickman', 202 'thanvannispen', 203 'virotic', 204 'zimbot', 205 ] 206 names.sort(key=lambda x: x.lower()) 207 freesound_names = _format_names(names, 90) 208 209 try: 210 with open( 211 os.path.join( 212 bui.app.env.data_directory, 213 'ba_data', 214 'data', 215 'langdata.json', 216 ), 217 encoding='utf-8', 218 ) as infile: 219 translation_contributors = json.loads(infile.read())[ 220 'translation_contributors' 221 ] 222 except Exception: 223 logging.exception('Error reading translation contributors.') 224 translation_contributors = [] 225 226 translation_names = _format_names(translation_contributors, 60) 227 228 # Need to bake this out and chop it up since we're passing our 229 # 65535 vertex limit for meshes.. 230 # We can remove that limit once we drop support for GL ES2.. :-/ 231 # (or add mesh splitting under the hood) 232 credits_text = ( 233 ' ' 234 + bui.Lstr(resource=f'{self._r}.codingGraphicsAudioText') 235 .evaluate() 236 .replace('${NAME}', 'Eric Froemling') 237 + '\n' 238 '\n' 239 ' ' 240 + bui.Lstr(resource=f'{self._r}.additionalAudioArtIdeasText') 241 .evaluate() 242 .replace('${NAME}', 'Raphael Suter') 243 + '\n' 244 '\n' 245 ' ' 246 + bui.Lstr(resource=f'{self._r}.soundAndMusicText').evaluate() 247 + '\n' 248 '\n' + sound_and_music + '\n' 249 '\n' 250 ' ' 251 + bui.Lstr(resource=f'{self._r}.publicDomainMusicViaText') 252 .evaluate() 253 .replace('${NAME}', 'Musopen.com') 254 + '\n' 255 ' ' 256 + bui.Lstr(resource=f'{self._r}.thanksEspeciallyToText') 257 .evaluate() 258 .replace('${NAME}', 'the US Army, Navy, and Marine Bands') 259 + '\n' 260 '\n' 261 ' ' 262 + bui.Lstr(resource=f'{self._r}.additionalMusicFromText') 263 .evaluate() 264 .replace('${NAME}', 'The YouTube Audio Library') 265 + '\n' 266 '\n' 267 ' ' 268 + bui.Lstr(resource=f'{self._r}.soundsText') 269 .evaluate() 270 .replace('${SOURCE}', 'Freesound.org') 271 + '\n' 272 '\n' + freesound_names + '\n' 273 '\n' 274 ' ' 275 + bui.Lstr( 276 resource=f'{self._r}.languageTranslationsText' 277 ).evaluate() 278 + '\n' 279 '\n' 280 + '\n'.join(translation_names.splitlines()[:146]) 281 + '\n'.join(translation_names.splitlines()[146:]) 282 + '\n' 283 '\n' 284 ' Shout Out to Awesome Mods / Modders / Contributors:\n\n' 285 ' BombDash ModPack\n' 286 ' TheMikirog & SoK - BombSquad Joyride Modpack\n' 287 ' Mrmaxmeier - BombSquad-Community-Mod-Manager\n' 288 ' Ritiek Malhotra \n' 289 ' Dliwk\n' 290 ' vishal332008\n' 291 ' itsre3\n' 292 ' Drooopyyy\n' 293 '\n' 294 ' Holiday theme vector art designed by Freepik\n' 295 '\n' 296 ' ' 297 + bui.Lstr(resource=f'{self._r}.specialThanksText').evaluate() 298 + '\n' 299 '\n' 300 ' Todd, Laura, and Robert Froemling\n' 301 ' ' 302 + bui.Lstr(resource=f'{self._r}.allMyFamilyText') 303 .evaluate() 304 .replace('\n', '\n ') 305 + '\n' 306 ' ' 307 + bui.Lstr( 308 resource=f'{self._r}.whoeverInventedCoffeeText' 309 ).evaluate() 310 + '\n' 311 '\n' 312 ' ' + bui.Lstr(resource=f'{self._r}.legalText').evaluate() + '\n' 313 '\n' 314 ' ' 315 + bui.Lstr(resource=f'{self._r}.softwareBasedOnText') 316 .evaluate() 317 .replace('${NAME}', 'the Khronos Group') 318 + '\n' 319 '\n' 320 ' ' 321 ' www.ballistica.net\n' 322 ) 323 324 txt = credits_text 325 lines = txt.splitlines() 326 line_height = 20 327 328 scale = 0.55 329 self._sub_width = width - 80 330 self._sub_height = line_height * len(lines) + 40 331 332 container = self._subcontainer = bui.containerwidget( 333 parent=scroll, 334 size=(self._sub_width, self._sub_height), 335 background=False, 336 claims_left_right=False, 337 ) 338 339 voffs = 0 340 for line in lines: 341 bui.textwidget( 342 parent=container, 343 padding=4, 344 color=(0.7, 0.9, 0.7, 1.0), 345 scale=scale, 346 flatness=1.0, 347 size=(0, 0), 348 position=(0, self._sub_height - 20 + voffs), 349 h_align='left', 350 v_align='top', 351 text=bui.Lstr(value=line), 352 ) 353 voffs -= line_height 354 355 @override 356 def get_main_window_state(self) -> bui.MainWindowState: 357 # Support recreating our window for back/refresh purposes. 358 cls = type(self) 359 return bui.BasicMainWindowState( 360 create_call=lambda transition, origin_widget: cls( 361 transition=transition, origin_widget=origin_widget 362 ) 363 )
Window for displaying game credits.
CreditsWindow( transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
22 def __init__( 23 self, 24 transition: str | None = 'in_right', 25 origin_widget: bui.Widget | None = None, 26 ): 27 # pylint: disable=too-many-locals 28 # pylint: disable=too-many-statements 29 30 bui.set_analytics_screen('Credits Window') 31 32 assert bui.app.classic is not None 33 uiscale = bui.app.ui_v1.uiscale 34 width = 990 if uiscale is bui.UIScale.SMALL else 670 35 x_inset = 100 if uiscale is bui.UIScale.SMALL else 0 36 height = 450 if uiscale is bui.UIScale.SMALL else 500 37 38 self._r = 'creditsWindow' 39 super().__init__( 40 root_widget=bui.containerwidget( 41 size=(width, height), 42 toolbar_visibility=( 43 'menu_minimal' 44 if uiscale is bui.UIScale.SMALL 45 else 'menu_full' 46 ), 47 scale=( 48 1.8 49 if uiscale is bui.UIScale.SMALL 50 else 1.2 if uiscale is bui.UIScale.MEDIUM else 1.0 51 ), 52 stack_offset=( 53 (0, 0) if uiscale is bui.UIScale.SMALL else (0, 0) 54 ), 55 ), 56 transition=transition, 57 origin_widget=origin_widget, 58 ) 59 60 if uiscale is bui.UIScale.SMALL: 61 bui.containerwidget( 62 edit=self._root_widget, on_cancel_call=self.main_window_back 63 ) 64 else: 65 btn = bui.buttonwidget( 66 parent=self._root_widget, 67 position=(40 + x_inset, height - 62), 68 size=(140, 60), 69 scale=0.8, 70 label=bui.Lstr(resource='backText'), 71 button_type='back', 72 on_activate_call=self.main_window_back, 73 autoselect=True, 74 ) 75 bui.containerwidget(edit=self._root_widget, cancel_button=btn) 76 77 bui.buttonwidget( 78 edit=btn, 79 button_type='backSmall', 80 position=( 81 40 + x_inset, 82 height - 62 + 5, 83 ), 84 size=(60, 48), 85 label=bui.charstr(bui.SpecialChar.BACK), 86 ) 87 88 bui.textwidget( 89 parent=self._root_widget, 90 position=(0, height - (93 if uiscale is bui.UIScale.SMALL else 54)), 91 size=(width, 30), 92 scale=0.8 if uiscale is bui.UIScale.SMALL else 1.0, 93 text=bui.Lstr( 94 resource=f'{self._r}.titleText', 95 subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))], 96 ), 97 h_align='center', 98 color=bui.app.ui_v1.title_color, 99 maxwidth=330, 100 v_align='center', 101 ) 102 103 scroll = bui.scrollwidget( 104 parent=self._root_widget, 105 position=(40 + x_inset, 62 if uiscale is bui.UIScale.SMALL else 35), 106 size=( 107 width - (80 + 2 * x_inset), 108 height - (160 if uiscale is bui.UIScale.SMALL else 100), 109 ), 110 capture_arrows=True, 111 border_opacity=0.4, 112 ) 113 114 bui.widget( 115 edit=scroll, 116 right_widget=bui.get_special_widget('squad_button'), 117 ) 118 if uiscale is bui.UIScale.SMALL: 119 bui.widget( 120 edit=scroll, 121 left_widget=bui.get_special_widget('back_button'), 122 ) 123 124 def _format_names(names2: Sequence[str], inset: float) -> str: 125 sval = '' 126 # measure a series since there's overlaps and stuff.. 127 space_width = ( 128 bui.get_string_width(' ' * 10, suppress_warning=True) / 10.0 129 ) 130 spacing = 330.0 131 col1 = inset 132 col2 = col1 + spacing 133 col3 = col2 + spacing 134 line_width = 0.0 135 nline = '' 136 for name in names2: 137 # move to the next column (or row) and print 138 if line_width > col3: 139 sval += nline + '\n' 140 nline = '' 141 line_width = 0 142 143 if line_width > col2: 144 target = col3 145 elif line_width > col1: 146 target = col2 147 else: 148 target = col1 149 spacingstr = ' ' * int((target - line_width) / space_width) 150 nline += spacingstr 151 nline += name 152 line_width = bui.get_string_width(nline, suppress_warning=True) 153 if nline != '': 154 sval += nline + '\n' 155 return sval 156 157 sound_and_music = bui.Lstr( 158 resource=f'{self._r}.songCreditText' 159 ).evaluate() 160 sound_and_music = sound_and_music.replace( 161 '${TITLE}', "'William Tell (Trumpet Entry)'" 162 ) 163 sound_and_music = sound_and_music.replace( 164 '${PERFORMER}', 'The Apollo Symphony Orchestra' 165 ) 166 sound_and_music = sound_and_music.replace( 167 '${PERFORMER}', 'The Apollo Symphony Orchestra' 168 ) 169 sound_and_music = sound_and_music.replace( 170 '${COMPOSER}', 'Gioacchino Rossini' 171 ) 172 sound_and_music = sound_and_music.replace('${ARRANGER}', 'Chris Worth') 173 sound_and_music = sound_and_music.replace('${PUBLISHER}', 'BMI') 174 sound_and_music = sound_and_music.replace( 175 '${SOURCE}', 'www.AudioSparx.com' 176 ) 177 spc = ' ' 178 sound_and_music = spc + sound_and_music.replace('\n', '\n' + spc) 179 names = [ 180 'HubOfTheUniverseProd', 181 'Jovica', 182 'LG', 183 'Leady', 184 'Percy Duke', 185 'PhreaKsAccount', 186 'Pogotron', 187 'Rock Savage', 188 'anamorphosis', 189 'benboncan', 190 'cdrk', 191 'chipfork', 192 'guitarguy1985', 193 'jascha', 194 'joedeshon', 195 'loofa', 196 'm_O_m', 197 'mich3d', 198 'sandyrb', 199 'shakaharu', 200 'sirplus', 201 'stickman', 202 'thanvannispen', 203 'virotic', 204 'zimbot', 205 ] 206 names.sort(key=lambda x: x.lower()) 207 freesound_names = _format_names(names, 90) 208 209 try: 210 with open( 211 os.path.join( 212 bui.app.env.data_directory, 213 'ba_data', 214 'data', 215 'langdata.json', 216 ), 217 encoding='utf-8', 218 ) as infile: 219 translation_contributors = json.loads(infile.read())[ 220 'translation_contributors' 221 ] 222 except Exception: 223 logging.exception('Error reading translation contributors.') 224 translation_contributors = [] 225 226 translation_names = _format_names(translation_contributors, 60) 227 228 # Need to bake this out and chop it up since we're passing our 229 # 65535 vertex limit for meshes.. 230 # We can remove that limit once we drop support for GL ES2.. :-/ 231 # (or add mesh splitting under the hood) 232 credits_text = ( 233 ' ' 234 + bui.Lstr(resource=f'{self._r}.codingGraphicsAudioText') 235 .evaluate() 236 .replace('${NAME}', 'Eric Froemling') 237 + '\n' 238 '\n' 239 ' ' 240 + bui.Lstr(resource=f'{self._r}.additionalAudioArtIdeasText') 241 .evaluate() 242 .replace('${NAME}', 'Raphael Suter') 243 + '\n' 244 '\n' 245 ' ' 246 + bui.Lstr(resource=f'{self._r}.soundAndMusicText').evaluate() 247 + '\n' 248 '\n' + sound_and_music + '\n' 249 '\n' 250 ' ' 251 + bui.Lstr(resource=f'{self._r}.publicDomainMusicViaText') 252 .evaluate() 253 .replace('${NAME}', 'Musopen.com') 254 + '\n' 255 ' ' 256 + bui.Lstr(resource=f'{self._r}.thanksEspeciallyToText') 257 .evaluate() 258 .replace('${NAME}', 'the US Army, Navy, and Marine Bands') 259 + '\n' 260 '\n' 261 ' ' 262 + bui.Lstr(resource=f'{self._r}.additionalMusicFromText') 263 .evaluate() 264 .replace('${NAME}', 'The YouTube Audio Library') 265 + '\n' 266 '\n' 267 ' ' 268 + bui.Lstr(resource=f'{self._r}.soundsText') 269 .evaluate() 270 .replace('${SOURCE}', 'Freesound.org') 271 + '\n' 272 '\n' + freesound_names + '\n' 273 '\n' 274 ' ' 275 + bui.Lstr( 276 resource=f'{self._r}.languageTranslationsText' 277 ).evaluate() 278 + '\n' 279 '\n' 280 + '\n'.join(translation_names.splitlines()[:146]) 281 + '\n'.join(translation_names.splitlines()[146:]) 282 + '\n' 283 '\n' 284 ' Shout Out to Awesome Mods / Modders / Contributors:\n\n' 285 ' BombDash ModPack\n' 286 ' TheMikirog & SoK - BombSquad Joyride Modpack\n' 287 ' Mrmaxmeier - BombSquad-Community-Mod-Manager\n' 288 ' Ritiek Malhotra \n' 289 ' Dliwk\n' 290 ' vishal332008\n' 291 ' itsre3\n' 292 ' Drooopyyy\n' 293 '\n' 294 ' Holiday theme vector art designed by Freepik\n' 295 '\n' 296 ' ' 297 + bui.Lstr(resource=f'{self._r}.specialThanksText').evaluate() 298 + '\n' 299 '\n' 300 ' Todd, Laura, and Robert Froemling\n' 301 ' ' 302 + bui.Lstr(resource=f'{self._r}.allMyFamilyText') 303 .evaluate() 304 .replace('\n', '\n ') 305 + '\n' 306 ' ' 307 + bui.Lstr( 308 resource=f'{self._r}.whoeverInventedCoffeeText' 309 ).evaluate() 310 + '\n' 311 '\n' 312 ' ' + bui.Lstr(resource=f'{self._r}.legalText').evaluate() + '\n' 313 '\n' 314 ' ' 315 + bui.Lstr(resource=f'{self._r}.softwareBasedOnText') 316 .evaluate() 317 .replace('${NAME}', 'the Khronos Group') 318 + '\n' 319 '\n' 320 ' ' 321 ' www.ballistica.net\n' 322 ) 323 324 txt = credits_text 325 lines = txt.splitlines() 326 line_height = 20 327 328 scale = 0.55 329 self._sub_width = width - 80 330 self._sub_height = line_height * len(lines) + 40 331 332 container = self._subcontainer = bui.containerwidget( 333 parent=scroll, 334 size=(self._sub_width, self._sub_height), 335 background=False, 336 claims_left_right=False, 337 ) 338 339 voffs = 0 340 for line in lines: 341 bui.textwidget( 342 parent=container, 343 padding=4, 344 color=(0.7, 0.9, 0.7, 1.0), 345 scale=scale, 346 flatness=1.0, 347 size=(0, 0), 348 position=(0, self._sub_height - 20 + voffs), 349 h_align='left', 350 v_align='top', 351 text=bui.Lstr(value=line), 352 ) 353 voffs -= line_height
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.
355 @override 356 def get_main_window_state(self) -> bui.MainWindowState: 357 # Support recreating our window for back/refresh purposes. 358 cls = type(self) 359 return bui.BasicMainWindowState( 360 create_call=lambda transition, origin_widget: cls( 361 transition=transition, origin_widget=origin_widget 362 ) 363 )
Return a WindowState to recreate this window, if supported.