bauiv1lib.playlist.edit

Provides a window for editing individual game playlists.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides a window for editing individual game playlists."""
  4
  5from __future__ import annotations
  6
  7import logging
  8from typing import TYPE_CHECKING, cast
  9
 10import bascenev1 as bs
 11import bauiv1 as bui
 12
 13if TYPE_CHECKING:
 14    from bauiv1lib.playlist.editcontroller import PlaylistEditController
 15
 16
 17class PlaylistEditWindow(bui.Window):
 18    """Window for editing an individual game playlist."""
 19
 20    def __init__(
 21        self,
 22        editcontroller: PlaylistEditController,
 23        transition: str = 'in_right',
 24    ):
 25        # pylint: disable=too-many-statements
 26        # pylint: disable=too-many-locals
 27        prev_selection: str | None
 28        self._editcontroller = editcontroller
 29        self._r = 'editGameListWindow'
 30        prev_selection = self._editcontroller.get_edit_ui_selection()
 31
 32        assert bui.app.classic is not None
 33        uiscale = bui.app.ui_v1.uiscale
 34        self._width = 870 if uiscale is bui.UIScale.SMALL else 670
 35        x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
 36        self._height = (
 37            400
 38            if uiscale is bui.UIScale.SMALL
 39            else 470
 40            if uiscale is bui.UIScale.MEDIUM
 41            else 540
 42        )
 43
 44        top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
 45        super().__init__(
 46            root_widget=bui.containerwidget(
 47                size=(self._width, self._height + top_extra),
 48                transition=transition,
 49                scale=(
 50                    2.0
 51                    if uiscale is bui.UIScale.SMALL
 52                    else 1.3
 53                    if uiscale is bui.UIScale.MEDIUM
 54                    else 1.0
 55                ),
 56                stack_offset=(0, -16)
 57                if uiscale is bui.UIScale.SMALL
 58                else (0, 0),
 59            )
 60        )
 61        cancel_button = bui.buttonwidget(
 62            parent=self._root_widget,
 63            position=(35 + x_inset, self._height - 60),
 64            scale=0.8,
 65            size=(175, 60),
 66            autoselect=True,
 67            label=bui.Lstr(resource='cancelText'),
 68            text_scale=1.2,
 69        )
 70        save_button = btn = bui.buttonwidget(
 71            parent=self._root_widget,
 72            position=(self._width - (195 + x_inset), self._height - 60),
 73            scale=0.8,
 74            size=(190, 60),
 75            autoselect=True,
 76            left_widget=cancel_button,
 77            label=bui.Lstr(resource='saveText'),
 78            text_scale=1.2,
 79        )
 80
 81        if bui.app.ui_v1.use_toolbars:
 82            bui.widget(
 83                edit=btn,
 84                right_widget=bui.get_special_widget('party_button'),
 85            )
 86
 87        bui.widget(
 88            edit=cancel_button,
 89            left_widget=cancel_button,
 90            right_widget=save_button,
 91        )
 92
 93        bui.textwidget(
 94            parent=self._root_widget,
 95            position=(-10, self._height - 50),
 96            size=(self._width, 25),
 97            text=bui.Lstr(resource=self._r + '.titleText'),
 98            color=bui.app.ui_v1.title_color,
 99            scale=1.05,
100            h_align='center',
101            v_align='center',
102            maxwidth=270,
103        )
104
105        v = self._height - 115.0
106
107        self._scroll_width = self._width - (205 + 2 * x_inset)
108
109        bui.textwidget(
110            parent=self._root_widget,
111            text=bui.Lstr(resource=self._r + '.listNameText'),
112            position=(196 + x_inset, v + 31),
113            maxwidth=150,
114            color=(0.8, 0.8, 0.8, 0.5),
115            size=(0, 0),
116            scale=0.75,
117            h_align='right',
118            v_align='center',
119        )
120
121        self._text_field = bui.textwidget(
122            parent=self._root_widget,
123            position=(210 + x_inset, v + 7),
124            size=(self._scroll_width - 53, 43),
125            text=self._editcontroller.getname(),
126            h_align='left',
127            v_align='center',
128            max_chars=40,
129            autoselect=True,
130            color=(0.9, 0.9, 0.9, 1.0),
131            description=bui.Lstr(resource=self._r + '.listNameText'),
132            editable=True,
133            padding=4,
134            on_return_press_call=self._save_press_with_sound,
135        )
136        bui.widget(edit=cancel_button, down_widget=self._text_field)
137
138        self._list_widgets: list[bui.Widget] = []
139
140        h = 40 + x_inset
141        v = self._height - 172.0
142
143        b_color = (0.6, 0.53, 0.63)
144        b_textcolor = (0.75, 0.7, 0.8)
145
146        v -= 2.0
147        v += 63
148
149        scl = (
150            1.03
151            if uiscale is bui.UIScale.SMALL
152            else 1.36
153            if uiscale is bui.UIScale.MEDIUM
154            else 1.74
155        )
156        v -= 63.0 * scl
157
158        add_game_button = bui.buttonwidget(
159            parent=self._root_widget,
160            position=(h, v),
161            size=(110, 61.0 * scl),
162            on_activate_call=self._add,
163            on_select_call=bui.Call(self._set_ui_selection, 'add_button'),
164            autoselect=True,
165            button_type='square',
166            color=b_color,
167            textcolor=b_textcolor,
168            text_scale=0.8,
169            label=bui.Lstr(resource=self._r + '.addGameText'),
170        )
171        bui.widget(edit=add_game_button, up_widget=self._text_field)
172        v -= 63.0 * scl
173
174        self._edit_button = edit_game_button = bui.buttonwidget(
175            parent=self._root_widget,
176            position=(h, v),
177            size=(110, 61.0 * scl),
178            on_activate_call=self._edit,
179            on_select_call=bui.Call(self._set_ui_selection, 'editButton'),
180            autoselect=True,
181            button_type='square',
182            color=b_color,
183            textcolor=b_textcolor,
184            text_scale=0.8,
185            label=bui.Lstr(resource=self._r + '.editGameText'),
186        )
187        v -= 63.0 * scl
188
189        remove_game_button = bui.buttonwidget(
190            parent=self._root_widget,
191            position=(h, v),
192            size=(110, 61.0 * scl),
193            text_scale=0.8,
194            on_activate_call=self._remove,
195            autoselect=True,
196            button_type='square',
197            color=b_color,
198            textcolor=b_textcolor,
199            label=bui.Lstr(resource=self._r + '.removeGameText'),
200        )
201        v -= 40
202        h += 9
203        bui.buttonwidget(
204            parent=self._root_widget,
205            position=(h, v),
206            size=(42, 35),
207            on_activate_call=self._move_up,
208            label=bui.charstr(bui.SpecialChar.UP_ARROW),
209            button_type='square',
210            color=b_color,
211            textcolor=b_textcolor,
212            autoselect=True,
213            repeat=True,
214        )
215        h += 52
216        bui.buttonwidget(
217            parent=self._root_widget,
218            position=(h, v),
219            size=(42, 35),
220            on_activate_call=self._move_down,
221            autoselect=True,
222            button_type='square',
223            color=b_color,
224            textcolor=b_textcolor,
225            label=bui.charstr(bui.SpecialChar.DOWN_ARROW),
226            repeat=True,
227        )
228
229        v = self._height - 100
230        scroll_height = self._height - 155
231        scrollwidget = bui.scrollwidget(
232            parent=self._root_widget,
233            position=(160 + x_inset, v - scroll_height),
234            highlight=False,
235            on_select_call=bui.Call(self._set_ui_selection, 'gameList'),
236            size=(self._scroll_width, (scroll_height - 15)),
237        )
238        bui.widget(
239            edit=scrollwidget,
240            left_widget=add_game_button,
241            right_widget=scrollwidget,
242        )
243        self._columnwidget = bui.columnwidget(
244            parent=scrollwidget, border=2, margin=0
245        )
246        bui.widget(edit=self._columnwidget, up_widget=self._text_field)
247
248        for button in [add_game_button, edit_game_button, remove_game_button]:
249            bui.widget(
250                edit=button, left_widget=button, right_widget=scrollwidget
251            )
252
253        self._refresh()
254
255        bui.buttonwidget(edit=cancel_button, on_activate_call=self._cancel)
256        bui.containerwidget(
257            edit=self._root_widget,
258            cancel_button=cancel_button,
259            selected_child=scrollwidget,
260        )
261
262        bui.buttonwidget(edit=save_button, on_activate_call=self._save_press)
263        bui.containerwidget(edit=self._root_widget, start_button=save_button)
264
265        if prev_selection == 'add_button':
266            bui.containerwidget(
267                edit=self._root_widget, selected_child=add_game_button
268            )
269        elif prev_selection == 'editButton':
270            bui.containerwidget(
271                edit=self._root_widget, selected_child=edit_game_button
272            )
273        elif prev_selection == 'gameList':
274            bui.containerwidget(
275                edit=self._root_widget, selected_child=scrollwidget
276            )
277
278    def _set_ui_selection(self, selection: str) -> None:
279        self._editcontroller.set_edit_ui_selection(selection)
280
281    def _cancel(self) -> None:
282        from bauiv1lib.playlist.customizebrowser import (
283            PlaylistCustomizeBrowserWindow,
284        )
285
286        # no-op if our underlying widget is dead or on its way out.
287        if not self._root_widget or self._root_widget.transitioning_out:
288            return
289
290        bui.getsound('powerdown01').play()
291        bui.containerwidget(edit=self._root_widget, transition='out_right')
292        assert bui.app.classic is not None
293        bui.app.ui_v1.set_main_menu_window(
294            PlaylistCustomizeBrowserWindow(
295                transition='in_left',
296                sessiontype=self._editcontroller.get_session_type(),
297                select_playlist=(
298                    self._editcontroller.get_existing_playlist_name()
299                ),
300            ).get_root_widget(),
301            from_window=self._root_widget,
302        )
303
304    def _add(self) -> None:
305        # Store list name then tell the session to perform an add.
306        self._editcontroller.setname(
307            cast(str, bui.textwidget(query=self._text_field))
308        )
309        self._editcontroller.add_game_pressed()
310
311    def _edit(self) -> None:
312        # Store list name then tell the session to perform an add.
313        self._editcontroller.setname(
314            cast(str, bui.textwidget(query=self._text_field))
315        )
316        self._editcontroller.edit_game_pressed()
317
318    def _save_press(self) -> None:
319        from bauiv1lib.playlist.customizebrowser import (
320            PlaylistCustomizeBrowserWindow,
321        )
322
323        # no-op if our underlying widget is dead or on its way out.
324        if not self._root_widget or self._root_widget.transitioning_out:
325            return
326
327        plus = bui.app.plus
328        assert plus is not None
329
330        new_name = cast(str, bui.textwidget(query=self._text_field))
331        if (
332            new_name != self._editcontroller.get_existing_playlist_name()
333            and new_name
334            in bui.app.config[
335                self._editcontroller.get_config_name() + ' Playlists'
336            ]
337        ):
338            bui.screenmessage(
339                bui.Lstr(resource=self._r + '.cantSaveAlreadyExistsText')
340            )
341            bui.getsound('error').play()
342            return
343        if not new_name:
344            bui.getsound('error').play()
345            return
346        if not self._editcontroller.get_playlist():
347            bui.screenmessage(
348                bui.Lstr(resource=self._r + '.cantSaveEmptyListText')
349            )
350            bui.getsound('error').play()
351            return
352
353        # We couldn't actually replace the default list anyway, but disallow
354        # using its exact name to avoid confusion.
355        if new_name == self._editcontroller.get_default_list_name().evaluate():
356            bui.screenmessage(
357                bui.Lstr(resource=self._r + '.cantOverwriteDefaultText')
358            )
359            bui.getsound('error').play()
360            return
361
362        # If we had an old one, delete it.
363        if self._editcontroller.get_existing_playlist_name() is not None:
364            plus.add_v1_account_transaction(
365                {
366                    'type': 'REMOVE_PLAYLIST',
367                    'playlistType': self._editcontroller.get_config_name(),
368                    'playlistName': (
369                        self._editcontroller.get_existing_playlist_name()
370                    ),
371                }
372            )
373
374        plus.add_v1_account_transaction(
375            {
376                'type': 'ADD_PLAYLIST',
377                'playlistType': self._editcontroller.get_config_name(),
378                'playlistName': new_name,
379                'playlist': self._editcontroller.get_playlist(),
380            }
381        )
382        plus.run_v1_account_transactions()
383
384        bui.containerwidget(edit=self._root_widget, transition='out_right')
385        bui.getsound('gunCocking').play()
386        assert bui.app.classic is not None
387        bui.app.ui_v1.set_main_menu_window(
388            PlaylistCustomizeBrowserWindow(
389                transition='in_left',
390                sessiontype=self._editcontroller.get_session_type(),
391                select_playlist=new_name,
392            ).get_root_widget(),
393            from_window=self._root_widget,
394        )
395
396    def _save_press_with_sound(self) -> None:
397        bui.getsound('swish').play()
398        self._save_press()
399
400    def _select(self, index: int) -> None:
401        self._editcontroller.set_selected_index(index)
402
403    def _refresh(self) -> None:
404        # Need to grab this here as rebuilding the list will
405        # change it otherwise.
406        old_selection_index = self._editcontroller.get_selected_index()
407
408        while self._list_widgets:
409            self._list_widgets.pop().delete()
410        for index, pentry in enumerate(self._editcontroller.get_playlist()):
411            try:
412                cls = bui.getclass(pentry['type'], subclassof=bs.GameActivity)
413                desc = cls.get_settings_display_string(pentry)
414            except Exception:
415                logging.exception('Error in playlist refresh.')
416                desc = "(invalid: '" + pentry['type'] + "')"
417
418            txtw = bui.textwidget(
419                parent=self._columnwidget,
420                size=(self._width - 80, 30),
421                on_select_call=bui.Call(self._select, index),
422                always_highlight=True,
423                color=(0.8, 0.8, 0.8, 1.0),
424                padding=0,
425                maxwidth=self._scroll_width * 0.93,
426                text=desc,
427                on_activate_call=self._edit_button.activate,
428                v_align='center',
429                selectable=True,
430            )
431            bui.widget(edit=txtw, show_buffer_top=50, show_buffer_bottom=50)
432
433            # Wanna be able to jump up to the text field from the top one.
434            if index == 0:
435                bui.widget(edit=txtw, up_widget=self._text_field)
436            self._list_widgets.append(txtw)
437            if old_selection_index == index:
438                bui.columnwidget(
439                    edit=self._columnwidget,
440                    selected_child=txtw,
441                    visible_child=txtw,
442                )
443
444    def _move_down(self) -> None:
445        playlist = self._editcontroller.get_playlist()
446        index = self._editcontroller.get_selected_index()
447        if index >= len(playlist) - 1:
448            return
449        tmp = playlist[index]
450        playlist[index] = playlist[index + 1]
451        playlist[index + 1] = tmp
452        index += 1
453        self._editcontroller.set_playlist(playlist)
454        self._editcontroller.set_selected_index(index)
455        self._refresh()
456
457    def _move_up(self) -> None:
458        playlist = self._editcontroller.get_playlist()
459        index = self._editcontroller.get_selected_index()
460        if index < 1:
461            return
462        tmp = playlist[index]
463        playlist[index] = playlist[index - 1]
464        playlist[index - 1] = tmp
465        index -= 1
466        self._editcontroller.set_playlist(playlist)
467        self._editcontroller.set_selected_index(index)
468        self._refresh()
469
470    def _remove(self) -> None:
471        playlist = self._editcontroller.get_playlist()
472        index = self._editcontroller.get_selected_index()
473        if not playlist:
474            return
475        del playlist[index]
476        if index >= len(playlist):
477            index = len(playlist) - 1
478        self._editcontroller.set_playlist(playlist)
479        self._editcontroller.set_selected_index(index)
480        bui.getsound('shieldDown').play()
481        self._refresh()
class PlaylistEditWindow(bauiv1._uitypes.Window):
 18class PlaylistEditWindow(bui.Window):
 19    """Window for editing an individual game playlist."""
 20
 21    def __init__(
 22        self,
 23        editcontroller: PlaylistEditController,
 24        transition: str = 'in_right',
 25    ):
 26        # pylint: disable=too-many-statements
 27        # pylint: disable=too-many-locals
 28        prev_selection: str | None
 29        self._editcontroller = editcontroller
 30        self._r = 'editGameListWindow'
 31        prev_selection = self._editcontroller.get_edit_ui_selection()
 32
 33        assert bui.app.classic is not None
 34        uiscale = bui.app.ui_v1.uiscale
 35        self._width = 870 if uiscale is bui.UIScale.SMALL else 670
 36        x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
 37        self._height = (
 38            400
 39            if uiscale is bui.UIScale.SMALL
 40            else 470
 41            if uiscale is bui.UIScale.MEDIUM
 42            else 540
 43        )
 44
 45        top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
 46        super().__init__(
 47            root_widget=bui.containerwidget(
 48                size=(self._width, self._height + top_extra),
 49                transition=transition,
 50                scale=(
 51                    2.0
 52                    if uiscale is bui.UIScale.SMALL
 53                    else 1.3
 54                    if uiscale is bui.UIScale.MEDIUM
 55                    else 1.0
 56                ),
 57                stack_offset=(0, -16)
 58                if uiscale is bui.UIScale.SMALL
 59                else (0, 0),
 60            )
 61        )
 62        cancel_button = bui.buttonwidget(
 63            parent=self._root_widget,
 64            position=(35 + x_inset, self._height - 60),
 65            scale=0.8,
 66            size=(175, 60),
 67            autoselect=True,
 68            label=bui.Lstr(resource='cancelText'),
 69            text_scale=1.2,
 70        )
 71        save_button = btn = bui.buttonwidget(
 72            parent=self._root_widget,
 73            position=(self._width - (195 + x_inset), self._height - 60),
 74            scale=0.8,
 75            size=(190, 60),
 76            autoselect=True,
 77            left_widget=cancel_button,
 78            label=bui.Lstr(resource='saveText'),
 79            text_scale=1.2,
 80        )
 81
 82        if bui.app.ui_v1.use_toolbars:
 83            bui.widget(
 84                edit=btn,
 85                right_widget=bui.get_special_widget('party_button'),
 86            )
 87
 88        bui.widget(
 89            edit=cancel_button,
 90            left_widget=cancel_button,
 91            right_widget=save_button,
 92        )
 93
 94        bui.textwidget(
 95            parent=self._root_widget,
 96            position=(-10, self._height - 50),
 97            size=(self._width, 25),
 98            text=bui.Lstr(resource=self._r + '.titleText'),
 99            color=bui.app.ui_v1.title_color,
100            scale=1.05,
101            h_align='center',
102            v_align='center',
103            maxwidth=270,
104        )
105
106        v = self._height - 115.0
107
108        self._scroll_width = self._width - (205 + 2 * x_inset)
109
110        bui.textwidget(
111            parent=self._root_widget,
112            text=bui.Lstr(resource=self._r + '.listNameText'),
113            position=(196 + x_inset, v + 31),
114            maxwidth=150,
115            color=(0.8, 0.8, 0.8, 0.5),
116            size=(0, 0),
117            scale=0.75,
118            h_align='right',
119            v_align='center',
120        )
121
122        self._text_field = bui.textwidget(
123            parent=self._root_widget,
124            position=(210 + x_inset, v + 7),
125            size=(self._scroll_width - 53, 43),
126            text=self._editcontroller.getname(),
127            h_align='left',
128            v_align='center',
129            max_chars=40,
130            autoselect=True,
131            color=(0.9, 0.9, 0.9, 1.0),
132            description=bui.Lstr(resource=self._r + '.listNameText'),
133            editable=True,
134            padding=4,
135            on_return_press_call=self._save_press_with_sound,
136        )
137        bui.widget(edit=cancel_button, down_widget=self._text_field)
138
139        self._list_widgets: list[bui.Widget] = []
140
141        h = 40 + x_inset
142        v = self._height - 172.0
143
144        b_color = (0.6, 0.53, 0.63)
145        b_textcolor = (0.75, 0.7, 0.8)
146
147        v -= 2.0
148        v += 63
149
150        scl = (
151            1.03
152            if uiscale is bui.UIScale.SMALL
153            else 1.36
154            if uiscale is bui.UIScale.MEDIUM
155            else 1.74
156        )
157        v -= 63.0 * scl
158
159        add_game_button = bui.buttonwidget(
160            parent=self._root_widget,
161            position=(h, v),
162            size=(110, 61.0 * scl),
163            on_activate_call=self._add,
164            on_select_call=bui.Call(self._set_ui_selection, 'add_button'),
165            autoselect=True,
166            button_type='square',
167            color=b_color,
168            textcolor=b_textcolor,
169            text_scale=0.8,
170            label=bui.Lstr(resource=self._r + '.addGameText'),
171        )
172        bui.widget(edit=add_game_button, up_widget=self._text_field)
173        v -= 63.0 * scl
174
175        self._edit_button = edit_game_button = bui.buttonwidget(
176            parent=self._root_widget,
177            position=(h, v),
178            size=(110, 61.0 * scl),
179            on_activate_call=self._edit,
180            on_select_call=bui.Call(self._set_ui_selection, 'editButton'),
181            autoselect=True,
182            button_type='square',
183            color=b_color,
184            textcolor=b_textcolor,
185            text_scale=0.8,
186            label=bui.Lstr(resource=self._r + '.editGameText'),
187        )
188        v -= 63.0 * scl
189
190        remove_game_button = bui.buttonwidget(
191            parent=self._root_widget,
192            position=(h, v),
193            size=(110, 61.0 * scl),
194            text_scale=0.8,
195            on_activate_call=self._remove,
196            autoselect=True,
197            button_type='square',
198            color=b_color,
199            textcolor=b_textcolor,
200            label=bui.Lstr(resource=self._r + '.removeGameText'),
201        )
202        v -= 40
203        h += 9
204        bui.buttonwidget(
205            parent=self._root_widget,
206            position=(h, v),
207            size=(42, 35),
208            on_activate_call=self._move_up,
209            label=bui.charstr(bui.SpecialChar.UP_ARROW),
210            button_type='square',
211            color=b_color,
212            textcolor=b_textcolor,
213            autoselect=True,
214            repeat=True,
215        )
216        h += 52
217        bui.buttonwidget(
218            parent=self._root_widget,
219            position=(h, v),
220            size=(42, 35),
221            on_activate_call=self._move_down,
222            autoselect=True,
223            button_type='square',
224            color=b_color,
225            textcolor=b_textcolor,
226            label=bui.charstr(bui.SpecialChar.DOWN_ARROW),
227            repeat=True,
228        )
229
230        v = self._height - 100
231        scroll_height = self._height - 155
232        scrollwidget = bui.scrollwidget(
233            parent=self._root_widget,
234            position=(160 + x_inset, v - scroll_height),
235            highlight=False,
236            on_select_call=bui.Call(self._set_ui_selection, 'gameList'),
237            size=(self._scroll_width, (scroll_height - 15)),
238        )
239        bui.widget(
240            edit=scrollwidget,
241            left_widget=add_game_button,
242            right_widget=scrollwidget,
243        )
244        self._columnwidget = bui.columnwidget(
245            parent=scrollwidget, border=2, margin=0
246        )
247        bui.widget(edit=self._columnwidget, up_widget=self._text_field)
248
249        for button in [add_game_button, edit_game_button, remove_game_button]:
250            bui.widget(
251                edit=button, left_widget=button, right_widget=scrollwidget
252            )
253
254        self._refresh()
255
256        bui.buttonwidget(edit=cancel_button, on_activate_call=self._cancel)
257        bui.containerwidget(
258            edit=self._root_widget,
259            cancel_button=cancel_button,
260            selected_child=scrollwidget,
261        )
262
263        bui.buttonwidget(edit=save_button, on_activate_call=self._save_press)
264        bui.containerwidget(edit=self._root_widget, start_button=save_button)
265
266        if prev_selection == 'add_button':
267            bui.containerwidget(
268                edit=self._root_widget, selected_child=add_game_button
269            )
270        elif prev_selection == 'editButton':
271            bui.containerwidget(
272                edit=self._root_widget, selected_child=edit_game_button
273            )
274        elif prev_selection == 'gameList':
275            bui.containerwidget(
276                edit=self._root_widget, selected_child=scrollwidget
277            )
278
279    def _set_ui_selection(self, selection: str) -> None:
280        self._editcontroller.set_edit_ui_selection(selection)
281
282    def _cancel(self) -> None:
283        from bauiv1lib.playlist.customizebrowser import (
284            PlaylistCustomizeBrowserWindow,
285        )
286
287        # no-op if our underlying widget is dead or on its way out.
288        if not self._root_widget or self._root_widget.transitioning_out:
289            return
290
291        bui.getsound('powerdown01').play()
292        bui.containerwidget(edit=self._root_widget, transition='out_right')
293        assert bui.app.classic is not None
294        bui.app.ui_v1.set_main_menu_window(
295            PlaylistCustomizeBrowserWindow(
296                transition='in_left',
297                sessiontype=self._editcontroller.get_session_type(),
298                select_playlist=(
299                    self._editcontroller.get_existing_playlist_name()
300                ),
301            ).get_root_widget(),
302            from_window=self._root_widget,
303        )
304
305    def _add(self) -> None:
306        # Store list name then tell the session to perform an add.
307        self._editcontroller.setname(
308            cast(str, bui.textwidget(query=self._text_field))
309        )
310        self._editcontroller.add_game_pressed()
311
312    def _edit(self) -> None:
313        # Store list name then tell the session to perform an add.
314        self._editcontroller.setname(
315            cast(str, bui.textwidget(query=self._text_field))
316        )
317        self._editcontroller.edit_game_pressed()
318
319    def _save_press(self) -> None:
320        from bauiv1lib.playlist.customizebrowser import (
321            PlaylistCustomizeBrowserWindow,
322        )
323
324        # no-op if our underlying widget is dead or on its way out.
325        if not self._root_widget or self._root_widget.transitioning_out:
326            return
327
328        plus = bui.app.plus
329        assert plus is not None
330
331        new_name = cast(str, bui.textwidget(query=self._text_field))
332        if (
333            new_name != self._editcontroller.get_existing_playlist_name()
334            and new_name
335            in bui.app.config[
336                self._editcontroller.get_config_name() + ' Playlists'
337            ]
338        ):
339            bui.screenmessage(
340                bui.Lstr(resource=self._r + '.cantSaveAlreadyExistsText')
341            )
342            bui.getsound('error').play()
343            return
344        if not new_name:
345            bui.getsound('error').play()
346            return
347        if not self._editcontroller.get_playlist():
348            bui.screenmessage(
349                bui.Lstr(resource=self._r + '.cantSaveEmptyListText')
350            )
351            bui.getsound('error').play()
352            return
353
354        # We couldn't actually replace the default list anyway, but disallow
355        # using its exact name to avoid confusion.
356        if new_name == self._editcontroller.get_default_list_name().evaluate():
357            bui.screenmessage(
358                bui.Lstr(resource=self._r + '.cantOverwriteDefaultText')
359            )
360            bui.getsound('error').play()
361            return
362
363        # If we had an old one, delete it.
364        if self._editcontroller.get_existing_playlist_name() is not None:
365            plus.add_v1_account_transaction(
366                {
367                    'type': 'REMOVE_PLAYLIST',
368                    'playlistType': self._editcontroller.get_config_name(),
369                    'playlistName': (
370                        self._editcontroller.get_existing_playlist_name()
371                    ),
372                }
373            )
374
375        plus.add_v1_account_transaction(
376            {
377                'type': 'ADD_PLAYLIST',
378                'playlistType': self._editcontroller.get_config_name(),
379                'playlistName': new_name,
380                'playlist': self._editcontroller.get_playlist(),
381            }
382        )
383        plus.run_v1_account_transactions()
384
385        bui.containerwidget(edit=self._root_widget, transition='out_right')
386        bui.getsound('gunCocking').play()
387        assert bui.app.classic is not None
388        bui.app.ui_v1.set_main_menu_window(
389            PlaylistCustomizeBrowserWindow(
390                transition='in_left',
391                sessiontype=self._editcontroller.get_session_type(),
392                select_playlist=new_name,
393            ).get_root_widget(),
394            from_window=self._root_widget,
395        )
396
397    def _save_press_with_sound(self) -> None:
398        bui.getsound('swish').play()
399        self._save_press()
400
401    def _select(self, index: int) -> None:
402        self._editcontroller.set_selected_index(index)
403
404    def _refresh(self) -> None:
405        # Need to grab this here as rebuilding the list will
406        # change it otherwise.
407        old_selection_index = self._editcontroller.get_selected_index()
408
409        while self._list_widgets:
410            self._list_widgets.pop().delete()
411        for index, pentry in enumerate(self._editcontroller.get_playlist()):
412            try:
413                cls = bui.getclass(pentry['type'], subclassof=bs.GameActivity)
414                desc = cls.get_settings_display_string(pentry)
415            except Exception:
416                logging.exception('Error in playlist refresh.')
417                desc = "(invalid: '" + pentry['type'] + "')"
418
419            txtw = bui.textwidget(
420                parent=self._columnwidget,
421                size=(self._width - 80, 30),
422                on_select_call=bui.Call(self._select, index),
423                always_highlight=True,
424                color=(0.8, 0.8, 0.8, 1.0),
425                padding=0,
426                maxwidth=self._scroll_width * 0.93,
427                text=desc,
428                on_activate_call=self._edit_button.activate,
429                v_align='center',
430                selectable=True,
431            )
432            bui.widget(edit=txtw, show_buffer_top=50, show_buffer_bottom=50)
433
434            # Wanna be able to jump up to the text field from the top one.
435            if index == 0:
436                bui.widget(edit=txtw, up_widget=self._text_field)
437            self._list_widgets.append(txtw)
438            if old_selection_index == index:
439                bui.columnwidget(
440                    edit=self._columnwidget,
441                    selected_child=txtw,
442                    visible_child=txtw,
443                )
444
445    def _move_down(self) -> None:
446        playlist = self._editcontroller.get_playlist()
447        index = self._editcontroller.get_selected_index()
448        if index >= len(playlist) - 1:
449            return
450        tmp = playlist[index]
451        playlist[index] = playlist[index + 1]
452        playlist[index + 1] = tmp
453        index += 1
454        self._editcontroller.set_playlist(playlist)
455        self._editcontroller.set_selected_index(index)
456        self._refresh()
457
458    def _move_up(self) -> None:
459        playlist = self._editcontroller.get_playlist()
460        index = self._editcontroller.get_selected_index()
461        if index < 1:
462            return
463        tmp = playlist[index]
464        playlist[index] = playlist[index - 1]
465        playlist[index - 1] = tmp
466        index -= 1
467        self._editcontroller.set_playlist(playlist)
468        self._editcontroller.set_selected_index(index)
469        self._refresh()
470
471    def _remove(self) -> None:
472        playlist = self._editcontroller.get_playlist()
473        index = self._editcontroller.get_selected_index()
474        if not playlist:
475            return
476        del playlist[index]
477        if index >= len(playlist):
478            index = len(playlist) - 1
479        self._editcontroller.set_playlist(playlist)
480        self._editcontroller.set_selected_index(index)
481        bui.getsound('shieldDown').play()
482        self._refresh()

Window for editing an individual game playlist.

PlaylistEditWindow( editcontroller: bauiv1lib.playlist.editcontroller.PlaylistEditController, transition: str = 'in_right')
 21    def __init__(
 22        self,
 23        editcontroller: PlaylistEditController,
 24        transition: str = 'in_right',
 25    ):
 26        # pylint: disable=too-many-statements
 27        # pylint: disable=too-many-locals
 28        prev_selection: str | None
 29        self._editcontroller = editcontroller
 30        self._r = 'editGameListWindow'
 31        prev_selection = self._editcontroller.get_edit_ui_selection()
 32
 33        assert bui.app.classic is not None
 34        uiscale = bui.app.ui_v1.uiscale
 35        self._width = 870 if uiscale is bui.UIScale.SMALL else 670
 36        x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
 37        self._height = (
 38            400
 39            if uiscale is bui.UIScale.SMALL
 40            else 470
 41            if uiscale is bui.UIScale.MEDIUM
 42            else 540
 43        )
 44
 45        top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
 46        super().__init__(
 47            root_widget=bui.containerwidget(
 48                size=(self._width, self._height + top_extra),
 49                transition=transition,
 50                scale=(
 51                    2.0
 52                    if uiscale is bui.UIScale.SMALL
 53                    else 1.3
 54                    if uiscale is bui.UIScale.MEDIUM
 55                    else 1.0
 56                ),
 57                stack_offset=(0, -16)
 58                if uiscale is bui.UIScale.SMALL
 59                else (0, 0),
 60            )
 61        )
 62        cancel_button = bui.buttonwidget(
 63            parent=self._root_widget,
 64            position=(35 + x_inset, self._height - 60),
 65            scale=0.8,
 66            size=(175, 60),
 67            autoselect=True,
 68            label=bui.Lstr(resource='cancelText'),
 69            text_scale=1.2,
 70        )
 71        save_button = btn = bui.buttonwidget(
 72            parent=self._root_widget,
 73            position=(self._width - (195 + x_inset), self._height - 60),
 74            scale=0.8,
 75            size=(190, 60),
 76            autoselect=True,
 77            left_widget=cancel_button,
 78            label=bui.Lstr(resource='saveText'),
 79            text_scale=1.2,
 80        )
 81
 82        if bui.app.ui_v1.use_toolbars:
 83            bui.widget(
 84                edit=btn,
 85                right_widget=bui.get_special_widget('party_button'),
 86            )
 87
 88        bui.widget(
 89            edit=cancel_button,
 90            left_widget=cancel_button,
 91            right_widget=save_button,
 92        )
 93
 94        bui.textwidget(
 95            parent=self._root_widget,
 96            position=(-10, self._height - 50),
 97            size=(self._width, 25),
 98            text=bui.Lstr(resource=self._r + '.titleText'),
 99            color=bui.app.ui_v1.title_color,
100            scale=1.05,
101            h_align='center',
102            v_align='center',
103            maxwidth=270,
104        )
105
106        v = self._height - 115.0
107
108        self._scroll_width = self._width - (205 + 2 * x_inset)
109
110        bui.textwidget(
111            parent=self._root_widget,
112            text=bui.Lstr(resource=self._r + '.listNameText'),
113            position=(196 + x_inset, v + 31),
114            maxwidth=150,
115            color=(0.8, 0.8, 0.8, 0.5),
116            size=(0, 0),
117            scale=0.75,
118            h_align='right',
119            v_align='center',
120        )
121
122        self._text_field = bui.textwidget(
123            parent=self._root_widget,
124            position=(210 + x_inset, v + 7),
125            size=(self._scroll_width - 53, 43),
126            text=self._editcontroller.getname(),
127            h_align='left',
128            v_align='center',
129            max_chars=40,
130            autoselect=True,
131            color=(0.9, 0.9, 0.9, 1.0),
132            description=bui.Lstr(resource=self._r + '.listNameText'),
133            editable=True,
134            padding=4,
135            on_return_press_call=self._save_press_with_sound,
136        )
137        bui.widget(edit=cancel_button, down_widget=self._text_field)
138
139        self._list_widgets: list[bui.Widget] = []
140
141        h = 40 + x_inset
142        v = self._height - 172.0
143
144        b_color = (0.6, 0.53, 0.63)
145        b_textcolor = (0.75, 0.7, 0.8)
146
147        v -= 2.0
148        v += 63
149
150        scl = (
151            1.03
152            if uiscale is bui.UIScale.SMALL
153            else 1.36
154            if uiscale is bui.UIScale.MEDIUM
155            else 1.74
156        )
157        v -= 63.0 * scl
158
159        add_game_button = bui.buttonwidget(
160            parent=self._root_widget,
161            position=(h, v),
162            size=(110, 61.0 * scl),
163            on_activate_call=self._add,
164            on_select_call=bui.Call(self._set_ui_selection, 'add_button'),
165            autoselect=True,
166            button_type='square',
167            color=b_color,
168            textcolor=b_textcolor,
169            text_scale=0.8,
170            label=bui.Lstr(resource=self._r + '.addGameText'),
171        )
172        bui.widget(edit=add_game_button, up_widget=self._text_field)
173        v -= 63.0 * scl
174
175        self._edit_button = edit_game_button = bui.buttonwidget(
176            parent=self._root_widget,
177            position=(h, v),
178            size=(110, 61.0 * scl),
179            on_activate_call=self._edit,
180            on_select_call=bui.Call(self._set_ui_selection, 'editButton'),
181            autoselect=True,
182            button_type='square',
183            color=b_color,
184            textcolor=b_textcolor,
185            text_scale=0.8,
186            label=bui.Lstr(resource=self._r + '.editGameText'),
187        )
188        v -= 63.0 * scl
189
190        remove_game_button = bui.buttonwidget(
191            parent=self._root_widget,
192            position=(h, v),
193            size=(110, 61.0 * scl),
194            text_scale=0.8,
195            on_activate_call=self._remove,
196            autoselect=True,
197            button_type='square',
198            color=b_color,
199            textcolor=b_textcolor,
200            label=bui.Lstr(resource=self._r + '.removeGameText'),
201        )
202        v -= 40
203        h += 9
204        bui.buttonwidget(
205            parent=self._root_widget,
206            position=(h, v),
207            size=(42, 35),
208            on_activate_call=self._move_up,
209            label=bui.charstr(bui.SpecialChar.UP_ARROW),
210            button_type='square',
211            color=b_color,
212            textcolor=b_textcolor,
213            autoselect=True,
214            repeat=True,
215        )
216        h += 52
217        bui.buttonwidget(
218            parent=self._root_widget,
219            position=(h, v),
220            size=(42, 35),
221            on_activate_call=self._move_down,
222            autoselect=True,
223            button_type='square',
224            color=b_color,
225            textcolor=b_textcolor,
226            label=bui.charstr(bui.SpecialChar.DOWN_ARROW),
227            repeat=True,
228        )
229
230        v = self._height - 100
231        scroll_height = self._height - 155
232        scrollwidget = bui.scrollwidget(
233            parent=self._root_widget,
234            position=(160 + x_inset, v - scroll_height),
235            highlight=False,
236            on_select_call=bui.Call(self._set_ui_selection, 'gameList'),
237            size=(self._scroll_width, (scroll_height - 15)),
238        )
239        bui.widget(
240            edit=scrollwidget,
241            left_widget=add_game_button,
242            right_widget=scrollwidget,
243        )
244        self._columnwidget = bui.columnwidget(
245            parent=scrollwidget, border=2, margin=0
246        )
247        bui.widget(edit=self._columnwidget, up_widget=self._text_field)
248
249        for button in [add_game_button, edit_game_button, remove_game_button]:
250            bui.widget(
251                edit=button, left_widget=button, right_widget=scrollwidget
252            )
253
254        self._refresh()
255
256        bui.buttonwidget(edit=cancel_button, on_activate_call=self._cancel)
257        bui.containerwidget(
258            edit=self._root_widget,
259            cancel_button=cancel_button,
260            selected_child=scrollwidget,
261        )
262
263        bui.buttonwidget(edit=save_button, on_activate_call=self._save_press)
264        bui.containerwidget(edit=self._root_widget, start_button=save_button)
265
266        if prev_selection == 'add_button':
267            bui.containerwidget(
268                edit=self._root_widget, selected_child=add_game_button
269            )
270        elif prev_selection == 'editButton':
271            bui.containerwidget(
272                edit=self._root_widget, selected_child=edit_game_button
273            )
274        elif prev_selection == 'gameList':
275            bui.containerwidget(
276                edit=self._root_widget, selected_child=scrollwidget
277            )
Inherited Members
bauiv1._uitypes.Window
get_root_widget