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

Assigning a random name to the player.

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

Attempt to upgrade the profile to global.

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

Show an explanation of account profiles.

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

Show an explanation of local profiles.

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

Show an explanation of global profiles.

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

Refresh available characters/icons.

def on_icon_picker_pick(self, icon: str) -> None:
613    def on_icon_picker_pick(self, icon: str) -> None:
614        """An icon has been selected by the picker."""
615        self._icon = icon
616        self._update_icon()

An icon has been selected by the picker.

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

A character has been selected by the picker.

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

Called when a color is selected in a color picker.

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

Return the current profile name value.

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

Save has been selected.

Inherited Members
bauiv1._uitypes.Window
get_root_widget