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