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

Window for editing a player profile.

EditProfileWindow( existing_profile: str | None, in_main_menu: bool, transition: str = 'in_right')
 36    def __init__(
 37        self,
 38        existing_profile: str | None,
 39        in_main_menu: bool,
 40        transition: str = 'in_right',
 41    ):
 42        # FIXME: Tidy this up a bit.
 43        # pylint: disable=too-many-branches
 44        # pylint: disable=too-many-statements
 45        # pylint: disable=too-many-locals
 46        assert bui.app.classic is not None
 47
 48        plus = bui.app.plus
 49        assert plus is not None
 50
 51        self._in_main_menu = in_main_menu
 52        self._existing_profile = existing_profile
 53        self._r = 'editProfileWindow'
 54        self._spazzes: list[str] = []
 55        self._icon_textures: list[bui.Texture] = []
 56        self._icon_tint_textures: list[bui.Texture] = []
 57
 58        # Grab profile colors or pick random ones.
 59        (
 60            self._color,
 61            self._highlight,
 62        ) = bui.app.classic.get_player_profile_colors(existing_profile)
 63        uiscale = bui.app.ui_v1.uiscale
 64        self._width = width = 880.0 if uiscale is bui.UIScale.SMALL else 680.0
 65        self._x_inset = x_inset = 100.0 if uiscale is bui.UIScale.SMALL else 0.0
 66        self._height = height = (
 67            350.0
 68            if uiscale is bui.UIScale.SMALL
 69            else 400.0
 70            if uiscale is bui.UIScale.MEDIUM
 71            else 450.0
 72        )
 73        spacing = 40
 74        self._base_scale = (
 75            2.05
 76            if uiscale is bui.UIScale.SMALL
 77            else 1.5
 78            if uiscale is bui.UIScale.MEDIUM
 79            else 1.0
 80        )
 81        top_extra = 15 if uiscale is bui.UIScale.SMALL else 15
 82        super().__init__(
 83            root_widget=bui.containerwidget(
 84                size=(width, height + top_extra),
 85                transition=transition,
 86                scale=self._base_scale,
 87                stack_offset=(0, 15)
 88                if uiscale is bui.UIScale.SMALL
 89                else (0, 0),
 90            )
 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=self._r + '.titleNewText')
119                if existing_profile is None
120                else bui.Lstr(resource=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=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=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=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=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=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=self._upgrade_button
480            if self._upgrade_button is not None
481            else self._account_type_info_button,
482            size=(b_size, b_size),
483            color=self._highlight,
484            label='',
485            button_type='square',
486        )
487
488        if not self._is_account_profile and not self._global:
489            bui.widget(edit=cancel_button, down_widget=self._text_field)
490            bui.widget(edit=save_button, down_widget=self._text_field)
491            bui.widget(edit=self._color_button, up_widget=self._text_field)
492        bui.widget(
493            edit=self._account_type_info_button,
494            down_widget=self._character_button,
495        )
496
497        origin = self._highlight_button.get_screen_space_center()
498        bui.buttonwidget(
499            edit=self._highlight_button,
500            on_activate_call=bui.WeakCall(
501                self._make_picker, 'highlight', origin
502            ),
503        )
504        bui.textwidget(
505            parent=self._root_widget,
506            h_align='center',
507            v_align='center',
508            position=(self._width * 0.5 + b_offs, v - 65),
509            size=(0, 0),
510            draw_controller=btn,
511            text=bui.Lstr(resource=self._r + '.highlightText'),
512            scale=0.7,
513            color=bui.app.ui_v1.title_color,
514            maxwidth=120,
515        )
516        self._update_character()
def reload_window(self) -> None:
20    def reload_window(self) -> None:
21        """Transitions out and recreates ourself."""
22
23        # no-op if our underlying widget is dead or on its way out.
24        if not self._root_widget or self._root_widget.transitioning_out:
25            return
26
27        bui.containerwidget(edit=self._root_widget, transition='out_left')
28        assert bui.app.classic is not None
29        bui.app.ui_v1.set_main_menu_window(
30            EditProfileWindow(
31                self.getname(), self._in_main_menu
32            ).get_root_widget(),
33            from_window=self._root_widget,
34        )

