bauiv1lib.profile.edit

Provides UI to edit a player profile.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides UI to edit a player profile."""
  4
  5from __future__ import annotations
  6
  7import random
  8from typing import cast, override
  9
 10from bauiv1lib.colorpicker import ColorPicker
 11import bauiv1 as bui
 12import bascenev1 as bs
 13
 14
 15class EditProfileWindow(bui.MainWindow):
 16    """Window for editing a player profile."""
 17
 18    def reload_window(self) -> None:
 19        """Transitions out and recreates ourself."""
 20
 21        # no-op if our underlying widget is dead or on its way out.
 22        if not self._root_widget or self._root_widget.transitioning_out:
 23            return
 24
 25        bui.containerwidget(edit=self._root_widget, transition='out_left')
 26        assert bui.app.classic is not None
 27        bui.app.ui_v1.set_main_window(
 28            EditProfileWindow(self.getname(), self._in_main_menu),
 29            from_window=self,
 30            is_back=True,
 31        )
 32
 33    def __init__(
 34        self,
 35        existing_profile: str | None,
 36        in_main_menu: bool,
 37        transition: str | None = 'in_right',
 38        origin_widget: bui.Widget | None = None,
 39    ):
 40        # FIXME: Tidy this up a bit.
 41        # pylint: disable=too-many-branches
 42        # pylint: disable=too-many-statements
 43        # pylint: disable=too-many-locals
 44        assert bui.app.classic is not None
 45
 46        plus = bui.app.plus
 47        assert plus is not None
 48
 49        self._in_main_menu = in_main_menu
 50        self._existing_profile = existing_profile
 51        self._r = 'editProfileWindow'
 52        self._spazzes: list[str] = []
 53        self._icon_textures: list[bui.Texture] = []
 54        self._icon_tint_textures: list[bui.Texture] = []
 55
 56        # Grab profile colors or pick random ones.
 57        (
 58            self._color,
 59            self._highlight,
 60        ) = bui.app.classic.get_player_profile_colors(existing_profile)
 61        uiscale = bui.app.ui_v1.uiscale
 62        self._width = width = 880.0 if uiscale is bui.UIScale.SMALL else 680.0
 63        self._x_inset = x_inset = 100.0 if uiscale is bui.UIScale.SMALL else 0.0
 64        self._height = height = (
 65            450.0
 66            if uiscale is bui.UIScale.SMALL
 67            else 400.0 if uiscale is bui.UIScale.MEDIUM else 450.0
 68        )
 69        spacing = 40
 70        self._base_scale = (
 71            1.6
 72            if uiscale is bui.UIScale.SMALL
 73            else 1.35 if uiscale is bui.UIScale.MEDIUM else 1.0
 74        )
 75        top_extra = 70 if uiscale is bui.UIScale.SMALL else 15
 76        super().__init__(
 77            root_widget=bui.containerwidget(
 78                size=(width, height + top_extra),
 79                scale=self._base_scale,
 80                stack_offset=(
 81                    (0, -40) if uiscale is bui.UIScale.SMALL else (0, 0)
 82                ),
 83                toolbar_visibility=(
 84                    'menu_minimal'
 85                    if uiscale is bui.UIScale.SMALL
 86                    else 'menu_full'
 87                ),
 88            ),
 89            transition=transition,
 90            origin_widget=origin_widget,
 91        )
 92        cancel_button = btn = bui.buttonwidget(
 93            parent=self._root_widget,
 94            position=(52 + x_inset, height - 60),
 95            size=(155, 60),
 96            scale=0.8,
 97            autoselect=True,
 98            label=bui.Lstr(resource='cancelText'),
 99            on_activate_call=self._cancel,
100        )
101        bui.containerwidget(edit=self._root_widget, cancel_button=btn)
102        save_button = btn = bui.buttonwidget(
103            parent=self._root_widget,
104            position=(width - (177 + x_inset), height - 60),
105            size=(155, 60),
106            autoselect=True,
107            scale=0.8,
108            label=bui.Lstr(resource='saveText'),
109        )
110        bui.widget(edit=save_button, left_widget=cancel_button)
111        bui.widget(edit=cancel_button, right_widget=save_button)
112        bui.containerwidget(edit=self._root_widget, start_button=btn)
113        bui.textwidget(
114            parent=self._root_widget,
115            position=(self._width * 0.5, height - 38),
116            size=(0, 0),
117            text=(
118                bui.Lstr(resource=f'{self._r}.titleNewText')
119                if existing_profile is None
120                else bui.Lstr(resource=f'{self._r}.titleEditText')
121            ),
122            color=bui.app.ui_v1.title_color,
123            maxwidth=290,
124            scale=1.0,
125            h_align='center',
126            v_align='center',
127        )
128
129        # Make a list of spaz icons.
130        self.refresh_characters()
131        profile = bui.app.config.get('Player Profiles', {}).get(
132            self._existing_profile, {}
133        )
134
135        if 'global' in profile:
136            self._global = profile['global']
137        else:
138            self._global = False
139
140        if 'icon' in profile:
141            self._icon = profile['icon']
142        else:
143            self._icon = bui.charstr(bui.SpecialChar.LOGO)
144
145        assigned_random_char = False
146
147        # Look for existing character choice or pick random one otherwise.
148        try:
149            icon_index = self._spazzes.index(profile['character'])
150        except Exception:
151            # Let's set the default icon to spaz for our first profile; after
152            # that we go random.
153            # (SCRATCH THAT.. we now hard-code account-profiles to start with
154            # spaz which has a similar effect)
155            # try: p_len = len(bui.app.config['Player Profiles'])
156            # except Exception: p_len = 0
157            # if p_len == 0: icon_index = self._spazzes.index('Spaz')
158            # else:
159            random.seed()
160            icon_index = random.randrange(len(self._spazzes))
161            assigned_random_char = True
162        self._icon_index = icon_index
163        bui.buttonwidget(edit=save_button, on_activate_call=self.save)
164
165        v = height - 115.0
166        self._name = (
167            '' if self._existing_profile is None else self._existing_profile
168        )
169        self._is_account_profile = self._name == '__account__'
170
171        # If we just picked a random character, see if it has specific
172        # colors/highlights associated with it and assign them if so.
173        if assigned_random_char:
174            assert bui.app.classic is not None
175            clr = bui.app.classic.spaz_appearances[
176                self._spazzes[icon_index]
177            ].default_color
178            if clr is not None:
179                self._color = clr
180            highlight = bui.app.classic.spaz_appearances[
181                self._spazzes[icon_index]
182            ].default_highlight
183            if highlight is not None:
184                self._highlight = highlight
185
186        # Assign a random name if they had none.
187        if self._name == '':
188            names = bs.get_random_names()
189            self._name = names[random.randrange(len(names))]
190
191        self._clipped_name_text = bui.textwidget(
192            parent=self._root_widget,
193            text='',
194            position=(580 + x_inset, v - 8),
195            flatness=1.0,
196            shadow=0.0,
197            scale=0.55,
198            size=(0, 0),
199            maxwidth=100,
200            h_align='center',
201            v_align='center',
202            color=(1, 1, 0, 0.5),
203        )
204
205        if not self._is_account_profile and not self._global:
206            bui.textwidget(
207                parent=self._root_widget,
208                text=bui.Lstr(resource=f'{self._r}.nameText'),
209                position=(200 + x_inset, v - 6),
210                size=(0, 0),
211                h_align='right',
212                v_align='center',
213                color=(1, 1, 1, 0.5),
214                scale=0.9,
215            )
216
217        self._upgrade_button = None
218        if self._is_account_profile:
219            if plus.get_v1_account_state() == 'signed_in':
220                sval = plus.get_v1_account_display_string()
221            else:
222                sval = '??'
223            bui.textwidget(
224                parent=self._root_widget,
225                position=(self._width * 0.5, v - 7),
226                size=(0, 0),
227                scale=1.2,
228                text=sval,
229                maxwidth=270,
230                h_align='center',
231                v_align='center',
232            )
233            txtl = bui.Lstr(
234                resource='editProfileWindow.accountProfileText'
235            ).evaluate()
236            b_width = min(
237                270.0,
238                bui.get_string_width(txtl, suppress_warning=True) * 0.6,
239            )
240            bui.textwidget(
241                parent=self._root_widget,
242                position=(self._width * 0.5, v - 39),
243                size=(0, 0),
244                scale=0.6,
245                color=bui.app.ui_v1.infotextcolor,
246                text=txtl,
247                maxwidth=270,
248                h_align='center',
249                v_align='center',
250            )
251            self._account_type_info_button = bui.buttonwidget(
252                parent=self._root_widget,
253                label='?',
254                size=(15, 15),
255                text_scale=0.6,
256                position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47),
257                button_type='square',
258                color=(0.6, 0.5, 0.65),
259                autoselect=True,
260                on_activate_call=self.show_account_profile_info,
261            )
262        elif self._global:
263            b_size = 60
264            self._icon_button = btn = bui.buttonwidget(
265                parent=self._root_widget,
266                autoselect=True,
267                position=(self._width * 0.5 - 160 - b_size * 0.5, v - 38 - 15),
268                size=(b_size, b_size),
269                color=(0.6, 0.5, 0.6),
270                label='',
271                button_type='square',
272                text_scale=1.2,
273                on_activate_call=self._on_icon_press,
274            )
275            self._icon_button_label = bui.textwidget(
276                parent=self._root_widget,
277                position=(self._width * 0.5 - 160, v - 35),
278                draw_controller=btn,
279                h_align='center',
280                v_align='center',
281                size=(0, 0),
282                color=(1, 1, 1),
283                text='',
284                scale=2.0,
285            )
286
287            bui.textwidget(
288                parent=self._root_widget,
289                h_align='center',
290                v_align='center',
291                position=(self._width * 0.5 - 160, v - 55 - 15),
292                size=(0, 0),
293                draw_controller=btn,
294                text=bui.Lstr(resource=f'{self._r}.iconText'),
295                scale=0.7,
296                color=bui.app.ui_v1.title_color,
297                maxwidth=120,
298            )
299
300            self._update_icon()
301
302            bui.textwidget(
303                parent=self._root_widget,
304                position=(self._width * 0.5, v - 7),
305                size=(0, 0),
306                scale=1.2,
307                text=self._name,
308                maxwidth=240,
309                h_align='center',
310                v_align='center',
311            )
312            # FIXME hard coded strings are bad
313            txtl = bui.Lstr(
314                resource='editProfileWindow.globalProfileText'
315            ).evaluate()
316            b_width = min(
317                240.0,
318                bui.get_string_width(txtl, suppress_warning=True) * 0.6,
319            )
320            bui.textwidget(
321                parent=self._root_widget,
322                position=(self._width * 0.5, v - 39),
323                size=(0, 0),
324                scale=0.6,
325                color=bui.app.ui_v1.infotextcolor,
326                text=txtl,
327                maxwidth=240,
328                h_align='center',
329                v_align='center',
330            )
331            self._account_type_info_button = bui.buttonwidget(
332                parent=self._root_widget,
333                label='?',
334                size=(15, 15),
335                text_scale=0.6,
336                position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47),
337                button_type='square',
338                color=(0.6, 0.5, 0.65),
339                autoselect=True,
340                on_activate_call=self.show_global_profile_info,
341            )
342        else:
343            self._text_field = bui.textwidget(
344                parent=self._root_widget,
345                position=(220 + x_inset, v - 30),
346                size=(265, 40),
347                text=self._name,
348                h_align='left',
349                v_align='center',
350                max_chars=16,
351                description=bui.Lstr(resource=f'{self._r}.nameDescriptionText'),
352                autoselect=True,
353                editable=True,
354                padding=4,
355                color=(0.9, 0.9, 0.9, 1.0),
356                on_return_press_call=bui.Call(save_button.activate),
357            )
358
359            # FIXME hard coded strings are bad
360            txtl = bui.Lstr(
361                resource='editProfileWindow.localProfileText'
362            ).evaluate()
363            b_width = min(
364                270.0,
365                bui.get_string_width(txtl, suppress_warning=True) * 0.6,
366            )
367            bui.textwidget(
368                parent=self._root_widget,
369                position=(self._width * 0.5, v - 43),
370                size=(0, 0),
371                scale=0.6,
372                color=bui.app.ui_v1.infotextcolor,
373                text=txtl,
374                maxwidth=270,
375                h_align='center',
376                v_align='center',
377            )
378            self._account_type_info_button = bui.buttonwidget(
379                parent=self._root_widget,
380                label='?',
381                size=(15, 15),
382                text_scale=0.6,
383                position=(self._width * 0.5 + b_width * 0.5 + 13, v - 50),
384                button_type='square',
385                color=(0.6, 0.5, 0.65),
386                autoselect=True,
387                on_activate_call=self.show_local_profile_info,
388            )
389            self._upgrade_button = bui.buttonwidget(
390                parent=self._root_widget,
391                label=bui.Lstr(resource='upgradeText'),
392                size=(40, 17),
393                text_scale=1.0,
394                button_type='square',
395                position=(self._width * 0.5 + b_width * 0.5 + 13 + 43, v - 51),
396                color=(0.6, 0.5, 0.65),
397                autoselect=True,
398                on_activate_call=self.upgrade_profile,
399            )
400            self._random_name_button = bui.buttonwidget(
401                parent=self._root_widget,
402                label=bui.Lstr(resource='randomText'),
403                size=(30, 20),
404                position=(495 + x_inset, v - 20),
405                button_type='square',
406                color=(0.6, 0.5, 0.65),
407                autoselect=True,
408                on_activate_call=self.assign_random_name,
409            )
410
411        self._update_clipped_name()
412        self._clipped_name_timer = bui.AppTimer(
413            0.333, bui.WeakCall(self._update_clipped_name), repeat=True
414        )
415
416        v -= spacing * 3.0
417        b_size = 80
418        b_size_2 = 100
419        b_offs = 150
420        self._color_button = btn = bui.buttonwidget(
421            parent=self._root_widget,
422            autoselect=True,
423            position=(self._width * 0.5 - b_offs - b_size * 0.5, v - 50),
424            size=(b_size, b_size),
425            color=self._color,
426            label='',
427            button_type='square',
428        )
429        origin = self._color_button.get_screen_space_center()
430        bui.buttonwidget(
431            edit=self._color_button,
432            on_activate_call=bui.WeakCall(self._make_picker, 'color', origin),
433        )
434        bui.textwidget(
435            parent=self._root_widget,
436            h_align='center',
437            v_align='center',
438            position=(self._width * 0.5 - b_offs, v - 65),
439            size=(0, 0),
440            draw_controller=btn,
441            text=bui.Lstr(resource=f'{self._r}.colorText'),
442            scale=0.7,
443            color=bui.app.ui_v1.title_color,
444            maxwidth=120,
445        )
446
447        self._character_button = btn = bui.buttonwidget(
448            parent=self._root_widget,
449            autoselect=True,
450            position=(self._width * 0.5 - b_size_2 * 0.5, v - 60),
451            up_widget=self._account_type_info_button,
452            on_activate_call=self._on_character_press,
453            size=(b_size_2, b_size_2),
454            label='',
455            color=(1, 1, 1),
456            mask_texture=bui.gettexture('characterIconMask'),
457        )
458        if not self._is_account_profile and not self._global:
459            bui.containerwidget(
460                edit=self._root_widget, selected_child=self._text_field
461            )
462        bui.textwidget(
463            parent=self._root_widget,
464            h_align='center',
465            v_align='center',
466            position=(self._width * 0.5, v - 80),
467            size=(0, 0),
468            draw_controller=btn,
469            text=bui.Lstr(resource=f'{self._r}.characterText'),
470            scale=0.7,
471            color=bui.app.ui_v1.title_color,
472            maxwidth=130,
473        )
474
475        self._highlight_button = btn = bui.buttonwidget(
476            parent=self._root_widget,
477            autoselect=True,
478            position=(self._width * 0.5 + b_offs - b_size * 0.5, v - 50),
479            up_widget=(
480                self._upgrade_button
481                if self._upgrade_button is not None
482                else self._account_type_info_button
483            ),
484            size=(b_size, b_size),
485            color=self._highlight,
486            label='',
487            button_type='square',
488        )
489
490        if not self._is_account_profile and not self._global:
491            bui.widget(edit=cancel_button, down_widget=self._text_field)
492            bui.widget(edit=save_button, down_widget=self._text_field)
493            bui.widget(edit=self._color_button, up_widget=self._text_field)
494        bui.widget(
495            edit=self._account_type_info_button,
496            down_widget=self._character_button,
497        )
498
499        origin = self._highlight_button.get_screen_space_center()
500        bui.buttonwidget(
501            edit=self._highlight_button,
502            on_activate_call=bui.WeakCall(
503                self._make_picker, 'highlight', origin
504            ),
505        )
506        bui.textwidget(
507            parent=self._root_widget,
508            h_align='center',
509            v_align='center',
510            position=(self._width * 0.5 + b_offs, v - 65),
511            size=(0, 0),
512            draw_controller=btn,
513            text=bui.Lstr(resource=f'{self._r}.highlightText'),
514            scale=0.7,
515            color=bui.app.ui_v1.title_color,
516            maxwidth=120,
517        )
518        self._update_character()
519
520    @override
521    def get_main_window_state(self) -> bui.MainWindowState:
522        # Support recreating our window for back/refresh purposes.
523        cls = type(self)
524        return bui.BasicMainWindowState(
525            create_call=lambda transition, origin_widget: cls(
526                transition=transition,
527                origin_widget=origin_widget,
528                existing_profile=self._existing_profile,
529                in_main_menu=self._in_main_menu,
530            )
531        )
532
533    def assign_random_name(self) -> None:
534        """Assigning a random name to the player."""
535        names = bs.get_random_names()
536        name = names[random.randrange(len(names))]
537        bui.textwidget(
538            edit=self._text_field,
539            text=name,
540        )
541
542    def upgrade_profile(self) -> None:
543        """Attempt to upgrade the profile to global."""
544        from bauiv1lib import account
545        from bauiv1lib.profile import upgrade as pupgrade
546
547        plus = bui.app.plus
548        assert plus is not None
549
550        if plus.get_v1_account_state() != 'signed_in':
551            account.show_sign_in_prompt()
552            return
553
554        pupgrade.ProfileUpgradeWindow(self)
555
556    def show_account_profile_info(self) -> None:
557        """Show an explanation of account profiles."""
558        from bauiv1lib.confirm import ConfirmWindow
559
560        icons_str = ' '.join(
561            [
562                bui.charstr(n)
563                for n in [
564                    bui.SpecialChar.GOOGLE_PLAY_GAMES_LOGO,
565                    bui.SpecialChar.GAME_CENTER_LOGO,
566                    bui.SpecialChar.LOCAL_ACCOUNT,
567                    bui.SpecialChar.OCULUS_LOGO,
568                    bui.SpecialChar.NVIDIA_LOGO,
569                    bui.SpecialChar.V2_LOGO,
570                ]
571            ]
572        )
573        txtl = bui.Lstr(
574            resource='editProfileWindow.accountProfileInfoText',
575            subs=[('${ICONS}', icons_str)],
576        )
577        ConfirmWindow(
578            txtl,
579            cancel_button=False,
580            width=500,
581            height=300,
582            origin_widget=self._account_type_info_button,
583        )
584
585    def show_local_profile_info(self) -> None:
586        """Show an explanation of local profiles."""
587        from bauiv1lib.confirm import ConfirmWindow
588
589        txtl = bui.Lstr(resource='editProfileWindow.localProfileInfoText')
590        ConfirmWindow(
591            txtl,
592            cancel_button=False,
593            width=600,
594            height=250,
595            origin_widget=self._account_type_info_button,
596        )
597
598    def show_global_profile_info(self) -> None:
599        """Show an explanation of global profiles."""
600        from bauiv1lib.confirm import ConfirmWindow
601
602        txtl = bui.Lstr(resource='editProfileWindow.globalProfileInfoText')
603        ConfirmWindow(
604            txtl,
605            cancel_button=False,
606            width=600,
607            height=250,
608            origin_widget=self._account_type_info_button,
609        )
610
611    def refresh_characters(self) -> None:
612        """Refresh available characters/icons."""
613        from bascenev1lib.actor import spazappearance
614
615        assert bui.app.classic is not None
616
617        self._spazzes = spazappearance.get_appearances()
618        self._spazzes.sort()
619        self._icon_textures = [
620            bui.gettexture(bui.app.classic.spaz_appearances[s].icon_texture)
621            for s in self._spazzes
622        ]
623        self._icon_tint_textures = [
624            bui.gettexture(
625                bui.app.classic.spaz_appearances[s].icon_mask_texture
626            )
627            for s in self._spazzes
628        ]
629
630    def on_icon_picker_pick(self, icon: str) -> None:
631        """An icon has been selected by the picker."""
632        self._icon = icon
633        self._update_icon()
634
635    def on_character_picker_pick(self, character: str) -> None:
636        """A character has been selected by the picker."""
637        if not self._root_widget:
638            return
639
640        # The player could have bought a new one while the picker was up.
641        self.refresh_characters()
642        self._icon_index = (
643            self._spazzes.index(character) if character in self._spazzes else 0
644        )
645        self._update_character()
646
647    def _on_character_press(self) -> None:
648        from bauiv1lib import characterpicker
649
650        characterpicker.CharacterPicker(
651            parent=self._root_widget,
652            position=self._character_button.get_screen_space_center(),
653            selected_character=self._spazzes[self._icon_index],
654            delegate=self,
655            tint_color=self._color,
656            tint2_color=self._highlight,
657        )
658
659    def _on_icon_press(self) -> None:
660        from bauiv1lib import iconpicker
661
662        iconpicker.IconPicker(
663            parent=self._root_widget,
664            position=self._icon_button.get_screen_space_center(),
665            selected_icon=self._icon,
666            delegate=self,
667            tint_color=self._color,
668            tint2_color=self._highlight,
669        )
670
671    def _make_picker(
672        self, picker_type: str, origin: tuple[float, float]
673    ) -> None:
674        if picker_type == 'color':
675            initial_color = self._color
676        elif picker_type == 'highlight':
677            initial_color = self._highlight
678        else:
679            raise ValueError('invalid picker_type: ' + picker_type)
680        ColorPicker(
681            parent=self._root_widget,
682            position=origin,
683            offset=(
684                self._base_scale * (-100 if picker_type == 'color' else 100),
685                0,
686            ),
687            initial_color=initial_color,
688            delegate=self,
689            tag=picker_type,
690        )
691
692    def _cancel(self) -> None:
693        self.main_window_back()
694        # from bauiv1lib.profile.browser import ProfileBrowserWindow
695
696        # # no-op if our underlying widget is dead or on its way out.
697        # if not self._root_widget or self._root_widget.transitioning_out:
698        #     return
699
700        # bui.containerwidget(edit=self._root_widget, transition='out_right')
701        # assert bui.app.classic is not None
702        # bui.app.ui_v1.set_main_window(
703        #     ProfileBrowserWindow(
704        #         'in_left',
705        #         selected_profile=self._existing_profile,
706        #         in_main_menu=self._in_main_menu,
707        #     ),
708        #     from_window=self,
709        #     is_back=True,
710        # )
711
712    def _set_color(self, color: tuple[float, float, float]) -> None:
713        self._color = color
714        if self._color_button:
715            bui.buttonwidget(edit=self._color_button, color=color)
716
717    def _set_highlight(self, color: tuple[float, float, float]) -> None:
718        self._highlight = color
719        if self._highlight_button:
720            bui.buttonwidget(edit=self._highlight_button, color=color)
721
722    def color_picker_closing(self, picker: ColorPicker) -> None:
723        """Called when a color picker is closing."""
724        if not self._root_widget:
725            return
726        tag = picker.get_tag()
727        if tag == 'color':
728            bui.containerwidget(
729                edit=self._root_widget, selected_child=self._color_button
730            )
731        elif tag == 'highlight':
732            bui.containerwidget(
733                edit=self._root_widget, selected_child=self._highlight_button
734            )
735        else:
736            print('color_picker_closing got unknown tag ' + str(tag))
737
738    def color_picker_selected_color(
739        self, picker: ColorPicker, color: tuple[float, float, float]
740    ) -> None:
741        """Called when a color is selected in a color picker."""
742        if not self._root_widget:
743            return
744        tag = picker.get_tag()
745        if tag == 'color':
746            self._set_color(color)
747        elif tag == 'highlight':
748            self._set_highlight(color)
749        else:
750            print('color_picker_selected_color got unknown tag ' + str(tag))
751        self._update_character()
752
753    def _update_clipped_name(self) -> None:
754        plus = bui.app.plus
755        assert plus is not None
756
757        if not self._clipped_name_text:
758            return
759        name = self.getname()
760        if name == '__account__':
761            name = (
762                plus.get_v1_account_name()
763                if plus.get_v1_account_state() == 'signed_in'
764                else '???'
765            )
766        if len(name) > 10 and not (self._global or self._is_account_profile):
767            name = name.strip()
768            display_name = (name[:10] + '...') if len(name) > 10 else name
769            bui.textwidget(
770                edit=self._clipped_name_text,
771                text=bui.Lstr(
772                    resource='inGameClippedNameText',
773                    subs=[('${NAME}', display_name)],
774                ),
775            )
776        else:
777            bui.textwidget(edit=self._clipped_name_text, text='')
778
779    def _update_character(self, change: int = 0) -> None:
780        self._icon_index = (self._icon_index + change) % len(self._spazzes)
781        if self._character_button:
782            bui.buttonwidget(
783                edit=self._character_button,
784                texture=self._icon_textures[self._icon_index],
785                tint_texture=self._icon_tint_textures[self._icon_index],
786                tint_color=self._color,
787                tint2_color=self._highlight,
788            )
789
790    def _update_icon(self) -> None:
791        if self._icon_button_label:
792            bui.textwidget(edit=self._icon_button_label, text=self._icon)
793
794    def getname(self) -> str:
795        """Return the current profile name value."""
796        if self._is_account_profile:
797            new_name = '__account__'
798        elif self._global:
799            new_name = self._name
800        else:
801            new_name = cast(str, bui.textwidget(query=self._text_field))
802        return new_name
803
804    def save(self, transition_out: bool = True) -> bool:
805        """Save has been selected."""
806
807        # no-op if our underlying widget is dead or on its way out.
808        if not self._root_widget or self._root_widget.transitioning_out:
809            return False
810
811        plus = bui.app.plus
812        assert plus is not None
813
814        new_name = self.getname().strip()
815
816        if not new_name:
817            bui.screenmessage(bui.Lstr(resource='nameNotEmptyText'))
818            bui.getsound('error').play()
819            return False
820
821        # Make sure we're not renaming to another existing profile.
822        profiles: dict = bui.app.config.get('Player Profiles', {})
823        if self._existing_profile != new_name and new_name in profiles.keys():
824            bui.screenmessage(
825                bui.Lstr(resource='editProfileWindow.profileAlreadyExistsText')
826            )
827            bui.getsound('error').play()
828            return False
829
830        if transition_out:
831            bui.getsound('gunCocking').play()
832
833        # Delete old in case we're renaming.
834        if self._existing_profile and self._existing_profile != new_name:
835            plus.add_v1_account_transaction(
836                {
837                    'type': 'REMOVE_PLAYER_PROFILE',
838                    'name': self._existing_profile,
839                }
840            )
841
842            # Also lets be aware we're no longer global if we're taking a
843            # new name (will need to re-request it).
844            self._global = False
845
846        plus.add_v1_account_transaction(
847            {
848                'type': 'ADD_PLAYER_PROFILE',
849                'name': new_name,
850                'profile': {
851                    'character': self._spazzes[self._icon_index],
852                    'color': list(self._color),
853                    'global': self._global,
854                    'icon': self._icon,
855                    'highlight': list(self._highlight),
856                },
857            }
858        )
859
860        if transition_out:
861            plus.run_v1_account_transactions()
862            self.main_window_back()
863            # bui.containerwidget(edit=self._root_widget,
864            # transition='out_right')
865            # assert bui.app.classic is not None
866            # bui.app.ui_v1.set_main_window(
867            #     ProfileBrowserWindow(
868            #         'in_left',
869            #         selected_profile=new_name,
870            #         in_main_menu=self._in_main_menu,
871            #     ),
872            #     from_window=self,
873            #     is_back=True,
874            # )
875        return True
class EditProfileWindow(bauiv1._uitypes.MainWindow):
 16class EditProfileWindow(bui.MainWindow):
 17    """Window for editing a player profile."""
 18
 19    def reload_window(self) -> None:
 20        """Transitions out and recreates ourself."""
 21
 22        # no-op if our underlying widget is dead or on its way out.
 23        if not self._root_widget or self._root_widget.transitioning_out:
 24            return
 25
 26        bui.containerwidget(edit=self._root_widget, transition='out_left')
 27        assert bui.app.classic is not None
 28        bui.app.ui_v1.set_main_window(
 29            EditProfileWindow(self.getname(), self._in_main_menu),
 30            from_window=self,
 31            is_back=True,
 32        )
 33
 34    def __init__(
 35        self,
 36        existing_profile: str | None,
 37        in_main_menu: bool,
 38        transition: str | None = 'in_right',
 39        origin_widget: bui.Widget | None = None,
 40    ):
 41        # FIXME: Tidy this up a bit.
 42        # pylint: disable=too-many-branches
 43        # pylint: disable=too-many-statements
 44        # pylint: disable=too-many-locals
 45        assert bui.app.classic is not None
 46
 47        plus = bui.app.plus
 48        assert plus is not None
 49
 50        self._in_main_menu = in_main_menu
 51        self._existing_profile = existing_profile
 52        self._r = 'editProfileWindow'
 53        self._spazzes: list[str] = []
 54        self._icon_textures: list[bui.Texture] = []
 55        self._icon_tint_textures: list[bui.Texture] = []
 56
 57        # Grab profile colors or pick random ones.
 58        (
 59            self._color,
 60            self._highlight,
 61        ) = bui.app.classic.get_player_profile_colors(existing_profile)
 62        uiscale = bui.app.ui_v1.uiscale
 63        self._width = width = 880.0 if uiscale is bui.UIScale.SMALL else 680.0
 64        self._x_inset = x_inset = 100.0 if uiscale is bui.UIScale.SMALL else 0.0
 65        self._height = height = (
 66            450.0
 67            if uiscale is bui.UIScale.SMALL
 68            else 400.0 if uiscale is bui.UIScale.MEDIUM else 450.0
 69        )
 70        spacing = 40
 71        self._base_scale = (
 72            1.6
 73            if uiscale is bui.UIScale.SMALL
 74            else 1.35 if uiscale is bui.UIScale.MEDIUM else 1.0
 75        )
 76        top_extra = 70 if uiscale is bui.UIScale.SMALL else 15
 77        super().__init__(
 78            root_widget=bui.containerwidget(
 79                size=(width, height + top_extra),
 80                scale=self._base_scale,
 81                stack_offset=(
 82                    (0, -40) if uiscale is bui.UIScale.SMALL else (0, 0)
 83                ),
 84                toolbar_visibility=(
 85                    'menu_minimal'
 86                    if uiscale is bui.UIScale.SMALL
 87                    else 'menu_full'
 88                ),
 89            ),
 90            transition=transition,
 91            origin_widget=origin_widget,
 92        )
 93        cancel_button = btn = bui.buttonwidget(
 94            parent=self._root_widget,
 95            position=(52 + x_inset, height - 60),
 96            size=(155, 60),
 97            scale=0.8,
 98            autoselect=True,
 99            label=bui.Lstr(resource='cancelText'),
100            on_activate_call=self._cancel,
101        )
102        bui.containerwidget(edit=self._root_widget, cancel_button=btn)
103        save_button = btn = bui.buttonwidget(
104            parent=self._root_widget,
105            position=(width - (177 + x_inset), height - 60),
106            size=(155, 60),
107            autoselect=True,
108            scale=0.8,
109            label=bui.Lstr(resource='saveText'),
110        )
111        bui.widget(edit=save_button, left_widget=cancel_button)
112        bui.widget(edit=cancel_button, right_widget=save_button)
113        bui.containerwidget(edit=self._root_widget, start_button=btn)
114        bui.textwidget(
115            parent=self._root_widget,
116            position=(self._width * 0.5, height - 38),
117            size=(0, 0),
118            text=(
119                bui.Lstr(resource=f'{self._r}.titleNewText')
120                if existing_profile is None
121                else bui.Lstr(resource=f'{self._r}.titleEditText')
122            ),
123            color=bui.app.ui_v1.title_color,
124            maxwidth=290,
125            scale=1.0,
126            h_align='center',
127            v_align='center',
128        )
129
130        # Make a list of spaz icons.
131        self.refresh_characters()
132        profile = bui.app.config.get('Player Profiles', {}).get(
133            self._existing_profile, {}
134        )
135
136        if 'global' in profile:
137            self._global = profile['global']
138        else:
139            self._global = False
140
141        if 'icon' in profile:
142            self._icon = profile['icon']
143        else:
144            self._icon = bui.charstr(bui.SpecialChar.LOGO)
145
146        assigned_random_char = False
147
148        # Look for existing character choice or pick random one otherwise.
149        try:
150            icon_index = self._spazzes.index(profile['character'])
151        except Exception:
152            # Let's set the default icon to spaz for our first profile; after
153            # that we go random.
154            # (SCRATCH THAT.. we now hard-code account-profiles to start with
155            # spaz which has a similar effect)
156            # try: p_len = len(bui.app.config['Player Profiles'])
157            # except Exception: p_len = 0
158            # if p_len == 0: icon_index = self._spazzes.index('Spaz')
159            # else:
160            random.seed()
161            icon_index = random.randrange(len(self._spazzes))
162            assigned_random_char = True
163        self._icon_index = icon_index
164        bui.buttonwidget(edit=save_button, on_activate_call=self.save)
165
166        v = height - 115.0
167        self._name = (
168            '' if self._existing_profile is None else self._existing_profile
169        )
170        self._is_account_profile = self._name == '__account__'
171
172        # If we just picked a random character, see if it has specific
173        # colors/highlights associated with it and assign them if so.
174        if assigned_random_char:
175            assert bui.app.classic is not None
176            clr = bui.app.classic.spaz_appearances[
177                self._spazzes[icon_index]
178            ].default_color
179            if clr is not None:
180                self._color = clr
181            highlight = bui.app.classic.spaz_appearances[
182                self._spazzes[icon_index]
183            ].default_highlight
184            if highlight is not None:
185                self._highlight = highlight
186
187        # Assign a random name if they had none.
188        if self._name == '':
189            names = bs.get_random_names()
190            self._name = names[random.randrange(len(names))]
191
192        self._clipped_name_text = bui.textwidget(
193            parent=self._root_widget,
194            text='',
195            position=(580 + x_inset, v - 8),
196            flatness=1.0,
197            shadow=0.0,
198            scale=0.55,
199            size=(0, 0),
200            maxwidth=100,
201            h_align='center',
202            v_align='center',
203            color=(1, 1, 0, 0.5),
204        )
205
206        if not self._is_account_profile and not self._global:
207            bui.textwidget(
208                parent=self._root_widget,
209                text=bui.Lstr(resource=f'{self._r}.nameText'),
210                position=(200 + x_inset, v - 6),
211                size=(0, 0),
212                h_align='right',
213                v_align='center',
214                color=(1, 1, 1, 0.5),
215                scale=0.9,
216            )
217
218        self._upgrade_button = None
219        if self._is_account_profile:
220            if plus.get_v1_account_state() == 'signed_in':
221                sval = plus.get_v1_account_display_string()
222            else:
223                sval = '??'
224            bui.textwidget(
225                parent=self._root_widget,
226                position=(self._width * 0.5, v - 7),
227                size=(0, 0),
228                scale=1.2,
229                text=sval,
230                maxwidth=270,
231                h_align='center',
232                v_align='center',
233            )
234            txtl = bui.Lstr(
235                resource='editProfileWindow.accountProfileText'
236            ).evaluate()
237            b_width = min(
238                270.0,
239                bui.get_string_width(txtl, suppress_warning=True) * 0.6,
240            )
241            bui.textwidget(
242                parent=self._root_widget,
243                position=(self._width * 0.5, v - 39),
244                size=(0, 0),
245                scale=0.6,
246                color=bui.app.ui_v1.infotextcolor,
247                text=txtl,
248                maxwidth=270,
249                h_align='center',
250                v_align='center',
251            )
252            self._account_type_info_button = bui.buttonwidget(
253                parent=self._root_widget,
254                label='?',
255                size=(15, 15),
256                text_scale=0.6,
257                position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47),
258                button_type='square',
259                color=(0.6, 0.5, 0.65),
260                autoselect=True,
261                on_activate_call=self.show_account_profile_info,
262            )
263        elif self._global:
264            b_size = 60
265            self._icon_button = btn = bui.buttonwidget(
266                parent=self._root_widget,
267                autoselect=True,
268                position=(self._width * 0.5 - 160 - b_size * 0.5, v - 38 - 15),
269                size=(b_size, b_size),
270                color=(0.6, 0.5, 0.6),
271                label='',
272                button_type='square',
273                text_scale=1.2,
274                on_activate_call=self._on_icon_press,
275            )
276            self._icon_button_label = bui.textwidget(
277                parent=self._root_widget,
278                position=(self._width * 0.5 - 160, v - 35),
279                draw_controller=btn,
280                h_align='center',
281                v_align='center',
282                size=(0, 0),
283                color=(1, 1, 1),
284                text='',
285                scale=2.0,
286            )
287
288            bui.textwidget(
289                parent=self._root_widget,
290                h_align='center',
291                v_align='center',
292                position=(self._width * 0.5 - 160, v - 55 - 15),
293                size=(0, 0),
294                draw_controller=btn,
295                text=bui.Lstr(resource=f'{self._r}.iconText'),
296                scale=0.7,
297                color=bui.app.ui_v1.title_color,
298                maxwidth=120,
299            )
300
301            self._update_icon()
302
303            bui.textwidget(
304                parent=self._root_widget,
305                position=(self._width * 0.5, v - 7),
306                size=(0, 0),
307                scale=1.2,
308                text=self._name,
309                maxwidth=240,
310                h_align='center',
311                v_align='center',
312            )
313            # FIXME hard coded strings are bad
314            txtl = bui.Lstr(
315                resource='editProfileWindow.globalProfileText'
316            ).evaluate()
317            b_width = min(
318                240.0,
319                bui.get_string_width(txtl, suppress_warning=True) * 0.6,
320            )
321            bui.textwidget(
322                parent=self._root_widget,
323                position=(self._width * 0.5, v - 39),
324                size=(0, 0),
325                scale=0.6,
326                color=bui.app.ui_v1.infotextcolor,
327                text=txtl,
328                maxwidth=240,
329                h_align='center',
330                v_align='center',
331            )
332            self._account_type_info_button = bui.buttonwidget(
333                parent=self._root_widget,
334                label='?',
335                size=(15, 15),
336                text_scale=0.6,
337                position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47),
338                button_type='square',
339                color=(0.6, 0.5, 0.65),
340                autoselect=True,
341                on_activate_call=self.show_global_profile_info,
342            )
343        else:
344            self._text_field = bui.textwidget(
345                parent=self._root_widget,
346                position=(220 + x_inset, v - 30),
347                size=(265, 40),
348                text=self._name,
349                h_align='left',
350                v_align='center',
351                max_chars=16,
352                description=bui.Lstr(resource=f'{self._r}.nameDescriptionText'),
353                autoselect=True,
354                editable=True,
355                padding=4,
356                color=(0.9, 0.9, 0.9, 1.0),
357                on_return_press_call=bui.Call(save_button.activate),
358            )
359
360            # FIXME hard coded strings are bad
361            txtl = bui.Lstr(
362                resource='editProfileWindow.localProfileText'
363            ).evaluate()
364            b_width = min(
365                270.0,
366                bui.get_string_width(txtl, suppress_warning=True) * 0.6,
367            )
368            bui.textwidget(
369                parent=self._root_widget,
370                position=(self._width * 0.5, v - 43),
371                size=(0, 0),
372                scale=0.6,
373                color=bui.app.ui_v1.infotextcolor,
374                text=txtl,
375                maxwidth=270,
376                h_align='center',
377                v_align='center',
378            )
379            self._account_type_info_button = bui.buttonwidget(
380                parent=self._root_widget,
381                label='?',
382                size=(15, 15),
383                text_scale=0.6,
384                position=(self._width * 0.5 + b_width * 0.5 + 13, v - 50),
385                button_type='square',
386                color=(0.6, 0.5, 0.65),
387                autoselect=True,
388                on_activate_call=self.show_local_profile_info,
389            )
390            self._upgrade_button = bui.buttonwidget(
391                parent=self._root_widget,
392                label=bui.Lstr(resource='upgradeText'),
393                size=(40, 17),
394                text_scale=1.0,
395                button_type='square',
396                position=(self._width * 0.5 + b_width * 0.5 + 13 + 43, v - 51),
397                color=(0.6, 0.5, 0.65),
398                autoselect=True,
399                on_activate_call=self.upgrade_profile,
400            )
401            self._random_name_button = bui.buttonwidget(
402                parent=self._root_widget,
403                label=bui.Lstr(resource='randomText'),
404                size=(30, 20),
405                position=(495 + x_inset, v - 20),
406                button_type='square',
407                color=(0.6, 0.5, 0.65),
408                autoselect=True,
409                on_activate_call=self.assign_random_name,
410            )
411
412        self._update_clipped_name()
413        self._clipped_name_timer = bui.AppTimer(
414            0.333, bui.WeakCall(self._update_clipped_name), repeat=True
415        )
416
417        v -= spacing * 3.0
418        b_size = 80
419        b_size_2 = 100
420        b_offs = 150
421        self._color_button = btn = bui.buttonwidget(
422            parent=self._root_widget,
423            autoselect=True,
424            position=(self._width * 0.5 - b_offs - b_size * 0.5, v - 50),
425            size=(b_size, b_size),
426            color=self._color,
427            label='',
428            button_type='square',
429        )
430        origin = self._color_button.get_screen_space_center()
431        bui.buttonwidget(
432            edit=self._color_button,
433            on_activate_call=bui.WeakCall(self._make_picker, 'color', origin),
434        )
435        bui.textwidget(
436            parent=self._root_widget,
437            h_align='center',
438            v_align='center',
439            position=(self._width * 0.5 - b_offs, v - 65),
440            size=(0, 0),
441            draw_controller=btn,
442            text=bui.Lstr(resource=f'{self._r}.colorText'),
443            scale=0.7,
444            color=bui.app.ui_v1.title_color,
445            maxwidth=120,
446        )
447
448        self._character_button = btn = bui.buttonwidget(
449            parent=self._root_widget,
450            autoselect=True,
451            position=(self._width * 0.5 - b_size_2 * 0.5, v - 60),
452            up_widget=self._account_type_info_button,
453            on_activate_call=self._on_character_press,
454            size=(b_size_2, b_size_2),
455            label='',
456            color=(1, 1, 1),
457            mask_texture=bui.gettexture('characterIconMask'),
458        )
459        if not self._is_account_profile and not self._global:
460            bui.containerwidget(
461                edit=self._root_widget, selected_child=self._text_field
462            )
463        bui.textwidget(
464            parent=self._root_widget,
465            h_align='center',
466            v_align='center',
467            position=(self._width * 0.5, v - 80),
468            size=(0, 0),
469            draw_controller=btn,
470            text=bui.Lstr(resource=f'{self._r}.characterText'),
471            scale=0.7,
472            color=bui.app.ui_v1.title_color,
473            maxwidth=130,
474        )
475
476        self._highlight_button = btn = bui.buttonwidget(
477            parent=self._root_widget,
478            autoselect=True,
479            position=(self._width * 0.5 + b_offs - b_size * 0.5, v - 50),
480            up_widget=(
481                self._upgrade_button
482                if self._upgrade_button is not None
483                else self._account_type_info_button
484            ),
485            size=(b_size, b_size),
486            color=self._highlight,
487            label='',
488            button_type='square',
489        )
490
491        if not self._is_account_profile and not self._global:
492            bui.widget(edit=cancel_button, down_widget=self._text_field)
493            bui.widget(edit=save_button, down_widget=self._text_field)
494            bui.widget(edit=self._color_button, up_widget=self._text_field)
495        bui.widget(
496            edit=self._account_type_info_button,
497            down_widget=self._character_button,
498        )
499
500        origin = self._highlight_button.get_screen_space_center()
501        bui.buttonwidget(
502            edit=self._highlight_button,
503            on_activate_call=bui.WeakCall(
504                self._make_picker, 'highlight', origin
505            ),
506        )
507        bui.textwidget(
508            parent=self._root_widget,
509            h_align='center',
510            v_align='center',
511            position=(self._width * 0.5 + b_offs, v - 65),
512            size=(0, 0),
513            draw_controller=btn,
514            text=bui.Lstr(resource=f'{self._r}.highlightText'),
515            scale=0.7,
516            color=bui.app.ui_v1.title_color,
517            maxwidth=120,
518        )
519        self._update_character()
520
521    @override
522    def get_main_window_state(self) -> bui.MainWindowState:
523        # Support recreating our window for back/refresh purposes.
524        cls = type(self)
525        return bui.BasicMainWindowState(
526            create_call=lambda transition, origin_widget: cls(
527                transition=transition,
528                origin_widget=origin_widget,
529                existing_profile=self._existing_profile,
530                in_main_menu=self._in_main_menu,
531            )
532        )
533
534    def assign_random_name(self) -> None:
535        """Assigning a random name to the player."""
536        names = bs.get_random_names()
537        name = names[random.randrange(len(names))]
538        bui.textwidget(
539            edit=self._text_field,
540            text=name,
541        )
542
543    def upgrade_profile(self) -> None:
544        """Attempt to upgrade the profile to global."""
545        from bauiv1lib import account
546        from bauiv1lib.profile import upgrade as pupgrade
547
548        plus = bui.app.plus
549        assert plus is not None
550
551        if plus.get_v1_account_state() != 'signed_in':
552            account.show_sign_in_prompt()
553            return
554
555        pupgrade.ProfileUpgradeWindow(self)
556
557    def show_account_profile_info(self) -> None:
558        """Show an explanation of account profiles."""
559        from bauiv1lib.confirm import ConfirmWindow
560
561        icons_str = ' '.join(
562            [
563                bui.charstr(n)
564                for n in [
565                    bui.SpecialChar.GOOGLE_PLAY_GAMES_LOGO,
566                    bui.SpecialChar.GAME_CENTER_LOGO,
567                    bui.SpecialChar.LOCAL_ACCOUNT,
568                    bui.SpecialChar.OCULUS_LOGO,
569                    bui.SpecialChar.NVIDIA_LOGO,
570                    bui.SpecialChar.V2_LOGO,
571                ]
572            ]
573        )
574        txtl = bui.Lstr(
575            resource='editProfileWindow.accountProfileInfoText',
576            subs=[('${ICONS}', icons_str)],
577        )
578        ConfirmWindow(
579            txtl,
580            cancel_button=False,
581            width=500,
582            height=300,
583            origin_widget=self._account_type_info_button,
584        )
585
586    def show_local_profile_info(self) -> None:
587        """Show an explanation of local profiles."""
588        from bauiv1lib.confirm import ConfirmWindow
589
590        txtl = bui.Lstr(resource='editProfileWindow.localProfileInfoText')
591        ConfirmWindow(
592            txtl,
593            cancel_button=False,
594            width=600,
595            height=250,
596            origin_widget=self._account_type_info_button,
597        )
598
599    def show_global_profile_info(self) -> None:
600        """Show an explanation of global profiles."""
601        from bauiv1lib.confirm import ConfirmWindow
602
603        txtl = bui.Lstr(resource='editProfileWindow.globalProfileInfoText')
604        ConfirmWindow(
605            txtl,
606            cancel_button=False,
607            width=600,
608            height=250,
609            origin_widget=self._account_type_info_button,
610        )
611
612    def refresh_characters(self) -> None:
613        """Refresh available characters/icons."""
614        from bascenev1lib.actor import spazappearance
615
616        assert bui.app.classic is not None
617
618        self._spazzes = spazappearance.get_appearances()
619        self._spazzes.sort()
620        self._icon_textures = [
621            bui.gettexture(bui.app.classic.spaz_appearances[s].icon_texture)
622            for s in self._spazzes
623        ]
624        self._icon_tint_textures = [
625            bui.gettexture(
626                bui.app.classic.spaz_appearances[s].icon_mask_texture
627            )
628            for s in self._spazzes
629        ]
630
631    def on_icon_picker_pick(self, icon: str) -> None:
632        """An icon has been selected by the picker."""
633        self._icon = icon
634        self._update_icon()
635
636    def on_character_picker_pick(self, character: str) -> None:
637        """A character has been selected by the picker."""
638        if not self._root_widget:
639            return
640
641        # The player could have bought a new one while the picker was up.
642        self.refresh_characters()
643        self._icon_index = (
644            self._spazzes.index(character) if character in self._spazzes else 0
645        )
646        self._update_character()
647
648    def _on_character_press(self) -> None:
649        from bauiv1lib import characterpicker
650
651        characterpicker.CharacterPicker(
652            parent=self._root_widget,
653            position=self._character_button.get_screen_space_center(),
654            selected_character=self._spazzes[self._icon_index],
655            delegate=self,
656            tint_color=self._color,
657            tint2_color=self._highlight,
658        )
659
660    def _on_icon_press(self) -> None:
661        from bauiv1lib import iconpicker
662
663        iconpicker.IconPicker(
664            parent=self._root_widget,
665            position=self._icon_button.get_screen_space_center(),
666            selected_icon=self._icon,
667            delegate=self,
668            tint_color=self._color,
669            tint2_color=self._highlight,
670        )
671
672    def _make_picker(
673        self, picker_type: str, origin: tuple[float, float]
674    ) -> None:
675        if picker_type == 'color':
676            initial_color = self._color
677        elif picker_type == 'highlight':
678            initial_color = self._highlight
679        else:
680            raise ValueError('invalid picker_type: ' + picker_type)
681        ColorPicker(
682            parent=self._root_widget,
683            position=origin,
684            offset=(
685                self._base_scale * (-100 if picker_type == 'color' else 100),
686                0,
687            ),
688            initial_color=initial_color,
689            delegate=self,
690            tag=picker_type,
691        )
692
693    def _cancel(self) -> None:
694        self.main_window_back()
695        # from bauiv1lib.profile.browser import ProfileBrowserWindow
696
697        # # no-op if our underlying widget is dead or on its way out.
698        # if not self._root_widget or self._root_widget.transitioning_out:
699        #     return
700
701        # bui.containerwidget(edit=self._root_widget, transition='out_right')
702        # assert bui.app.classic is not None
703        # bui.app.ui_v1.set_main_window(
704        #     ProfileBrowserWindow(
705        #         'in_left',
706        #         selected_profile=self._existing_profile,
707        #         in_main_menu=self._in_main_menu,
708        #     ),
709        #     from_window=self,
710        #     is_back=True,
711        # )
712
713    def _set_color(self, color: tuple[float, float, float]) -> None:
714        self._color = color
715        if self._color_button:
716            bui.buttonwidget(edit=self._color_button, color=color)
717
718    def _set_highlight(self, color: tuple[float, float, float]) -> None:
719        self._highlight = color
720        if self._highlight_button:
721            bui.buttonwidget(edit=self._highlight_button, color=color)
722
723    def color_picker_closing(self, picker: ColorPicker) -> None:
724        """Called when a color picker is closing."""
725        if not self._root_widget:
726            return
727        tag = picker.get_tag()
728        if tag == 'color':
729            bui.containerwidget(
730                edit=self._root_widget, selected_child=self._color_button
731            )
732        elif tag == 'highlight':
733            bui.containerwidget(
734                edit=self._root_widget, selected_child=self._highlight_button
735            )
736        else:
737            print('color_picker_closing got unknown tag ' + str(tag))
738
739    def color_picker_selected_color(
740        self, picker: ColorPicker, color: tuple[float, float, float]
741    ) -> None:
742        """Called when a color is selected in a color picker."""
743        if not self._root_widget:
744            return
745        tag = picker.get_tag()
746        if tag == 'color':
747            self._set_color(color)
748        elif tag == 'highlight':
749            self._set_highlight(color)
750        else:
751            print('color_picker_selected_color got unknown tag ' + str(tag))
752        self._update_character()
753
754    def _update_clipped_name(self) -> None:
755        plus = bui.app.plus
756        assert plus is not None
757
758        if not self._clipped_name_text:
759            return
760        name = self.getname()
761        if name == '__account__':
762            name = (
763                plus.get_v1_account_name()
764                if plus.get_v1_account_state() == 'signed_in'
765                else '???'
766            )
767        if len(name) > 10 and not (self._global or self._is_account_profile):
768            name = name.strip()
769            display_name = (name[:10] + '...') if len(name) > 10 else name
770            bui.textwidget(
771                edit=self._clipped_name_text,
772                text=bui.Lstr(
773                    resource='inGameClippedNameText',
774                    subs=[('${NAME}', display_name)],
775                ),
776            )
777        else:
778            bui.textwidget(edit=self._clipped_name_text, text='')
779
780    def _update_character(self, change: int = 0) -> None:
781        self._icon_index = (self._icon_index + change) % len(self._spazzes)
782        if self._character_button:
783            bui.buttonwidget(
784                edit=self._character_button,
785                texture=self._icon_textures[self._icon_index],
786                tint_texture=self._icon_tint_textures[self._icon_index],
787                tint_color=self._color,
788                tint2_color=self._highlight,
789            )
790
791    def _update_icon(self) -> None:
792        if self._icon_button_label:
793            bui.textwidget(edit=self._icon_button_label, text=self._icon)
794
795    def getname(self) -> str:
796        """Return the current profile name value."""
797        if self._is_account_profile:
798            new_name = '__account__'
799        elif self._global:
800            new_name = self._name
801        else:
802            new_name = cast(str, bui.textwidget(query=self._text_field))
803        return new_name
804
805    def save(self, transition_out: bool = True) -> bool:
806        """Save has been selected."""
807
808        # no-op if our underlying widget is dead or on its way out.
809        if not self._root_widget or self._root_widget.transitioning_out:
810            return False
811
812        plus = bui.app.plus
813        assert plus is not None
814
815        new_name = self.getname().strip()
816
817        if not new_name:
818            bui.screenmessage(bui.Lstr(resource='nameNotEmptyText'))
819            bui.getsound('error').play()
820            return False
821
822        # Make sure we're not renaming to another existing profile.
823        profiles: dict = bui.app.config.get('Player Profiles', {})
824        if self._existing_profile != new_name and new_name in profiles.keys():
825            bui.screenmessage(
826                bui.Lstr(resource='editProfileWindow.profileAlreadyExistsText')
827            )
828            bui.getsound('error').play()
829            return False
830
831        if transition_out:
832            bui.getsound('gunCocking').play()
833
834        # Delete old in case we're renaming.
835        if self._existing_profile and self._existing_profile != new_name:
836            plus.add_v1_account_transaction(
837                {
838                    'type': 'REMOVE_PLAYER_PROFILE',
839                    'name': self._existing_profile,
840                }
841            )
842
843            # Also lets be aware we're no longer global if we're taking a
844            # new name (will need to re-request it).
845            self._global = False
846
847        plus.add_v1_account_transaction(
848            {
849                'type': 'ADD_PLAYER_PROFILE',
850                'name': new_name,
851                'profile': {
852                    'character': self._spazzes[self._icon_index],
853                    'color': list(self._color),
854                    'global': self._global,
855                    'icon': self._icon,
856                    'highlight': list(self._highlight),
857                },
858            }
859        )
860
861        if transition_out:
862            plus.run_v1_account_transactions()
863            self.main_window_back()
864            # bui.containerwidget(edit=self._root_widget,
865            # transition='out_right')
866            # assert bui.app.classic is not None
867            # bui.app.ui_v1.set_main_window(
868            #     ProfileBrowserWindow(
869            #         'in_left',
870            #         selected_profile=new_name,
871            #         in_main_menu=self._in_main_menu,
872            #     ),
873            #     from_window=self,
874            #     is_back=True,
875            # )
876        return True

Window for editing a player profile.

EditProfileWindow( existing_profile: str | None, in_main_menu: bool, transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
 34    def __init__(
 35        self,
 36        existing_profile: str | None,
 37        in_main_menu: bool,
 38        transition: str | None = 'in_right',
 39        origin_widget: bui.Widget | None = None,
 40    ):
 41        # FIXME: Tidy this up a bit.
 42        # pylint: disable=too-many-branches
 43        # pylint: disable=too-many-statements
 44        # pylint: disable=too-many-locals
 45        assert bui.app.classic is not None
 46
 47        plus = bui.app.plus
 48        assert plus is not None
 49
 50        self._in_main_menu = in_main_menu
 51        self._existing_profile = existing_profile
 52        self._r = 'editProfileWindow'
 53        self._spazzes: list[str] = []
 54        self._icon_textures: list[bui.Texture] = []
 55        self._icon_tint_textures: list[bui.Texture] = []
 56
 57        # Grab profile colors or pick random ones.
 58        (
 59            self._color,
 60            self._highlight,
 61        ) = bui.app.classic.get_player_profile_colors(existing_profile)
 62        uiscale = bui.app.ui_v1.uiscale
 63        self._width = width = 880.0 if uiscale is bui.UIScale.SMALL else 680.0
 64        self._x_inset = x_inset = 100.0 if uiscale is bui.UIScale.SMALL else 0.0
 65        self._height = height = (
 66            450.0
 67            if uiscale is bui.UIScale.SMALL
 68            else 400.0 if uiscale is bui.UIScale.MEDIUM else 450.0
 69        )
 70        spacing = 40
 71        self._base_scale = (
 72            1.6
 73            if uiscale is bui.UIScale.SMALL
 74            else 1.35 if uiscale is bui.UIScale.MEDIUM else 1.0
 75        )
 76        top_extra = 70 if uiscale is bui.UIScale.SMALL else 15
 77        super().__init__(
 78            root_widget=bui.containerwidget(
 79                size=(width, height + top_extra),
 80                scale=self._base_scale,
 81                stack_offset=(
 82                    (0, -40) if uiscale is bui.UIScale.SMALL else (0, 0)
 83                ),
 84                toolbar_visibility=(
 85                    'menu_minimal'
 86                    if uiscale is bui.UIScale.SMALL
 87                    else 'menu_full'
 88                ),
 89            ),
 90            transition=transition,
 91            origin_widget=origin_widget,
 92        )
 93        cancel_button = btn = bui.buttonwidget(
 94            parent=self._root_widget,
 95            position=(52 + x_inset, height - 60),
 96            size=(155, 60),
 97            scale=0.8,
 98            autoselect=True,
 99            label=bui.Lstr(resource='cancelText'),
100            on_activate_call=self._cancel,
101        )
102        bui.containerwidget(edit=self._root_widget, cancel_button=btn)
103        save_button = btn = bui.buttonwidget(
104            parent=self._root_widget,
105            position=(width - (177 + x_inset), height - 60),
106            size=(155, 60),
107            autoselect=True,
108            scale=0.8,
109            label=bui.Lstr(resource='saveText'),
110        )
111        bui.widget(edit=save_button, left_widget=cancel_button)
112        bui.widget(edit=cancel_button, right_widget=save_button)
113        bui.containerwidget(edit=self._root_widget, start_button=btn)
114        bui.textwidget(
115            parent=self._root_widget,
116            position=(self._width * 0.5, height - 38),
117            size=(0, 0),
118            text=(
119                bui.Lstr(resource=f'{self._r}.titleNewText')
120                if existing_profile is None
121                else bui.Lstr(resource=f'{self._r}.titleEditText')
122            ),
123            color=bui.app.ui_v1.title_color,
124            maxwidth=290,
125            scale=1.0,
126            h_align='center',
127            v_align='center',
128        )
129
130        # Make a list of spaz icons.
131        self.refresh_characters()
132        profile = bui.app.config.get('Player Profiles', {}).get(
133            self._existing_profile, {}
134        )
135
136        if 'global' in profile:
137            self._global = profile['global']
138        else:
139            self._global = False
140
141        if 'icon' in profile:
142            self._icon = profile['icon']
143        else:
144            self._icon = bui.charstr(bui.SpecialChar.LOGO)
145
146        assigned_random_char = False
147
148        # Look for existing character choice or pick random one otherwise.
149        try:
150            icon_index = self._spazzes.index(profile['character'])
151        except Exception:
152            # Let's set the default icon to spaz for our first profile; after
153            # that we go random.
154            # (SCRATCH THAT.. we now hard-code account-profiles to start with
155            # spaz which has a similar effect)
156            # try: p_len = len(bui.app.config['Player Profiles'])
157            # except Exception: p_len = 0
158            # if p_len == 0: icon_index = self._spazzes.index('Spaz')
159            # else:
160            random.seed()
161            icon_index = random.randrange(len(self._spazzes))
162            assigned_random_char = True
163        self._icon_index = icon_index
164        bui.buttonwidget(edit=save_button, on_activate_call=self.save)
165
166        v = height - 115.0
167        self._name = (
168            '' if self._existing_profile is None else self._existing_profile
169        )
170        self._is_account_profile = self._name == '__account__'
171
172        # If we just picked a random character, see if it has specific
173        # colors/highlights associated with it and assign them if so.
174        if assigned_random_char:
175            assert bui.app.classic is not None
176            clr = bui.app.classic.spaz_appearances[
177                self._spazzes[icon_index]
178            ].default_color
179            if clr is not None:
180                self._color = clr
181            highlight = bui.app.classic.spaz_appearances[
182                self._spazzes[icon_index]
183            ].default_highlight
184            if highlight is not None:
185                self._highlight = highlight
186
187        # Assign a random name if they had none.
188        if self._name == '':
189            names = bs.get_random_names()
190            self._name = names[random.randrange(len(names))]
191
192        self._clipped_name_text = bui.textwidget(
193            parent=self._root_widget,
194            text='',
195            position=(580 + x_inset, v - 8),
196            flatness=1.0,
197            shadow=0.0,
198            scale=0.55,
199            size=(0, 0),
200            maxwidth=100,
201            h_align='center',
202            v_align='center',
203            color=(1, 1, 0, 0.5),
204        )
205
206        if not self._is_account_profile and not self._global:
207            bui.textwidget(
208                parent=self._root_widget,
209                text=bui.Lstr(resource=f'{self._r}.nameText'),
210                position=(200 + x_inset, v - 6),
211                size=(0, 0),
212                h_align='right',
213                v_align='center',
214                color=(1, 1, 1, 0.5),
215                scale=0.9,
216            )
217
218        self._upgrade_button = None
219        if self._is_account_profile:
220            if plus.get_v1_account_state() == 'signed_in':
221                sval = plus.get_v1_account_display_string()
222            else:
223                sval = '??'
224            bui.textwidget(
225                parent=self._root_widget,
226                position=(self._width * 0.5, v - 7),
227                size=(0, 0),
228                scale=1.2,
229                text=sval,
230                maxwidth=270,
231                h_align='center',
232                v_align='center',
233            )
234            txtl = bui.Lstr(
235                resource='editProfileWindow.accountProfileText'
236            ).evaluate()
237            b_width = min(
238                270.0,
239                bui.get_string_width(txtl, suppress_warning=True) * 0.6,
240            )
241            bui.textwidget(
242                parent=self._root_widget,
243                position=(self._width * 0.5, v - 39),
244                size=(0, 0),
245                scale=0.6,
246                color=bui.app.ui_v1.infotextcolor,
247                text=txtl,
248                maxwidth=270,
249                h_align='center',
250                v_align='center',
251            )
252            self._account_type_info_button = bui.buttonwidget(
253                parent=self._root_widget,
254                label='?',
255                size=(15, 15),
256                text_scale=0.6,
257                position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47),
258                button_type='square',
259                color=(0.6, 0.5, 0.65),
260                autoselect=True,
261                on_activate_call=self.show_account_profile_info,
262            )
263        elif self._global:
264            b_size = 60
265            self._icon_button = btn = bui.buttonwidget(
266                parent=self._root_widget,
267                autoselect=True,
268                position=(self._width * 0.5 - 160 - b_size * 0.5, v - 38 - 15),
269                size=(b_size, b_size),
270                color=(0.6, 0.5, 0.6),
271                label='',
272                button_type='square',
273                text_scale=1.2,
274                on_activate_call=self._on_icon_press,
275            )
276            self._icon_button_label = bui.textwidget(
277                parent=self._root_widget,
278                position=(self._width * 0.5 - 160, v - 35),
279                draw_controller=btn,
280                h_align='center',
281                v_align='center',
282                size=(0, 0),
283                color=(1, 1, 1),
284                text='',
285                scale=2.0,
286            )
287
288            bui.textwidget(
289                parent=self._root_widget,
290                h_align='center',
291                v_align='center',
292                position=(self._width * 0.5 - 160, v - 55 - 15),
293                size=(0, 0),
294                draw_controller=btn,
295                text=bui.Lstr(resource=f'{self._r}.iconText'),
296                scale=0.7,
297                color=bui.app.ui_v1.title_color,
298                maxwidth=120,
299            )
300
301            self._update_icon()
302
303            bui.textwidget(
304                parent=self._root_widget,
305                position=(self._width * 0.5, v - 7),
306                size=(0, 0),
307                scale=1.2,
308                text=self._name,
309                maxwidth=240,
310                h_align='center',
311                v_align='center',
312            )
313            # FIXME hard coded strings are bad
314            txtl = bui.Lstr(
315                resource='editProfileWindow.globalProfileText'
316            ).evaluate()
317            b_width = min(
318                240.0,
319                bui.get_string_width(txtl, suppress_warning=True) * 0.6,
320            )
321            bui.textwidget(
322                parent=self._root_widget,
323                position=(self._width * 0.5, v - 39),
324                size=(0, 0),
325                scale=0.6,
326                color=bui.app.ui_v1.infotextcolor,
327                text=txtl,
328                maxwidth=240,
329                h_align='center',
330                v_align='center',
331            )
332            self._account_type_info_button = bui.buttonwidget(
333                parent=self._root_widget,
334                label='?',
335                size=(15, 15),
336                text_scale=0.6,
337                position=(self._width * 0.5 + b_width * 0.5 + 13, v - 47),
338                button_type='square',
339                color=(0.6, 0.5, 0.65),
340                autoselect=True,
341                on_activate_call=self.show_global_profile_info,
342            )
343        else:
344            self._text_field = bui.textwidget(
345                parent=self._root_widget,
346                position=(220 + x_inset, v - 30),
347                size=(265, 40),
348                text=self._name,
349                h_align='left',
350                v_align='center',
351                max_chars=16,
352                description=bui.Lstr(resource=f'{self._r}.nameDescriptionText'),
353                autoselect=True,
354                editable=True,
355                padding=4,
356                color=(0.9, 0.9, 0.9, 1.0),
357                on_return_press_call=bui.Call(save_button.activate),
358            )
359
360            # FIXME hard coded strings are bad
361            txtl = bui.Lstr(
362                resource='editProfileWindow.localProfileText'
363            ).evaluate()
364            b_width = min(
365                270.0,
366                bui.get_string_width(txtl, suppress_warning=True) * 0.6,
367            )
368            bui.textwidget(
369                parent=self._root_widget,
370                position=(self._width * 0.5, v - 43),
371                size=(0, 0),
372                scale=0.6,
373                color=bui.app.ui_v1.infotextcolor,
374                text=txtl,
375                maxwidth=270,
376                h_align='center',
377                v_align='center',
378            )
379            self._account_type_info_button = bui.buttonwidget(
380                parent=self._root_widget,
381                label='?',
382                size=(15, 15),
383                text_scale=0.6,
384                position=(self._width * 0.5 + b_width * 0.5 + 13, v - 50),
385                button_type='square',
386                color=(0.6, 0.5, 0.65),
387                autoselect=True,
388                on_activate_call=self.show_local_profile_info,
389            )
390            self._upgrade_button = bui.buttonwidget(
391                parent=self._root_widget,
392                label=bui.Lstr(resource='upgradeText'),
393                size=(40, 17),
394                text_scale=1.0,
395                button_type='square',
396                position=(self._width * 0.5 + b_width * 0.5 + 13 + 43, v - 51),
397                color=(0.6, 0.5, 0.65),
398                autoselect=True,
399                on_activate_call=self.upgrade_profile,
400            )
401            self._random_name_button = bui.buttonwidget(
402                parent=self._root_widget,
403                label=bui.Lstr(resource='randomText'),
404                size=(30, 20),
405                position=(495 + x_inset, v - 20),
406                button_type='square',
407                color=(0.6, 0.5, 0.65),
408                autoselect=True,
409                on_activate_call=self.assign_random_name,
410            )
411
412        self._update_clipped_name()
413        self._clipped_name_timer = bui.AppTimer(
414            0.333, bui.WeakCall(self._update_clipped_name), repeat=True
415        )
416
417        v -= spacing * 3.0
418        b_size = 80
419        b_size_2 = 100
420        b_offs = 150
421        self._color_button = btn = bui.buttonwidget(
422            parent=self._root_widget,
423            autoselect=True,
424            position=(self._width * 0.5 - b_offs - b_size * 0.5, v - 50),
425            size=(b_size, b_size),
426            color=self._color,
427            label='',
428            button_type='square',
429        )
430        origin = self._color_button.get_screen_space_center()
431        bui.buttonwidget(
432            edit=self._color_button,
433            on_activate_call=bui.WeakCall(self._make_picker, 'color', origin),
434        )
435        bui.textwidget(
436            parent=self._root_widget,
437            h_align='center',
438            v_align='center',
439            position=(self._width * 0.5 - b_offs, v - 65),
440            size=(0, 0),
441            draw_controller=btn,
442            text=bui.Lstr(resource=f'{self._r}.colorText'),
443            scale=0.7,
444            color=bui.app.ui_v1.title_color,
445            maxwidth=120,
446        )
447
448        self._character_button = btn = bui.buttonwidget(
449            parent=self._root_widget,
450            autoselect=True,
451            position=(self._width * 0.5 - b_size_2 * 0.5, v - 60),
452            up_widget=self._account_type_info_button,
453            on_activate_call=self._on_character_press,
454            size=(b_size_2, b_size_2),
455            label='',
456            color=(1, 1, 1),
457            mask_texture=bui.gettexture('characterIconMask'),
458        )
459        if not self._is_account_profile and not self._global:
460            bui.containerwidget(
461                edit=self._root_widget, selected_child=self._text_field
462            )
463        bui.textwidget(
464            parent=self._root_widget,
465            h_align='center',
466            v_align='center',
467            position=(self._width * 0.5, v - 80),
468            size=(0, 0),
469            draw_controller=btn,
470            text=bui.Lstr(resource=f'{self._r}.characterText'),
471            scale=0.7,
472            color=bui.app.ui_v1.title_color,
473            maxwidth=130,
474        )
475
476        self._highlight_button = btn = bui.buttonwidget(
477            parent=self._root_widget,
478            autoselect=True,
479            position=(self._width * 0.5 + b_offs - b_size * 0.5, v - 50),
480            up_widget=(
481                self._upgrade_button
482                if self._upgrade_button is not None
483                else self._account_type_info_button
484            ),
485            size=(b_size, b_size),
486            color=self._highlight,
487            label='',
488            button_type='square',
489        )
490
491        if not self._is_account_profile and not self._global:
492            bui.widget(edit=cancel_button, down_widget=self._text_field)
493            bui.widget(edit=save_button, down_widget=self._text_field)
494            bui.widget(edit=self._color_button, up_widget=self._text_field)
495        bui.widget(
496            edit=self._account_type_info_button,
497            down_widget=self._character_button,
498        )
499
500        origin = self._highlight_button.get_screen_space_center()
501        bui.buttonwidget(
502            edit=self._highlight_button,
503            on_activate_call=bui.WeakCall(
504                self._make_picker, 'highlight', origin
505            ),
506        )
507        bui.textwidget(
508            parent=self._root_widget,
509            h_align='center',
510            v_align='center',
511            position=(self._width * 0.5 + b_offs, v - 65),
512            size=(0, 0),
513            draw_controller=btn,
514            text=bui.Lstr(resource=f'{self._r}.highlightText'),
515            scale=0.7,
516            color=bui.app.ui_v1.title_color,
517            maxwidth=120,
518        )
519        self._update_character()

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.

def reload_window(self) -> None:
19    def reload_window(self) -> None:
20        """Transitions out and recreates ourself."""
21
22        # no-op if our underlying widget is dead or on its way out.
23        if not self._root_widget or self._root_widget.transitioning_out:
24            return
25
26        bui.containerwidget(edit=self._root_widget, transition='out_left')
27        assert bui.app.classic is not None
28        bui.app.ui_v1.set_main_window(
29            EditProfileWindow(self.getname(), self._in_main_menu),
30            from_window=self,
31            is_back=True,
32        )

Transitions out and recreates ourself.

@override
def get_main_window_state(self) -> bauiv1.MainWindowState:
521    @override
522    def get_main_window_state(self) -> bui.MainWindowState:
523        # Support recreating our window for back/refresh purposes.
524        cls = type(self)
525        return bui.BasicMainWindowState(
526            create_call=lambda transition, origin_widget: cls(
527                transition=transition,
528                origin_widget=origin_widget,
529                existing_profile=self._existing_profile,
530                in_main_menu=self._in_main_menu,
531            )
532        )

Return a WindowState to recreate this window, if supported.

def assign_random_name(self) -> None:
534    def assign_random_name(self) -> None:
535        """Assigning a random name to the player."""
536        names = bs.get_random_names()
537        name = names[random.randrange(len(names))]
538        bui.textwidget(
539            edit=self._text_field,
540            text=name,
541        )

Assigning a random name to the player.

def upgrade_profile(self) -> None:
543    def upgrade_profile(self) -> None:
544        """Attempt to upgrade the profile to global."""
545        from bauiv1lib import account
546        from bauiv1lib.profile import upgrade as pupgrade
547
548        plus = bui.app.plus
549        assert plus is not None
550
551        if plus.get_v1_account_state() != 'signed_in':
552            account.show_sign_in_prompt()
553            return
554
555        pupgrade.ProfileUpgradeWindow(self)

Attempt to upgrade the profile to global.

def show_account_profile_info(self) -> None:
557    def show_account_profile_info(self) -> None:
558        """Show an explanation of account profiles."""
559        from bauiv1lib.confirm import ConfirmWindow
560
561        icons_str = ' '.join(
562            [
563                bui.charstr(n)
564                for n in [
565                    bui.SpecialChar.GOOGLE_PLAY_GAMES_LOGO,
566                    bui.SpecialChar.GAME_CENTER_LOGO,
567                    bui.SpecialChar.LOCAL_ACCOUNT,
568                    bui.SpecialChar.OCULUS_LOGO,
569                    bui.SpecialChar.NVIDIA_LOGO,
570                    bui.SpecialChar.V2_LOGO,
571                ]
572            ]
573        )
574        txtl = bui.Lstr(
575            resource='editProfileWindow.accountProfileInfoText',
576            subs=[('${ICONS}', icons_str)],
577        )
578        ConfirmWindow(
579            txtl,
580            cancel_button=False,
581            width=500,
582            height=300,
583            origin_widget=self._account_type_info_button,
584        )

Show an explanation of account profiles.

def show_local_profile_info(self) -> None:
586    def show_local_profile_info(self) -> None:
587        """Show an explanation of local profiles."""
588        from bauiv1lib.confirm import ConfirmWindow
589
590        txtl = bui.Lstr(resource='editProfileWindow.localProfileInfoText')
591        ConfirmWindow(
592            txtl,
593            cancel_button=False,
594            width=600,
595            height=250,
596            origin_widget=self._account_type_info_button,
597        )

Show an explanation of local profiles.

def show_global_profile_info(self) -> None:
599    def show_global_profile_info(self) -> None:
600        """Show an explanation of global profiles."""
601        from bauiv1lib.confirm import ConfirmWindow
602
603        txtl = bui.Lstr(resource='editProfileWindow.globalProfileInfoText')
604        ConfirmWindow(
605            txtl,
606            cancel_button=False,
607            width=600,
608            height=250,
609            origin_widget=self._account_type_info_button,
610        )

Show an explanation of global profiles.

def refresh_characters(self) -> None:
612    def refresh_characters(self) -> None:
613        """Refresh available characters/icons."""
614        from bascenev1lib.actor import spazappearance
615
616        assert bui.app.classic is not None
617
618        self._spazzes = spazappearance.get_appearances()
619        self._spazzes.sort()
620        self._icon_textures = [
621            bui.gettexture(bui.app.classic.spaz_appearances[s].icon_texture)
622            for s in self._spazzes
623        ]
624        self._icon_tint_textures = [
625            bui.gettexture(
626                bui.app.classic.spaz_appearances[s].icon_mask_texture
627            )
628            for s in self._spazzes
629        ]

Refresh available characters/icons.

def on_icon_picker_pick(self, icon: str) -> None:
631    def on_icon_picker_pick(self, icon: str) -> None:
632        """An icon has been selected by the picker."""
633        self._icon = icon
634        self._update_icon()

An icon has been selected by the picker.

def on_character_picker_pick(self, character: str) -> None:
636    def on_character_picker_pick(self, character: str) -> None:
637        """A character has been selected by the picker."""
638        if not self._root_widget:
639            return
640
641        # The player could have bought a new one while the picker was up.
642        self.refresh_characters()
643        self._icon_index = (
644            self._spazzes.index(character) if character in self._spazzes else 0
645        )
646        self._update_character()

A character has been selected by the picker.

def color_picker_closing(self, picker: bauiv1lib.colorpicker.ColorPicker) -> None:
723    def color_picker_closing(self, picker: ColorPicker) -> None:
724        """Called when a color picker is closing."""
725        if not self._root_widget:
726            return
727        tag = picker.get_tag()
728        if tag == 'color':
729            bui.containerwidget(
730                edit=self._root_widget, selected_child=self._color_button
731            )
732        elif tag == 'highlight':
733            bui.containerwidget(
734                edit=self._root_widget, selected_child=self._highlight_button
735            )
736        else:
737            print('color_picker_closing got unknown tag ' + str(tag))

Called when a color picker is closing.

def color_picker_selected_color( self, picker: bauiv1lib.colorpicker.ColorPicker, color: tuple[float, float, float]) -> None:
739    def color_picker_selected_color(
740        self, picker: ColorPicker, color: tuple[float, float, float]
741    ) -> None:
742        """Called when a color is selected in a color picker."""
743        if not self._root_widget:
744            return
745        tag = picker.get_tag()
746        if tag == 'color':
747            self._set_color(color)
748        elif tag == 'highlight':
749            self._set_highlight(color)
750        else:
751            print('color_picker_selected_color got unknown tag ' + str(tag))
752        self._update_character()

Called when a color is selected in a color picker.

def getname(self) -> str:
795    def getname(self) -> str:
796        """Return the current profile name value."""
797        if self._is_account_profile:
798            new_name = '__account__'
799        elif self._global:
800            new_name = self._name
801        else:
802            new_name = cast(str, bui.textwidget(query=self._text_field))
803        return new_name

Return the current profile name value.

def save(self, transition_out: bool = True) -> bool:
805    def save(self, transition_out: bool = True) -> bool:
806        """Save has been selected."""
807
808        # no-op if our underlying widget is dead or on its way out.
809        if not self._root_widget or self._root_widget.transitioning_out:
810            return False
811
812        plus = bui.app.plus
813        assert plus is not None
814
815        new_name = self.getname().strip()
816
817        if not new_name:
818            bui.screenmessage(bui.Lstr(resource='nameNotEmptyText'))
819            bui.getsound('error').play()
820            return False
821
822        # Make sure we're not renaming to another existing profile.
823        profiles: dict = bui.app.config.get('Player Profiles', {})
824        if self._existing_profile != new_name and new_name in profiles.keys():
825            bui.screenmessage(
826                bui.Lstr(resource='editProfileWindow.profileAlreadyExistsText')
827            )
828            bui.getsound('error').play()
829            return False
830
831        if transition_out:
832            bui.getsound('gunCocking').play()
833
834        # Delete old in case we're renaming.
835        if self._existing_profile and self._existing_profile != new_name:
836            plus.add_v1_account_transaction(
837                {
838                    'type': 'REMOVE_PLAYER_PROFILE',
839                    'name': self._existing_profile,
840                }
841            )
842
843            # Also lets be aware we're no longer global if we're taking a
844            # new name (will need to re-request it).
845            self._global = False
846
847        plus.add_v1_account_transaction(
848            {
849                'type': 'ADD_PLAYER_PROFILE',
850                'name': new_name,
851                'profile': {
852                    'character': self._spazzes[self._icon_index],
853                    'color': list(self._color),
854                    'global': self._global,
855                    'icon': self._icon,
856                    'highlight': list(self._highlight),
857                },
858            }
859        )
860
861        if transition_out:
862            plus.run_v1_account_transactions()
863            self.main_window_back()
864            # bui.containerwidget(edit=self._root_widget,
865            # transition='out_right')
866            # assert bui.app.classic is not None
867            # bui.app.ui_v1.set_main_window(
868            #     ProfileBrowserWindow(
869            #         'in_left',
870            #         selected_profile=new_name,
871            #         in_main_menu=self._in_main_menu,
872            #     ),
873            #     from_window=self,
874            #     is_back=True,
875            # )
876        return True

Save has been selected.

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