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

Window for editing an individual game playlist.

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

Create a MainWindow given a root widget and transition info.

Automatically handles in and out transitions on the provided widget, so there is no need to set transitions when creating it.

@override
def get_main_window_state(self) -> bauiv1.MainWindowState:
275    @override
276    def get_main_window_state(self) -> bui.MainWindowState:
277        # Support recreating our window for back/refresh purposes.
278        cls = type(self)
279
280        editcontroller = self._editcontroller
281
282        return bui.BasicMainWindowState(
283            create_call=lambda transition, origin_widget: cls(
284                transition=transition,
285                origin_widget=origin_widget,
286                editcontroller=editcontroller,
287            )
288        )

Return a WindowState to recreate this window, if supported.

Inherited Members
bauiv1._uitypes.MainWindow
main_window_back_state
main_window_close
can_change_main_window
main_window_back
main_window_replace
on_main_window_close
bauiv1._uitypes.Window
get_root_widget