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

Window for graphics settings.

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