bastd.ui.settings.graphics

Provides UI for graphics settings.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides UI for graphics settings."""
  4
  5from __future__ import annotations
  6
  7from typing import TYPE_CHECKING
  8
  9import ba
 10import ba.internal
 11
 12if TYPE_CHECKING:
 13    pass
 14
 15
 16class GraphicsSettingsWindow(ba.Window):
 17    """Window for graphics settings."""
 18
 19    def __init__(
 20        self,
 21        transition: str = 'in_right',
 22        origin_widget: ba.Widget | None = None,
 23    ):
 24        # pylint: disable=too-many-locals
 25        # pylint: disable=too-many-branches
 26        # pylint: disable=too-many-statements
 27        from bastd.ui import popup
 28        from bastd.ui.config import ConfigCheckBox, ConfigNumberEdit
 29
 30        # if they provided an origin-widget, scale up from that
 31        scale_origin: tuple[float, float] | None
 32        if origin_widget is not None:
 33            self._transition_out = 'out_scale'
 34            scale_origin = origin_widget.get_screen_space_center()
 35            transition = 'in_scale'
 36        else:
 37            self._transition_out = 'out_right'
 38            scale_origin = None
 39
 40        self._r = 'graphicsSettingsWindow'
 41        app = ba.app
 42
 43        spacing = 32
 44        self._have_selected_child = False
 45        uiscale = app.ui.uiscale
 46        width = 450.0
 47        height = 302.0
 48
 49        self._show_fullscreen = False
 50        fullscreen_spacing_top = spacing * 0.2
 51        fullscreen_spacing = spacing * 1.2
 52        if uiscale == ba.UIScale.LARGE and app.platform != 'android':
 53            self._show_fullscreen = True
 54            height += fullscreen_spacing + fullscreen_spacing_top
 55
 56        show_gamma = False
 57        gamma_spacing = spacing * 1.3
 58        if ba.internal.has_gamma_control():
 59            show_gamma = True
 60            height += gamma_spacing
 61
 62        show_vsync = False
 63        if app.platform == 'mac':
 64            show_vsync = True
 65
 66        show_resolution = True
 67        if app.vr_mode:
 68            show_resolution = (
 69                app.platform == 'android' and app.subplatform == 'cardboard'
 70            )
 71
 72        uiscale = ba.app.ui.uiscale
 73        base_scale = (
 74            2.4
 75            if uiscale is ba.UIScale.SMALL
 76            else 1.5
 77            if uiscale is ba.UIScale.MEDIUM
 78            else 1.0
 79        )
 80        popup_menu_scale = base_scale * 1.2
 81        v = height - 50
 82        v -= spacing * 1.15
 83        super().__init__(
 84            root_widget=ba.containerwidget(
 85                size=(width, height),
 86                transition=transition,
 87                scale_origin_stack_offset=scale_origin,
 88                scale=base_scale,
 89                stack_offset=(0, -30)
 90                if uiscale is ba.UIScale.SMALL
 91                else (0, 0),
 92            )
 93        )
 94
 95        btn = ba.buttonwidget(
 96            parent=self._root_widget,
 97            position=(35, height - 50),
 98            size=(120, 60),
 99            scale=0.8,
100            text_scale=1.2,
101            autoselect=True,
102            label=ba.Lstr(resource='backText'),
103            button_type='back',
104            on_activate_call=self._back,
105        )
106
107        ba.containerwidget(edit=self._root_widget, cancel_button=btn)
108
109        ba.textwidget(
110            parent=self._root_widget,
111            position=(0, height - 44),
112            size=(width, 25),
113            text=ba.Lstr(resource=self._r + '.titleText'),
114            color=ba.app.ui.title_color,
115            h_align='center',
116            v_align='top',
117        )
118
119        ba.buttonwidget(
120            edit=btn,
121            button_type='backSmall',
122            size=(60, 60),
123            label=ba.charstr(ba.SpecialChar.BACK),
124        )
125
126        self._fullscreen_checkbox: ba.Widget | None
127        if self._show_fullscreen:
128            v -= fullscreen_spacing_top
129            self._fullscreen_checkbox = ConfigCheckBox(
130                parent=self._root_widget,
131                position=(100, v),
132                maxwidth=200,
133                size=(300, 30),
134                configkey='Fullscreen',
135                displayname=ba.Lstr(
136                    resource=self._r
137                    + (
138                        '.fullScreenCmdText'
139                        if app.platform == 'mac'
140                        else '.fullScreenCtrlText'
141                    )
142                ),
143            ).widget
144            if not self._have_selected_child:
145                ba.containerwidget(
146                    edit=self._root_widget,
147                    selected_child=self._fullscreen_checkbox,
148                )
149                self._have_selected_child = True
150            v -= fullscreen_spacing
151        else:
152            self._fullscreen_checkbox = None
153
154        self._gamma_controls: ConfigNumberEdit | None
155        if show_gamma:
156            self._gamma_controls = gmc = ConfigNumberEdit(
157                parent=self._root_widget,
158                position=(90, v),
159                configkey='Screen Gamma',
160                displayname=ba.Lstr(resource=self._r + '.gammaText'),
161                minval=0.1,
162                maxval=2.0,
163                increment=0.1,
164                xoffset=-70,
165                textscale=0.85,
166            )
167            if ba.app.ui.use_toolbars:
168                ba.widget(
169                    edit=gmc.plusbutton,
170                    right_widget=ba.internal.get_special_widget('party_button'),
171                )
172            if not self._have_selected_child:
173                ba.containerwidget(
174                    edit=self._root_widget, selected_child=gmc.minusbutton
175                )
176                self._have_selected_child = True
177            v -= gamma_spacing
178        else:
179            self._gamma_controls = None
180
181        self._selected_color = (0.5, 1, 0.5, 1)
182        self._unselected_color = (0.7, 0.7, 0.7, 1)
183
184        # quality
185        ba.textwidget(
186            parent=self._root_widget,
187            position=(60, v),
188            size=(160, 25),
189            text=ba.Lstr(resource=self._r + '.visualsText'),
190            color=ba.app.ui.heading_color,
191            scale=0.65,
192            maxwidth=150,
193            h_align='center',
194            v_align='center',
195        )
196        popup.PopupMenu(
197            parent=self._root_widget,
198            position=(60, v - 50),
199            width=150,
200            scale=popup_menu_scale,
201            choices=['Auto', 'Higher', 'High', 'Medium', 'Low'],
202            choices_disabled=['Higher', 'High']
203            if ba.internal.get_max_graphics_quality() == 'Medium'
204            else [],
205            choices_display=[
206                ba.Lstr(resource='autoText'),
207                ba.Lstr(resource=self._r + '.higherText'),
208                ba.Lstr(resource=self._r + '.highText'),
209                ba.Lstr(resource=self._r + '.mediumText'),
210                ba.Lstr(resource=self._r + '.lowText'),
211            ],
212            current_choice=ba.app.config.resolve('Graphics Quality'),
213            on_value_change_call=self._set_quality,
214        )
215
216        # texture controls
217        ba.textwidget(
218            parent=self._root_widget,
219            position=(230, v),
220            size=(160, 25),
221            text=ba.Lstr(resource=self._r + '.texturesText'),
222            color=ba.app.ui.heading_color,
223            scale=0.65,
224            maxwidth=150,
225            h_align='center',
226            v_align='center',
227        )
228        textures_popup = popup.PopupMenu(
229            parent=self._root_widget,
230            position=(230, v - 50),
231            width=150,
232            scale=popup_menu_scale,
233            choices=['Auto', 'High', 'Medium', 'Low'],
234            choices_display=[
235                ba.Lstr(resource='autoText'),
236                ba.Lstr(resource=self._r + '.highText'),
237                ba.Lstr(resource=self._r + '.mediumText'),
238                ba.Lstr(resource=self._r + '.lowText'),
239            ],
240            current_choice=ba.app.config.resolve('Texture Quality'),
241            on_value_change_call=self._set_textures,
242        )
243        if ba.app.ui.use_toolbars:
244            ba.widget(
245                edit=textures_popup.get_button(),
246                right_widget=ba.internal.get_special_widget('party_button'),
247            )
248        v -= 80
249
250        h_offs = 0
251
252        if show_resolution:
253            # resolution
254            ba.textwidget(
255                parent=self._root_widget,
256                position=(h_offs + 60, v),
257                size=(160, 25),
258                text=ba.Lstr(resource=self._r + '.resolutionText'),
259                color=ba.app.ui.heading_color,
260                scale=0.65,
261                maxwidth=150,
262                h_align='center',
263                v_align='center',
264            )
265
266            # on standard android we have 'Auto', 'Native', and a few
267            # HD standards
268            if app.platform == 'android':
269                # on cardboard/daydream android we have a few
270                # render-target-scale options
271                if app.subplatform == 'cardboard':
272                    current_res_cardboard = (
273                        str(
274                            min(
275                                100,
276                                max(
277                                    10,
278                                    int(
279                                        round(
280                                            ba.app.config.resolve(
281                                                'GVR Render Target Scale'
282                                            )
283                                            * 100.0
284                                        )
285                                    ),
286                                ),
287                            )
288                        )
289                        + '%'
290                    )  # yapf: disable
291                    popup.PopupMenu(
292                        parent=self._root_widget,
293                        position=(h_offs + 60, v - 50),
294                        width=120,
295                        scale=popup_menu_scale,
296                        choices=['100%', '75%', '50%', '35%'],
297                        current_choice=current_res_cardboard,
298                        on_value_change_call=self._set_gvr_render_target_scale,
299                    )
300                else:
301                    native_res = ba.internal.get_display_resolution()
302                    assert native_res is not None
303                    choices = ['Auto', 'Native']
304                    choices_display = [
305                        ba.Lstr(resource='autoText'),
306                        ba.Lstr(resource='nativeText'),
307                    ]
308                    for res in [1440, 1080, 960, 720, 480]:
309                        # nav bar is 72px so lets allow for that in what
310                        # choices we show
311                        if native_res[1] >= res - 72:
312                            res_str = str(res) + 'p'
313                            choices.append(res_str)
314                            choices_display.append(ba.Lstr(value=res_str))
315                    current_res_android = ba.app.config.resolve(
316                        'Resolution (Android)'
317                    )
318                    popup.PopupMenu(
319                        parent=self._root_widget,
320                        position=(h_offs + 60, v - 50),
321                        width=120,
322                        scale=popup_menu_scale,
323                        choices=choices,
324                        choices_display=choices_display,
325                        current_choice=current_res_android,
326                        on_value_change_call=self._set_android_res,
327                    )
328            else:
329                # if we're on a system that doesn't allow setting resolution,
330                # set pixel-scale instead
331                current_res = ba.internal.get_display_resolution()
332                if current_res is None:
333                    current_res2 = (
334                        str(
335                            min(
336                                100,
337                                max(
338                                    10,
339                                    int(
340                                        round(
341                                            ba.app.config.resolve(
342                                                'Screen Pixel Scale'
343                                            )
344                                            * 100.0
345                                        )
346                                    ),
347                                ),
348                            )
349                        )
350                        + '%'
351                    )  # yapf: disable
352                    popup.PopupMenu(
353                        parent=self._root_widget,
354                        position=(h_offs + 60, v - 50),
355                        width=120,
356                        scale=popup_menu_scale,
357                        choices=['100%', '88%', '75%', '63%', '50%'],
358                        current_choice=current_res2,
359                        on_value_change_call=self._set_pixel_scale,
360                    )
361                else:
362                    raise Exception(
363                        'obsolete path; discrete resolutions'
364                        ' no longer supported'
365                    )
366
367        # vsync
368        if show_vsync:
369            ba.textwidget(
370                parent=self._root_widget,
371                position=(230, v),
372                size=(160, 25),
373                text=ba.Lstr(resource=self._r + '.verticalSyncText'),
374                color=ba.app.ui.heading_color,
375                scale=0.65,
376                maxwidth=150,
377                h_align='center',
378                v_align='center',
379            )
380
381            popup.PopupMenu(
382                parent=self._root_widget,
383                position=(230, v - 50),
384                width=150,
385                scale=popup_menu_scale,
386                choices=['Auto', 'Always', 'Never'],
387                choices_display=[
388                    ba.Lstr(resource='autoText'),
389                    ba.Lstr(resource=self._r + '.alwaysText'),
390                    ba.Lstr(resource=self._r + '.neverText'),
391                ],
392                current_choice=ba.app.config.resolve('Vertical Sync'),
393                on_value_change_call=self._set_vsync,
394            )
395
396        v -= 90
397        fpsc = ConfigCheckBox(
398            parent=self._root_widget,
399            position=(69, v - 6),
400            size=(210, 30),
401            scale=0.86,
402            configkey='Show FPS',
403            displayname=ba.Lstr(resource=self._r + '.showFPSText'),
404            maxwidth=130,
405        )
406
407        # (tv mode doesnt apply to vr)
408        if not ba.app.vr_mode:
409            tvc = ConfigCheckBox(
410                parent=self._root_widget,
411                position=(240, v - 6),
412                size=(210, 30),
413                scale=0.86,
414                configkey='TV Border',
415                displayname=ba.Lstr(resource=self._r + '.tvBorderText'),
416                maxwidth=130,
417            )
418            # grumble..
419            ba.widget(edit=fpsc.widget, right_widget=tvc.widget)
420        try:
421            pass
422
423        except Exception:
424            ba.print_exception('Exception wiring up graphics settings UI:')
425
426        v -= spacing
427
428        # make a timer to update our controls in case the config changes
429        # under us
430        self._update_timer = ba.Timer(
431            0.25,
432            ba.WeakCall(self._update_controls),
433            repeat=True,
434            timetype=ba.TimeType.REAL,
435        )
436
437    def _back(self) -> None:
438        from bastd.ui.settings import allsettings
439
440        ba.containerwidget(
441            edit=self._root_widget, transition=self._transition_out
442        )
443        ba.app.ui.set_main_menu_window(
444            allsettings.AllSettingsWindow(
445                transition='in_left'
446            ).get_root_widget()
447        )
448
449    def _set_quality(self, quality: str) -> None:
450        cfg = ba.app.config
451        cfg['Graphics Quality'] = quality
452        cfg.apply_and_commit()
453
454    def _set_textures(self, val: str) -> None:
455        cfg = ba.app.config
456        cfg['Texture Quality'] = val
457        cfg.apply_and_commit()
458
459    def _set_android_res(self, val: str) -> None:
460        cfg = ba.app.config
461        cfg['Resolution (Android)'] = val
462        cfg.apply_and_commit()
463
464    def _set_pixel_scale(self, res: str) -> None:
465        cfg = ba.app.config
466        cfg['Screen Pixel Scale'] = float(res[:-1]) / 100.0
467        cfg.apply_and_commit()
468
469    def _set_gvr_render_target_scale(self, res: str) -> None:
470        cfg = ba.app.config
471        cfg['GVR Render Target Scale'] = float(res[:-1]) / 100.0
472        cfg.apply_and_commit()
473
474    def _set_vsync(self, val: str) -> None:
475        cfg = ba.app.config
476        cfg['Vertical Sync'] = val
477        cfg.apply_and_commit()
478
479    def _update_controls(self) -> None:
480        if self._show_fullscreen:
481            ba.checkboxwidget(
482                edit=self._fullscreen_checkbox,
483                value=ba.app.config.resolve('Fullscreen'),
484            )
class GraphicsSettingsWindow(ba.ui.Window):
 17class GraphicsSettingsWindow(ba.Window):
 18    """Window for graphics settings."""
 19
 20    def __init__(
 21        self,
 22        transition: str = 'in_right',
 23        origin_widget: ba.Widget | None = None,
 24    ):
 25        # pylint: disable=too-many-locals
 26        # pylint: disable=too-many-branches
 27        # pylint: disable=too-many-statements
 28        from bastd.ui import popup
 29        from bastd.ui.config import ConfigCheckBox, ConfigNumberEdit
 30
 31        # if they provided an origin-widget, scale up from that
 32        scale_origin: tuple[float, float] | None
 33        if origin_widget is not None:
 34            self._transition_out = 'out_scale'
 35            scale_origin = origin_widget.get_screen_space_center()
 36            transition = 'in_scale'
 37        else:
 38            self._transition_out = 'out_right'
 39            scale_origin = None
 40
 41        self._r = 'graphicsSettingsWindow'
 42        app = ba.app
 43
 44        spacing = 32
 45        self._have_selected_child = False
 46        uiscale = app.ui.uiscale
 47        width = 450.0
 48        height = 302.0
 49
 50        self._show_fullscreen = False
 51        fullscreen_spacing_top = spacing * 0.2
 52        fullscreen_spacing = spacing * 1.2
 53        if uiscale == ba.UIScale.LARGE and app.platform != 'android':
 54            self._show_fullscreen = True
 55            height += fullscreen_spacing + fullscreen_spacing_top
 56
 57        show_gamma = False
 58        gamma_spacing = spacing * 1.3
 59        if ba.internal.has_gamma_control():
 60            show_gamma = True
 61            height += gamma_spacing
 62
 63        show_vsync = False
 64        if app.platform == 'mac':
 65            show_vsync = True
 66
 67        show_resolution = True
 68        if app.vr_mode:
 69            show_resolution = (
 70                app.platform == 'android' and app.subplatform == 'cardboard'
 71            )
 72
 73        uiscale = ba.app.ui.uiscale
 74        base_scale = (
 75            2.4
 76            if uiscale is ba.UIScale.SMALL
 77            else 1.5
 78            if uiscale is ba.UIScale.MEDIUM
 79            else 1.0
 80        )
 81        popup_menu_scale = base_scale * 1.2
 82        v = height - 50
 83        v -= spacing * 1.15
 84        super().__init__(
 85            root_widget=ba.containerwidget(
 86                size=(width, height),
 87                transition=transition,
 88                scale_origin_stack_offset=scale_origin,
 89                scale=base_scale,
 90                stack_offset=(0, -30)
 91                if uiscale is ba.UIScale.SMALL
 92                else (0, 0),
 93            )
 94        )
 95
 96        btn = ba.buttonwidget(
 97            parent=self._root_widget,
 98            position=(35, height - 50),
 99            size=(120, 60),
100            scale=0.8,
101            text_scale=1.2,
102            autoselect=True,
103            label=ba.Lstr(resource='backText'),
104            button_type='back',
105            on_activate_call=self._back,
106        )
107
108        ba.containerwidget(edit=self._root_widget, cancel_button=btn)
109
110        ba.textwidget(
111            parent=self._root_widget,
112            position=(0, height - 44),
113            size=(width, 25),
114            text=ba.Lstr(resource=self._r + '.titleText'),
115            color=ba.app.ui.title_color,
116            h_align='center',
117            v_align='top',
118        )
119
120        ba.buttonwidget(
121            edit=btn,
122            button_type='backSmall',
123            size=(60, 60),
124            label=ba.charstr(ba.SpecialChar.BACK),
125        )
126
127        self._fullscreen_checkbox: ba.Widget | None
128        if self._show_fullscreen:
129            v -= fullscreen_spacing_top
130            self._fullscreen_checkbox = ConfigCheckBox(
131                parent=self._root_widget,
132                position=(100, v),
133                maxwidth=200,
134                size=(300, 30),
135                configkey='Fullscreen',
136                displayname=ba.Lstr(
137                    resource=self._r
138                    + (
139                        '.fullScreenCmdText'
140                        if app.platform == 'mac'
141                        else '.fullScreenCtrlText'
142                    )
143                ),
144            ).widget
145            if not self._have_selected_child:
146                ba.containerwidget(
147                    edit=self._root_widget,
148                    selected_child=self._fullscreen_checkbox,
149                )
150                self._have_selected_child = True
151            v -= fullscreen_spacing
152        else:
153            self._fullscreen_checkbox = None
154
155        self._gamma_controls: ConfigNumberEdit | None
156        if show_gamma:
157            self._gamma_controls = gmc = ConfigNumberEdit(
158                parent=self._root_widget,
159                position=(90, v),
160                configkey='Screen Gamma',
161                displayname=ba.Lstr(resource=self._r + '.gammaText'),
162                minval=0.1,
163                maxval=2.0,
164                increment=0.1,
165                xoffset=-70,
166                textscale=0.85,
167            )
168            if ba.app.ui.use_toolbars:
169                ba.widget(
170                    edit=gmc.plusbutton,
171                    right_widget=ba.internal.get_special_widget('party_button'),
172                )
173            if not self._have_selected_child:
174                ba.containerwidget(
175                    edit=self._root_widget, selected_child=gmc.minusbutton
176                )
177                self._have_selected_child = True
178            v -= gamma_spacing
179        else:
180            self._gamma_controls = None
181
182        self._selected_color = (0.5, 1, 0.5, 1)
183        self._unselected_color = (0.7, 0.7, 0.7, 1)
184
185        # quality
186        ba.textwidget(
187            parent=self._root_widget,
188            position=(60, v),
189            size=(160, 25),
190            text=ba.Lstr(resource=self._r + '.visualsText'),
191            color=ba.app.ui.heading_color,
192            scale=0.65,
193            maxwidth=150,
194            h_align='center',
195            v_align='center',
196        )
197        popup.PopupMenu(
198            parent=self._root_widget,
199            position=(60, v - 50),
200            width=150,
201            scale=popup_menu_scale,
202            choices=['Auto', 'Higher', 'High', 'Medium', 'Low'],
203            choices_disabled=['Higher', 'High']
204            if ba.internal.get_max_graphics_quality() == 'Medium'
205            else [],
206            choices_display=[
207                ba.Lstr(resource='autoText'),
208                ba.Lstr(resource=self._r + '.higherText'),
209                ba.Lstr(resource=self._r + '.highText'),
210                ba.Lstr(resource=self._r + '.mediumText'),
211                ba.Lstr(resource=self._r + '.lowText'),
212            ],
213            current_choice=ba.app.config.resolve('Graphics Quality'),
214            on_value_change_call=self._set_quality,
215        )
216
217        # texture controls
218        ba.textwidget(
219            parent=self._root_widget,
220            position=(230, v),
221            size=(160, 25),
222            text=ba.Lstr(resource=self._r + '.texturesText'),
223            color=ba.app.ui.heading_color,
224            scale=0.65,
225            maxwidth=150,
226            h_align='center',
227            v_align='center',
228        )
229        textures_popup = popup.PopupMenu(
230            parent=self._root_widget,
231            position=(230, v - 50),
232            width=150,
233            scale=popup_menu_scale,
234            choices=['Auto', 'High', 'Medium', 'Low'],
235            choices_display=[
236                ba.Lstr(resource='autoText'),
237                ba.Lstr(resource=self._r + '.highText'),
238                ba.Lstr(resource=self._r + '.mediumText'),
239                ba.Lstr(resource=self._r + '.lowText'),
240            ],
241            current_choice=ba.app.config.resolve('Texture Quality'),
242            on_value_change_call=self._set_textures,
243        )
244        if ba.app.ui.use_toolbars:
245            ba.widget(
246                edit=textures_popup.get_button(),
247                right_widget=ba.internal.get_special_widget('party_button'),
248            )
249        v -= 80
250
251        h_offs = 0
252
253        if show_resolution:
254            # resolution
255            ba.textwidget(
256                parent=self._root_widget,
257                position=(h_offs + 60, v),
258                size=(160, 25),
259                text=ba.Lstr(resource=self._r + '.resolutionText'),
260                color=ba.app.ui.heading_color,
261                scale=0.65,
262                maxwidth=150,
263                h_align='center',
264                v_align='center',
265            )
266
267            # on standard android we have 'Auto', 'Native', and a few
268            # HD standards
269            if app.platform == 'android':
270                # on cardboard/daydream android we have a few
271                # render-target-scale options
272                if app.subplatform == 'cardboard':
273                    current_res_cardboard = (
274                        str(
275                            min(
276                                100,
277                                max(
278                                    10,
279                                    int(
280                                        round(
281                                            ba.app.config.resolve(
282                                                'GVR Render Target Scale'
283                                            )
284                                            * 100.0
285                                        )
286                                    ),
287                                ),
288                            )
289                        )
290                        + '%'
291                    )  # yapf: disable
292                    popup.PopupMenu(
293                        parent=self._root_widget,
294                        position=(h_offs + 60, v - 50),
295                        width=120,
296                        scale=popup_menu_scale,
297                        choices=['100%', '75%', '50%', '35%'],
298                        current_choice=current_res_cardboard,
299                        on_value_change_call=self._set_gvr_render_target_scale,
300                    )
301                else:
302                    native_res = ba.internal.get_display_resolution()
303                    assert native_res is not None
304                    choices = ['Auto', 'Native']
305                    choices_display = [
306                        ba.Lstr(resource='autoText'),
307                        ba.Lstr(resource='nativeText'),
308                    ]
309                    for res in [1440, 1080, 960, 720, 480]:
310                        # nav bar is 72px so lets allow for that in what
311                        # choices we show
312                        if native_res[1] >= res - 72:
313                            res_str = str(res) + 'p'
314                            choices.append(res_str)
315                            choices_display.append(ba.Lstr(value=res_str))
316                    current_res_android = ba.app.config.resolve(
317                        'Resolution (Android)'
318                    )
319                    popup.PopupMenu(
320                        parent=self._root_widget,
321                        position=(h_offs + 60, v - 50),
322                        width=120,
323                        scale=popup_menu_scale,
324                        choices=choices,
325                        choices_display=choices_display,
326                        current_choice=current_res_android,
327                        on_value_change_call=self._set_android_res,
328                    )
329            else:
330                # if we're on a system that doesn't allow setting resolution,
331                # set pixel-scale instead
332                current_res = ba.internal.get_display_resolution()
333                if current_res is None:
334                    current_res2 = (
335                        str(
336                            min(
337                                100,
338                                max(
339                                    10,
340                                    int(
341                                        round(
342                                            ba.app.config.resolve(
343                                                'Screen Pixel Scale'
344                                            )
345                                            * 100.0
346                                        )
347                                    ),
348                                ),
349                            )
350                        )
351                        + '%'
352                    )  # yapf: disable
353                    popup.PopupMenu(
354                        parent=self._root_widget,
355                        position=(h_offs + 60, v - 50),
356                        width=120,
357                        scale=popup_menu_scale,
358                        choices=['100%', '88%', '75%', '63%', '50%'],
359                        current_choice=current_res2,
360                        on_value_change_call=self._set_pixel_scale,
361                    )
362                else:
363                    raise Exception(
364                        'obsolete path; discrete resolutions'
365                        ' no longer supported'
366                    )
367
368        # vsync
369        if show_vsync:
370            ba.textwidget(
371                parent=self._root_widget,
372                position=(230, v),
373                size=(160, 25),
374                text=ba.Lstr(resource=self._r + '.verticalSyncText'),
375                color=ba.app.ui.heading_color,
376                scale=0.65,
377                maxwidth=150,
378                h_align='center',
379                v_align='center',
380            )
381
382            popup.PopupMenu(
383                parent=self._root_widget,
384                position=(230, v - 50),
385                width=150,
386                scale=popup_menu_scale,
387                choices=['Auto', 'Always', 'Never'],
388                choices_display=[
389                    ba.Lstr(resource='autoText'),
390                    ba.Lstr(resource=self._r + '.alwaysText'),
391                    ba.Lstr(resource=self._r + '.neverText'),
392                ],
393                current_choice=ba.app.config.resolve('Vertical Sync'),
394                on_value_change_call=self._set_vsync,
395            )
396
397        v -= 90
398        fpsc = ConfigCheckBox(
399            parent=self._root_widget,
400            position=(69, v - 6),
401            size=(210, 30),
402            scale=0.86,
403            configkey='Show FPS',
404            displayname=ba.Lstr(resource=self._r + '.showFPSText'),
405            maxwidth=130,
406        )
407
408        # (tv mode doesnt apply to vr)
409        if not ba.app.vr_mode:
410            tvc = ConfigCheckBox(
411                parent=self._root_widget,
412                position=(240, v - 6),
413                size=(210, 30),
414                scale=0.86,
415                configkey='TV Border',
416                displayname=ba.Lstr(resource=self._r + '.tvBorderText'),
417                maxwidth=130,
418            )
419            # grumble..
420            ba.widget(edit=fpsc.widget, right_widget=tvc.widget)
421        try:
422            pass
423
424        except Exception:
425            ba.print_exception('Exception wiring up graphics settings UI:')
426
427        v -= spacing
428
429        # make a timer to update our controls in case the config changes
430        # under us
431        self._update_timer = ba.Timer(
432            0.25,
433            ba.WeakCall(self._update_controls),
434            repeat=True,
435            timetype=ba.TimeType.REAL,
436        )
437
438    def _back(self) -> None:
439        from bastd.ui.settings import allsettings
440
441        ba.containerwidget(
442            edit=self._root_widget, transition=self._transition_out
443        )
444        ba.app.ui.set_main_menu_window(
445            allsettings.AllSettingsWindow(
446                transition='in_left'
447            ).get_root_widget()
448        )
449
450    def _set_quality(self, quality: str) -> None:
451        cfg = ba.app.config
452        cfg['Graphics Quality'] = quality
453        cfg.apply_and_commit()
454
455    def _set_textures(self, val: str) -> None:
456        cfg = ba.app.config
457        cfg['Texture Quality'] = val
458        cfg.apply_and_commit()
459
460    def _set_android_res(self, val: str) -> None:
461        cfg = ba.app.config
462        cfg['Resolution (Android)'] = val
463        cfg.apply_and_commit()
464
465    def _set_pixel_scale(self, res: str) -> None:
466        cfg = ba.app.config
467        cfg['Screen Pixel Scale'] = float(res[:-1]) / 100.0
468        cfg.apply_and_commit()
469
470    def _set_gvr_render_target_scale(self, res: str) -> None:
471        cfg = ba.app.config
472        cfg['GVR Render Target Scale'] = float(res[:-1]) / 100.0
473        cfg.apply_and_commit()
474
475    def _set_vsync(self, val: str) -> None:
476        cfg = ba.app.config
477        cfg['Vertical Sync'] = val
478        cfg.apply_and_commit()
479
480    def _update_controls(self) -> None:
481        if self._show_fullscreen:
482            ba.checkboxwidget(
483                edit=self._fullscreen_checkbox,
484                value=ba.app.config.resolve('Fullscreen'),
485            )

Window for graphics settings.

GraphicsSettingsWindow( transition: str = 'in_right', origin_widget: _ba.Widget | None = None)
 20    def __init__(
 21        self,
 22        transition: str = 'in_right',
 23        origin_widget: ba.Widget | None = None,
 24    ):
 25        # pylint: disable=too-many-locals
 26        # pylint: disable=too-many-branches
 27        # pylint: disable=too-many-statements
 28        from bastd.ui import popup
 29        from bastd.ui.config import ConfigCheckBox, ConfigNumberEdit
 30
 31        # if they provided an origin-widget, scale up from that
 32        scale_origin: tuple[float, float] | None
 33        if origin_widget is not None:
 34            self._transition_out = 'out_scale'
 35            scale_origin = origin_widget.get_screen_space_center()
 36            transition = 'in_scale'
 37        else:
 38            self._transition_out = 'out_right'
 39            scale_origin = None
 40
 41        self._r = 'graphicsSettingsWindow'
 42        app = ba.app
 43
 44        spacing = 32
 45        self._have_selected_child = False
 46        uiscale = app.ui.uiscale
 47        width = 450.0
 48        height = 302.0
 49
 50        self._show_fullscreen = False
 51        fullscreen_spacing_top = spacing * 0.2
 52        fullscreen_spacing = spacing * 1.2
 53        if uiscale == ba.UIScale.LARGE and app.platform != 'android':
 54            self._show_fullscreen = True
 55            height += fullscreen_spacing + fullscreen_spacing_top
 56
 57        show_gamma = False
 58        gamma_spacing = spacing * 1.3
 59        if ba.internal.has_gamma_control():
 60            show_gamma = True
 61            height += gamma_spacing
 62
 63        show_vsync = False
 64        if app.platform == 'mac':
 65            show_vsync = True
 66
 67        show_resolution = True
 68        if app.vr_mode:
 69            show_resolution = (
 70                app.platform == 'android' and app.subplatform == 'cardboard'
 71            )
 72
 73        uiscale = ba.app.ui.uiscale
 74        base_scale = (
 75            2.4
 76            if uiscale is ba.UIScale.SMALL
 77            else 1.5
 78            if uiscale is ba.UIScale.MEDIUM
 79            else 1.0
 80        )
 81        popup_menu_scale = base_scale * 1.2
 82        v = height - 50
 83        v -= spacing * 1.15
 84        super().__init__(
 85            root_widget=ba.containerwidget(
 86                size=(width, height),
 87                transition=transition,
 88                scale_origin_stack_offset=scale_origin,
 89                scale=base_scale,
 90                stack_offset=(0, -30)
 91                if uiscale is ba.UIScale.SMALL
 92                else (0, 0),
 93            )
 94        )
 95
 96        btn = ba.buttonwidget(
 97            parent=self._root_widget,
 98            position=(35, height - 50),
 99            size=(120, 60),
100            scale=0.8,
101            text_scale=1.2,
102            autoselect=True,
103            label=ba.Lstr(resource='backText'),
104            button_type='back',
105            on_activate_call=self._back,
106        )
107
108        ba.containerwidget(edit=self._root_widget, cancel_button=btn)
109
110        ba.textwidget(
111            parent=self._root_widget,
112            position=(0, height - 44),
113            size=(width, 25),
114            text=ba.Lstr(resource=self._r + '.titleText'),
115            color=ba.app.ui.title_color,
116            h_align='center',
117            v_align='top',
118        )
119
120        ba.buttonwidget(
121            edit=btn,
122            button_type='backSmall',
123            size=(60, 60),
124            label=ba.charstr(ba.SpecialChar.BACK),
125        )
126
127        self._fullscreen_checkbox: ba.Widget | None
128        if self._show_fullscreen:
129            v -= fullscreen_spacing_top
130            self._fullscreen_checkbox = ConfigCheckBox(
131                parent=self._root_widget,
132                position=(100, v),
133                maxwidth=200,
134                size=(300, 30),
135                configkey='Fullscreen',
136                displayname=ba.Lstr(
137                    resource=self._r
138                    + (
139                        '.fullScreenCmdText'
140                        if app.platform == 'mac'
141                        else '.fullScreenCtrlText'
142                    )
143                ),
144            ).widget
145            if not self._have_selected_child:
146                ba.containerwidget(
147                    edit=self._root_widget,
148                    selected_child=self._fullscreen_checkbox,
149                )
150                self._have_selected_child = True
151            v -= fullscreen_spacing
152        else:
153            self._fullscreen_checkbox = None
154
155        self._gamma_controls: ConfigNumberEdit | None
156        if show_gamma:
157            self._gamma_controls = gmc = ConfigNumberEdit(
158                parent=self._root_widget,
159                position=(90, v),
160                configkey='Screen Gamma',
161                displayname=ba.Lstr(resource=self._r + '.gammaText'),
162                minval=0.1,
163                maxval=2.0,
164                increment=0.1,
165                xoffset=-70,
166                textscale=0.85,
167            )
168            if ba.app.ui.use_toolbars:
169                ba.widget(
170                    edit=gmc.plusbutton,
171                    right_widget=ba.internal.get_special_widget('party_button'),
172                )
173            if not self._have_selected_child:
174                ba.containerwidget(
175                    edit=self._root_widget, selected_child=gmc.minusbutton
176                )
177                self._have_selected_child = True
178            v -= gamma_spacing
179        else:
180            self._gamma_controls = None
181
182        self._selected_color = (0.5, 1, 0.5, 1)
183        self._unselected_color = (0.7, 0.7, 0.7, 1)
184
185        # quality
186        ba.textwidget(
187            parent=self._root_widget,
188            position=(60, v),
189            size=(160, 25),
190            text=ba.Lstr(resource=self._r + '.visualsText'),
191            color=ba.app.ui.heading_color,
192            scale=0.65,
193            maxwidth=150,
194            h_align='center',
195            v_align='center',
196        )
197        popup.PopupMenu(
198            parent=self._root_widget,
199            position=(60, v - 50),
200            width=150,
201            scale=popup_menu_scale,
202            choices=['Auto', 'Higher', 'High', 'Medium', 'Low'],
203            choices_disabled=['Higher', 'High']
204            if ba.internal.get_max_graphics_quality() == 'Medium'
205            else [],
206            choices_display=[
207                ba.Lstr(resource='autoText'),
208                ba.Lstr(resource=self._r + '.higherText'),
209                ba.Lstr(resource=self._r + '.highText'),
210                ba.Lstr(resource=self._r + '.mediumText'),
211                ba.Lstr(resource=self._r + '.lowText'),
212            ],
213            current_choice=ba.app.config.resolve('Graphics Quality'),
214            on_value_change_call=self._set_quality,
215        )
216
217        # texture controls
218        ba.textwidget(
219            parent=self._root_widget,
220            position=(230, v),
221            size=(160, 25),
222            text=ba.Lstr(resource=self._r + '.texturesText'),
223            color=ba.app.ui.heading_color,
224            scale=0.65,
225            maxwidth=150,
226            h_align='center',
227            v_align='center',
228        )
229        textures_popup = popup.PopupMenu(
230            parent=self._root_widget,
231            position=(230, v - 50),
232            width=150,
233            scale=popup_menu_scale,
234            choices=['Auto', 'High', 'Medium', 'Low'],
235            choices_display=[
236                ba.Lstr(resource='autoText'),
237                ba.Lstr(resource=self._r + '.highText'),
238                ba.Lstr(resource=self._r + '.mediumText'),
239                ba.Lstr(resource=self._r + '.lowText'),
240            ],
241            current_choice=ba.app.config.resolve('Texture Quality'),
242            on_value_change_call=self._set_textures,
243        )
244        if ba.app.ui.use_toolbars:
245            ba.widget(
246                edit=textures_popup.get_button(),
247                right_widget=ba.internal.get_special_widget('party_button'),
248            )
249        v -= 80
250
251        h_offs = 0
252
253        if show_resolution:
254            # resolution
255            ba.textwidget(
256                parent=self._root_widget,
257                position=(h_offs + 60, v),
258                size=(160, 25),
259                text=ba.Lstr(resource=self._r + '.resolutionText'),
260                color=ba.app.ui.heading_color,
261                scale=0.65,
262                maxwidth=150,
263                h_align='center',
264                v_align='center',
265            )
266
267            # on standard android we have 'Auto', 'Native', and a few
268            # HD standards
269            if app.platform == 'android':
270                # on cardboard/daydream android we have a few
271                # render-target-scale options
272                if app.subplatform == 'cardboard':
273                    current_res_cardboard = (
274                        str(
275                            min(
276                                100,
277                                max(
278                                    10,
279                                    int(
280                                        round(
281                                            ba.app.config.resolve(
282                                                'GVR Render Target Scale'
283                                            )
284                                            * 100.0
285                                        )
286                                    ),
287                                ),
288                            )
289                        )
290                        + '%'
291                    )  # yapf: disable
292                    popup.PopupMenu(
293                        parent=self._root_widget,
294                        position=(h_offs + 60, v - 50),
295                        width=120,
296                        scale=popup_menu_scale,
297                        choices=['100%', '75%', '50%', '35%'],
298                        current_choice=current_res_cardboard,
299                        on_value_change_call=self._set_gvr_render_target_scale,
300                    )
301                else:
302                    native_res = ba.internal.get_display_resolution()
303                    assert native_res is not None
304                    choices = ['Auto', 'Native']
305                    choices_display = [
306                        ba.Lstr(resource='autoText'),
307                        ba.Lstr(resource='nativeText'),
308                    ]
309                    for res in [1440, 1080, 960, 720, 480]:
310                        # nav bar is 72px so lets allow for that in what
311                        # choices we show
312                        if native_res[1] >= res - 72:
313                            res_str = str(res) + 'p'
314                            choices.append(res_str)
315                            choices_display.append(ba.Lstr(value=res_str))
316                    current_res_android = ba.app.config.resolve(
317                        'Resolution (Android)'
318                    )
319                    popup.PopupMenu(
320                        parent=self._root_widget,
321                        position=(h_offs + 60, v - 50),
322                        width=120,
323                        scale=popup_menu_scale,
324                        choices=choices,
325                        choices_display=choices_display,
326                        current_choice=current_res_android,
327                        on_value_change_call=self._set_android_res,
328                    )
329            else:
330                # if we're on a system that doesn't allow setting resolution,
331                # set pixel-scale instead
332                current_res = ba.internal.get_display_resolution()
333                if current_res is None:
334                    current_res2 = (
335                        str(
336                            min(
337                                100,
338                                max(
339                                    10,
340                                    int(
341                                        round(
342                                            ba.app.config.resolve(
343                                                'Screen Pixel Scale'
344                                            )
345                                            * 100.0
346                                        )
347                                    ),
348                                ),
349                            )
350                        )
351                        + '%'
352                    )  # yapf: disable
353                    popup.PopupMenu(
354                        parent=self._root_widget,
355                        position=(h_offs + 60, v - 50),
356                        width=120,
357                        scale=popup_menu_scale,
358                        choices=['100%', '88%', '75%', '63%', '50%'],
359                        current_choice=current_res2,
360                        on_value_change_call=self._set_pixel_scale,
361                    )
362                else:
363                    raise Exception(
364                        'obsolete path; discrete resolutions'
365                        ' no longer supported'
366                    )
367
368        # vsync
369        if show_vsync:
370            ba.textwidget(
371                parent=self._root_widget,
372                position=(230, v),
373                size=(160, 25),
374                text=ba.Lstr(resource=self._r + '.verticalSyncText'),
375                color=ba.app.ui.heading_color,
376                scale=0.65,
377                maxwidth=150,
378                h_align='center',
379                v_align='center',
380            )
381
382            popup.PopupMenu(
383                parent=self._root_widget,
384                position=(230, v - 50),
385                width=150,
386                scale=popup_menu_scale,
387                choices=['Auto', 'Always', 'Never'],
388                choices_display=[
389                    ba.Lstr(resource='autoText'),
390                    ba.Lstr(resource=self._r + '.alwaysText'),
391                    ba.Lstr(resource=self._r + '.neverText'),
392                ],
393                current_choice=ba.app.config.resolve('Vertical Sync'),
394                on_value_change_call=self._set_vsync,
395            )
396
397        v -= 90
398        fpsc = ConfigCheckBox(
399            parent=self._root_widget,
400            position=(69, v - 6),
401            size=(210, 30),
402            scale=0.86,
403            configkey='Show FPS',
404            displayname=ba.Lstr(resource=self._r + '.showFPSText'),
405            maxwidth=130,
406        )
407
408        # (tv mode doesnt apply to vr)
409        if not ba.app.vr_mode:
410            tvc = ConfigCheckBox(
411                parent=self._root_widget,
412                position=(240, v - 6),
413                size=(210, 30),
414                scale=0.86,
415                configkey='TV Border',
416                displayname=ba.Lstr(resource=self._r + '.tvBorderText'),
417                maxwidth=130,
418            )
419            # grumble..
420            ba.widget(edit=fpsc.widget, right_widget=tvc.widget)
421        try:
422            pass
423
424        except Exception:
425            ba.print_exception('Exception wiring up graphics settings UI:')
426
427        v -= spacing
428
429        # make a timer to update our controls in case the config changes
430        # under us
431        self._update_timer = ba.Timer(
432            0.25,
433            ba.WeakCall(self._update_controls),
434            repeat=True,
435            timetype=ba.TimeType.REAL,
436        )
Inherited Members
ba.ui.Window
get_root_widget