Transitions out and recreates ourself.

def assign_random_name(self) -> None:
518    def assign_random_name(self) -> None:
519        """Assigning a random name to the player."""
520        names = bs.get_random_names()
521        name = names[random.randrange(len(names))]
522        bui.textwidget(
523            edit=self._text_field,
524            text=name,
525        )

Assigning a random name to the player.

def upgrade_profile(self) -> None:
527    def upgrade_profile(self) -> None:
528        """Attempt to upgrade the profile to global."""
529        from bauiv1lib import account
530        from bauiv1lib.profile import upgrade as pupgrade
531
532        plus = bui.app.plus
533        assert plus is not None
534
535        if plus.get_v1_account_state() != 'signed_in':
536            account.show_sign_in_prompt()
537            return
538
539        pupgrade.ProfileUpgradeWindow(self)

Attempt to upgrade the profile to global.

def show_account_profile_info(self) -> None:
541    def show_account_profile_info(self) -> None:
542        """Show an explanation of account profiles."""
543        from bauiv1lib.confirm import ConfirmWindow
544
545        icons_str = ' '.join(
546            [
547                bui.charstr(n)
548                for n in [
549                    bui.SpecialChar.GOOGLE_PLAY_GAMES_LOGO,
550                    bui.SpecialChar.GAME_CENTER_LOGO,
551                    bui.SpecialChar.GAME_CIRCLE_LOGO,
552                    bui.SpecialChar.OUYA_LOGO,
553                    bui.SpecialChar.LOCAL_ACCOUNT,
554                    bui.SpecialChar.OCULUS_LOGO,
555                    bui.SpecialChar.NVIDIA_LOGO,
556                    bui.SpecialChar.V2_LOGO,
557                ]
558            ]
559        )
560        txtl = bui.Lstr(
561            resource='editProfileWindow.accountProfileInfoText',
562            subs=[('${ICONS}', icons_str)],
563        )
564        ConfirmWindow(
565            txtl,
566            cancel_button=False,
567            width=500,
568            height=300,
569            origin_widget=self._account_type_info_button,
570        )

Show an explanation of account profiles.

def show_local_profile_info(self) -> None:
572    def show_local_profile_info(self) -> None:
573        """Show an explanation of local profiles."""
574        from bauiv1lib.confirm import ConfirmWindow
575
576        txtl = bui.Lstr(resource='editProfileWindow.localProfileInfoText')
577        ConfirmWindow(
578            txtl,
579            cancel_button=False,
580            width=600,
581            height=250,
582            origin_widget=self._account_type_info_button,
583        )

Show an explanation of local profiles.

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

Show an explanation of global profiles.

def refresh_characters(self) -> None:
598    def refresh_characters(self) -> None:
599        """Refresh available characters/icons."""
600        from bascenev1lib.actor import spazappearance
601
602        assert bui.app.classic is not None
603
604        self._spazzes = spazappearance.get_appearances()
605        self._spazzes.sort()
606        self._icon_textures = [
607            bui.gettexture(bui.app.classic.spaz_appearances[s].icon_texture)
608            for s in self._spazzes
609        ]
610        self._icon_tint_textures = [
611            bui.gettexture(
612                bui.app.classic.spaz_appearances[s].icon_mask_texture
613            )
614            for s in self._spazzes
615        ]

Refresh available characters/icons.

def on_icon_picker_pick(self, icon: str) -> None:
617    def on_icon_picker_pick(self, icon: str) -> None:
618        """An icon has been selected by the picker."""
619        self._icon = icon
620        self._update_icon()

An icon has been selected by the picker.

def on_character_picker_pick(self, character: str) -> None:
622    def on_character_picker_pick(self, character: str) -> None:
623        """A character has been selected by the picker."""
624        if not self._root_widget:
625            return
626
627        # The player could have bought a new one while the picker was up.
628        self.refresh_characters()
629        self._icon_index = (
630            self._spazzes.index(character) if character in self._spazzes else 0
631        )
632        self._update_character()

A character has been selected by the picker.

def color_picker_closing(self, picker: bauiv1lib.colorpicker.ColorPicker) -> None:
707    def color_picker_closing(self, picker: ColorPicker) -> None:
708        """Called when a color picker is closing."""
709        if not self._root_widget:
710            return
711        tag = picker.get_tag()
712        if tag == 'color':
713            bui.containerwidget(
714                edit=self._root_widget, selected_child=self._color_button
715            )
716        elif tag == 'highlight':
717            bui.containerwidget(
718                edit=self._root_widget, selected_child=self._highlight_button
719            )
720        else:
721            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:
723    def color_picker_selected_color(
724        self, picker: ColorPicker, color: tuple[float, float, float]
725    ) -> None:
726        """Called when a color is selected in a color picker."""
727        if not self._root_widget:
728            return
729        tag = picker.get_tag()
730        if tag == 'color':
731            self._set_color(color)
732        elif tag == 'highlight':
733            self._set_highlight(color)
734        else:
735            print('color_picker_selected_color got unknown tag ' + str(tag))
736        self._update_character()

Called when a color is selected in a color picker.

def getname(self) -> str:
779    def getname(self) -> str:
780        """Return the current profile name value."""
781        if self._is_account_profile:
782            new_name = '__account__'
783        elif self._global:
784            new_name = self._name
785        else:
786            new_name = cast(str, bui.textwidget(query=self._text_field))
787        return new_name

Return the current profile name value.

def save(self, transition_out: bool = True) -> bool:
789    def save(self, transition_out: bool = True) -> bool:
790        """Save has been selected."""
791        from bauiv1lib.profile.browser import ProfileBrowserWindow
792
793        # no-op if our underlying widget is dead or on its way out.
794        if not self._root_widget or self._root_widget.transitioning_out:
795            return False
796
797        plus = bui.app.plus
798        assert plus is not None
799
800        new_name = self.getname().strip()
801
802        if not new_name:
803            bui.screenmessage(bui.Lstr(resource='nameNotEmptyText'))
804            bui.getsound('error').play()
805            return False
806
807        if transition_out:
808            bui.getsound('gunCocking').play()
809
810        # Delete old in case we're renaming.
811        if self._existing_profile and self._existing_profile != new_name:
812            plus.add_v1_account_transaction(
813                {
814                    'type': 'REMOVE_PLAYER_PROFILE',
815                    'name': self._existing_profile,
816                }
817            )
818
819            # Also lets be aware we're no longer global if we're taking a
820            # new name (will need to re-request it).
821            self._global = False
822
823        plus.add_v1_account_transaction(
824            {
825                'type': 'ADD_PLAYER_PROFILE',
826                'name': new_name,
827                'profile': {
828                    'character': self._spazzes[self._icon_index],
829                    'color': list(self._color),
830                    'global': self._global,
831                    'icon': self._icon,
832                    'highlight': list(self._highlight),
833                },
834            }
835        )
836
837        if transition_out:
838            plus.run_v1_account_transactions()
839            bui.containerwidget(edit=self._root_widget, transition='out_right')
840            assert bui.app.classic is not None
841            bui.app.ui_v1.set_main_menu_window(
842                ProfileBrowserWindow(
843                    'in_left',
844                    selected_profile=new_name,
845                    in_main_menu=self._in_main_menu,
846                ).get_root_widget(),
847                from_window=self._root_widget,
848            )
849        return True

Save has been selected.

Inherited Members
bauiv1._uitypes.Window
get_root_widget