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=self._r + '.titleNewText')
114                if existing_profile is None
115                else bui.Lstr(resource=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=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=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=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=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=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=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.GAME_CIRCLE_LOGO,
549                    bui.SpecialChar.OUYA_LOGO,
550                    bui.SpecialChar.LOCAL_ACCOUNT,
551                    bui.SpecialChar.OCULUS_LOGO,
552                    bui.SpecialChar.NVIDIA_LOGO,
553                    bui.SpecialChar.V2_LOGO,
554                ]
555            ]
556        )
557        txtl = bui.Lstr(
558            resource='editProfileWindow.accountProfileInfoText',
559            subs=[('${ICONS}', icons_str)],
560        )
561        ConfirmWindow(
562            txtl,
563            cancel_button=False,
564            width=500,
565            height=300,
566            origin_widget=self._account_type_info_button,
567        )
568
569    def show_local_profile_info(self) -> None:
570        """Show an explanation of local profiles."""
571        from bauiv1lib.confirm import ConfirmWindow
572
573        txtl = bui.Lstr(resource='editProfileWindow.localProfileInfoText')
574        ConfirmWindow(
575            txtl,
576            cancel_button=False,
577            width=600,
578            height=250,
579            origin_widget=self._account_type_info_button,
580        )
581
582    def show_global_profile_info(self) -> None:
583        """Show an explanation of global profiles."""
584        from bauiv1lib.confirm import ConfirmWindow
585
586        txtl = bui.Lstr(resource='editProfileWindow.globalProfileInfoText')
587        ConfirmWindow(
588            txtl,
589            cancel_button=False,
590            width=600,
591            height=250,
592            origin_widget=self._account_type_info_button,
593        )
594
595    def refresh_characters(self) -> None:
596        """Refresh available characters/icons."""
597        from bascenev1lib.actor import spazappearance
598
599        assert bui.app.classic is not None
600
601        self._spazzes = spazappearance.get_appearances()
602        self._spazzes.sort()
603        self._icon_textures = [
604            bui.gettexture(bui.app.classic.spaz_appearances[s].icon_texture)
605            for s in self._spazzes
606        ]
607        self._icon_tint_textures = [
608            bui.gettexture(
609                bui.app.classic.spaz_appearances[s].icon_mask_texture
610            )
611            for s in self._spazzes
612        ]
613
614    def on_icon_picker_pick(self, icon: str) -> None:
615        """An icon has been selected by the picker."""
616        self._icon = icon
617        self._update_icon()
618
619    def on_character_picker_pick(self, character: str) -> None:
620        """A character has been selected by the picker."""
621        if not self._root_widget:
622            return
623
624        # The player could have bought a new one while the picker was up.
625        self.refresh_characters()
626        self._icon_index = (
627            self._spazzes.index(character) if character in self._spazzes else 0
628        )
629        self._update_character()
630
631    def _on_character_press(self) -> None:
632        from bauiv1lib import characterpicker
633
634        characterpicker.CharacterPicker(
635            parent=self._root_widget,
636            position=self._character_button.get_screen_space_center(),
637            selected_character=self._spazzes[self._icon_index],
638            delegate=self,
639            tint_color=self._color,
640            tint2_color=self._highlight,
641        )
642
643    def _on_icon_press(self) -> None:
644        from bauiv1lib import iconpicker
645
646        iconpicker.IconPicker(
647            parent=self._root_widget,
648            position=self._icon_button.get_screen_space_center(),
649            selected_icon=self._icon,
650            delegate=self,
651            tint_color=self._color,
652            tint2_color=self._highlight,
653        )
654
655    def _make_picker(
656        self, picker_type: str, origin: tuple[float, float]
657    ) -> None:
658        if picker_type == 'color':
659            initial_color = self._color
660        elif picker_type == 'highlight':
661            initial_color = self._highlight
662        else:
663            raise ValueError('invalid picker_type: ' + picker_type)
664        ColorPicker(
665            parent=self._root_widget,
666            position=origin,
667            offset=(
668                self._base_scale * (-100 if picker_type == 'color' else 100),
669                0,
670            ),
671            initial_color=initial_color,
672            delegate=self,
673            tag=picker_type,
674        )
675
676    def _cancel(self) -> None:
677        from bauiv1lib.profile.browser import ProfileBrowserWindow
678
679        # no-op if our underlying widget is dead or on its way out.
680        if not self._root_widget or self._root_widget.transitioning_out:
681            return
682
683        bui.containerwidget(edit=self._root_widget, transition='out_right')
684        assert bui.app.classic is not None
685        bui.app.ui_v1.set_main_menu_window(
686            ProfileBrowserWindow(
687                'in_left',
688                selected_profile=self._existing_profile,
689                in_main_menu=self._in_main_menu,
690            ).get_root_widget(),
691            from_window=self._root_widget,
692        )
693
694    def _set_color(self, color: tuple[float, float, float]) -> None:
695        self._color = color
696        if self._color_button:
697            bui.buttonwidget(edit=self._color_button, color=color)
698
699    def _set_highlight(self, color: tuple[float, float, float]) -> None:
700        self._highlight = color
701        if self._highlight_button:
702            bui.buttonwidget(edit=self._highlight_button, color=color)
703
704    def color_picker_closing(self, picker: ColorPicker) -> None:
705        """Called when a color picker is closing."""
706        if not self._root_widget:
707            return
708        tag = picker.get_tag()
709        if tag == 'color':
710            bui.containerwidget(
711                edit=self._root_widget, selected_child=self._color_button
712            )
713        elif tag == 'highlight':
714            bui.containerwidget(
715                edit=self._root_widget, selected_child=self._highlight_button
716            )
717        else:
718            print('color_picker_closing got unknown tag ' + str(tag))
719
720    def color_picker_selected_color(
721        self, picker: ColorPicker, color: tuple[float, float, float]
722    ) -> None:
723        """Called when a color is selected in a color picker."""
724        if not self._root_widget:
725            return
726        tag = picker.get_tag()
727        if tag == 'color':
728            self._set_color(color)
729        elif tag == 'highlight':
730            self._set_highlight(color)
731        else:
732            print('color_picker_selected_color got unknown tag ' + str(tag))
733        self._update_character()
734
735    def _update_clipped_name(self) -> None:
736        plus = bui.app.plus
737        assert plus is not None
738
739        if not self._clipped_name_text:
740            return
741        name = self.getname()
742        if name == '__account__':
743            name = (
744                plus.get_v1_account_name()
745                if plus.get_v1_account_state() == 'signed_in'
746                else '???'
747            )
748        if len(name) > 10 and not (self._global or self._is_account_profile):
749            name = name.strip()
750            display_name = (name[:10] + '...') if len(name) > 10 else name
751            bui.textwidget(
752                edit=self._clipped_name_text,
753                text=bui.Lstr(
754                    resource='inGameClippedNameText',
755                    subs=[('${NAME}', display_name)],
756                ),
757            )
758        else:
759            bui.textwidget(edit=self._clipped_name_text, text='')
760
761    def _update_character(self, change: int = 0) -> None:
762        self._icon_index = (self._icon_index + change) % len(self._spazzes)
763        if self._character_button:
764            bui.buttonwidget(
765                edit=self._character_button,
766                texture=self._icon_textures[self._icon_index],
767                tint_texture=self._icon_tint_textures[self._icon_index],
768                tint_color=self._color,
769                tint2_color=self._highlight,
770            )
771
772    def _update_icon(self) -> None:
773        if self._icon_button_label:
774            bui.textwidget(edit=self._icon_button_label, text=self._icon)
775
776    def getname(self) -> str:
777        """Return the current profile name value."""
778        if self._is_account_profile:
779            new_name = '__account__'
780        elif self._global:
781            new_name = self._name
782        else:
783            new_name = cast(str, bui.textwidget(query=self._text_field))
784        return new_name
785
786    def save(self, transition_out: bool = True) -> bool:
787        """Save has been selected."""
788        from bauiv1lib.profile.browser import ProfileBrowserWindow
789
790        # no-op if our underlying widget is dead or on its way out.
791        if not self._root_widget or self._root_widget.transitioning_out:
792            return False
793
794        plus = bui.app.plus
795        assert plus is not None
796
797        new_name = self.getname().strip()
798
799        if not new_name:
800            bui.screenmessage(bui.Lstr(resource='nameNotEmptyText'))
801            bui.getsound('error').play()
802            return False
803
804        # Make sure we're not renaming to another existing profile.
805        profiles: dict = bui.app.config.get('Player Profiles', {})
806        if self._existing_profile != new_name and new_name in profiles.keys():
807            bui.screenmessage(
808                bui.Lstr(resource='editProfileWindow.profileAlreadyExistsText')
809            )
810            bui.getsound('error').play()
811            return False
812
813        if transition_out:
814            bui.getsound('gunCocking').play()
815
816        # Delete old in case we're renaming.
817        if self._existing_profile and self._existing_profile != new_name:
818            plus.add_v1_account_transaction(
819                {
820                    'type': 'REMOVE_PLAYER_PROFILE',
821                    'name': self._existing_profile,
822                }
823            )
824
825            # Also lets be aware we're no longer global if we're taking a
826            # new name (will need to re-request it).
827            self._global = False
828
829        plus.add_v1_account_transaction(
830            {
831                'type': 'ADD_PLAYER_PROFILE',
832                'name': new_name,
833                'profile': {
834                    'character': self._spazzes[self._icon_index],
835                    'color': list(self._color),
836                    'global': self._global,
837                    'icon': self._icon,
838                    'highlight': list(self._highlight),
839                },
840            }
841        )
842
843        if transition_out:
844            plus.run_v1_account_transactions()
845            bui.containerwidget(edit=self._root_widget, transition='out_right')
846            assert bui.app.classic is not None
847            bui.app.ui_v1.set_main_menu_window(
848                ProfileBrowserWindow(
849                    'in_left',
850                    selected_profile=new_name,
851                    in_main_menu=self._in_main_menu,
852                ).get_root_widget(),
853                from_window=self._root_widget,
854            )
855        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=self._r + '.titleNewText')
115                if existing_profile is None
116                else bui.Lstr(resource=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=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=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=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=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=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=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.GAME_CIRCLE_LOGO,
550                    bui.SpecialChar.OUYA_LOGO,
551                    bui.SpecialChar.LOCAL_ACCOUNT,
552                    bui.SpecialChar.OCULUS_LOGO,
553                    bui.SpecialChar.NVIDIA_LOGO,
554                    bui.SpecialChar.V2_LOGO,
555                ]
556            ]
557        )
558        txtl = bui.Lstr(
559            resource='editProfileWindow.accountProfileInfoText',
560            subs=[('${ICONS}', icons_str)],
561        )
562        ConfirmWindow(
563            txtl,
564            cancel_button=False,
565            width=500,
566            height=300,
567            origin_widget=self._account_type_info_button,
568        )
569
570    def show_local_profile_info(self) -> None:
571        """Show an explanation of local profiles."""
572        from bauiv1lib.confirm import ConfirmWindow
573
574        txtl = bui.Lstr(resource='editProfileWindow.localProfileInfoText')
575        ConfirmWindow(
576            txtl,
577            cancel_button=False,
578            width=600,
579            height=250,
580            origin_widget=self._account_type_info_button,
581        )
582
583    def show_global_profile_info(self) -> None:
584        """Show an explanation of global profiles."""
585        from bauiv1lib.confirm import ConfirmWindow
586
587        txtl = bui.Lstr(resource='editProfileWindow.globalProfileInfoText')
588        ConfirmWindow(
589            txtl,
590            cancel_button=False,
591            width=600,
592            height=250,
593            origin_widget=self._account_type_info_button,
594        )
595
596    def refresh_characters(self) -> None:
597        """Refresh available characters/icons."""
598        from bascenev1lib.actor import spazappearance
599
600        assert bui.app.classic is not None
601
602        self._spazzes = spazappearance.get_appearances()
603        self._spazzes.sort()
604        self._icon_textures = [
605            bui.gettexture(bui.app.classic.spaz_appearances[s].icon_texture)
606            for s in self._spazzes
607        ]
608        self._icon_tint_textures = [
609            bui.gettexture(
610                bui.app.classic.spaz_appearances[s].icon_mask_texture
611            )
612            for s in self._spazzes
613        ]
614
615    def on_icon_picker_pick(self, icon: str) -> None:
616        """An icon has been selected by the picker."""
617        self._icon = icon
618        self._update_icon()
619
620    def on_character_picker_pick(self, character: str) -> None:
621        """A character has been selected by the picker."""
622        if not self._root_widget:
623            return
624
625        # The player could have bought a new one while the picker was up.
626        self.refresh_characters()
627        self._icon_index = (
628            self._spazzes.index(character) if character in self._spazzes else 0
629        )
630        self._update_character()
631
632    def _on_character_press(self) -> None:
633        from bauiv1lib import characterpicker
634
635        characterpicker.CharacterPicker(
636            parent=self._root_widget,
637            position=self._character_button.get_screen_space_center(),
638            selected_character=self._spazzes[self._icon_index],
639            delegate=self,
640            tint_color=self._color,
641            tint2_color=self._highlight,
642        )
643
644    def _on_icon_press(self) -> None:
645        from bauiv1lib import iconpicker
646
647        iconpicker.IconPicker(
648            parent=self._root_widget,
649            position=self._icon_button.get_screen_space_center(),
650            selected_icon=self._icon,
651            delegate=self,
652            tint_color=self._color,
653            tint2_color=self._highlight,
654        )
655
656    def _make_picker(
657        self, picker_type: str, origin: tuple[float, float]
658    ) -> None:
659        if picker_type == 'color':
660            initial_color = self._color
661        elif picker_type == 'highlight':
662            initial_color = self._highlight
663        else:
664            raise ValueError('invalid picker_type: ' + picker_type)
665        ColorPicker(
666            parent=self._root_widget,
667            position=origin,
668            offset=(
669                self._base_scale * (-100 if picker_type == 'color' else 100),
670                0,
671            ),
672            initial_color=initial_color,
673            delegate=self,
674            tag=picker_type,
675        )
676
677    def _cancel(self) -> None:
678        from bauiv1lib.profile.browser import ProfileBrowserWindow
679
680        # no-op if our underlying widget is dead or on its way out.
681        if not self._root_widget or self._root_widget.transitioning_out:
682            return
683
684        bui.containerwidget(edit=self._root_widget, transition='out_right')
685        assert bui.app.classic is not None
686        bui.app.ui_v1.set_main_menu_window(
687            ProfileBrowserWindow(
688                'in_left',
689                selected_profile=self._existing_profile,
690                in_main_menu=self._in_main_menu,
691            ).get_root_widget(),
692            from_window=self._root_widget,
693        )
694
695    def _set_color(self, color: tuple[float, float, float]) -> None:
696        self._color = color
697        if self._color_button:
698            bui.buttonwidget(edit=self._color_button, color=color)
699
700    def _set_highlight(self, color: tuple[float, float, float]) -> None:
701        self._highlight = color
702        if self._highlight_button:
703            bui.buttonwidget(edit=self._highlight_button, color=color)
704
705    def color_picker_closing(self, picker: ColorPicker) -> None:
706        """Called when a color picker is closing."""
707        if not self._root_widget:
708            return
709        tag = picker.get_tag()
710        if tag == 'color':
711            bui.containerwidget(
712                edit=self._root_widget, selected_child=self._color_button
713            )
714        elif tag == 'highlight':
715            bui.containerwidget(
716                edit=self._root_widget, selected_child=self._highlight_button
717            )
718        else:
719            print('color_picker_closing got unknown tag ' + str(tag))
720
721    def color_picker_selected_color(
722        self, picker: ColorPicker, color: tuple[float, float, float]
723    ) -> None:
724        """Called when a color is selected in a color picker."""
725        if not self._root_widget:
726            return
727        tag = picker.get_tag()
728        if tag == 'color':
729            self._set_color(color)
730        elif tag == 'highlight':
731            self._set_highlight(color)
732        else:
733            print('color_picker_selected_color got unknown tag ' + str(tag))
734        self._update_character()
735
736    def _update_clipped_name(self) -> None:
737        plus = bui.app.plus
738        assert plus is not None
739
740        if not self._clipped_name_text:
741            return
742        name = self.getname()
743        if name == '__account__':
744            name = (
745                plus.get_v1_account_name()
746                if plus.get_v1_account_state() == 'signed_in'
747                else '???'
748            )
749        if len(name) > 10 and not (self._global or self._is_account_profile):
750            name = name.strip()
751            display_name = (name[:10] + '...') if len(name) > 10 else name
752            bui.textwidget(
753                edit=self._clipped_name_text,
754                text=bui.Lstr(
755                    resource='inGameClippedNameText',
756                    subs=[('${NAME}', display_name)],
757                ),
758            )
759        else:
760            bui.textwidget(edit=self._clipped_name_text, text='')
761
762    def _update_character(self, change: int = 0) -> None:
763        self._icon_index = (self._icon_index + change) % len(self._spazzes)
764        if self._character_button:
765            bui.buttonwidget(
766                edit=self._character_button,
767                texture=self._icon_textures[self._icon_index],
768                tint_texture=self._icon_tint_textures[self._icon_index],
769                tint_color=self._color,
770                tint2_color=self._highlight,
771            )
772
773    def _update_icon(self) -> None:
774        if self._icon_button_label:
775            bui.textwidget(edit=self._icon_button_label, text=self._icon)
776
777    def getname(self) -> str:
778        """Return the current profile name value."""
779        if self._is_account_profile:
780            new_name = '__account__'
781        elif self._global:
782            new_name = self._name
783        else:
784            new_name = cast(str, bui.textwidget(query=self._text_field))
785        return new_name
786
787    def save(self, transition_out: bool = True) -> bool:
788        """Save has been selected."""
789        from bauiv1lib.profile.browser import ProfileBrowserWindow
790
791        # no-op if our underlying widget is dead or on its way out.
792        if not self._root_widget or self._root_widget.transitioning_out:
793            return False
794
795        plus = bui.app.plus
796        assert plus is not None
797
798        new_name = self.getname().strip()
799
800        if not new_name:
801            bui.screenmessage(bui.Lstr(resource='nameNotEmptyText'))
802            bui.getsound('error').play()
803            return False
804
805        # Make sure we're not renaming to another existing profile.
806        profiles: dict = bui.app.config.get('Player Profiles', {})
807        if self._existing_profile != new_name and new_name in profiles.keys():
808            bui.screenmessage(
809                bui.Lstr(resource='editProfileWindow.profileAlreadyExistsText')
810            )
811            bui.getsound('error').play()
812            return False
813
814        if transition_out:
815            bui.getsound('gunCocking').play()
816
817        # Delete old in case we're renaming.
818        if self._existing_profile and self._existing_profile != new_name:
819            plus.add_v1_account_transaction(
820                {
821                    'type': 'REMOVE_PLAYER_PROFILE',
822                    'name': self._existing_profile,
823                }
824            )
825
826            # Also lets be aware we're no longer global if we're taking a
827            # new name (will need to re-request it).
828            self._global = False
829
830        plus.add_v1_account_transaction(
831            {
832                'type': 'ADD_PLAYER_PROFILE',
833                'name': new_name,
834                'profile': {
835                    'character': self._spazzes[self._icon_index],
836                    'color': list(self._color),
837                    'global': self._global,
838                    'icon': self._icon,
839                    'highlight': list(self._highlight),
840                },
841            }
842        )
843
844        if transition_out:
845            plus.run_v1_account_transactions()
846            bui.containerwidget(edit=self._root_widget, transition='out_right')
847            assert bui.app.classic is not None
848            bui.app.ui_v1.set_main_menu_window(
849                ProfileBrowserWindow(
850                    'in_left',
851                    selected_profile=new_name,
852                    in_main_menu=self._in_main_menu,
853                ).get_root_widget(),
854                from_window=self._root_widget,
855            )
856        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=self._r + '.titleNewText')
115                if existing_profile is None
116                else bui.Lstr(resource=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=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=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=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=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=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=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.GAME_CIRCLE_LOGO,
550                    bui.SpecialChar.OUYA_LOGO,
551                    bui.SpecialChar.LOCAL_ACCOUNT,
552                    bui.SpecialChar.OCULUS_LOGO,
553                    bui.SpecialChar.NVIDIA_LOGO,
554                    bui.SpecialChar.V2_LOGO,
555                ]
556            ]
557        )
558        txtl = bui.Lstr(
559            resource='editProfileWindow.accountProfileInfoText',
560            subs=[('${ICONS}', icons_str)],
561        )
562        ConfirmWindow(
563            txtl,
564            cancel_button=False,
565            width=500,
566            height=300,
567            origin_widget=self._account_type_info_button,
568        )

Show an explanation of account profiles.

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

Show an explanation of local profiles.

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

Show an explanation of global profiles.

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

Refresh available characters/icons.

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

An icon has been selected by the picker.

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

A character has been selected by the picker.

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

Called when a color is selected in a color picker.

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

Return the current profile name value.

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

Save has been selected.

Inherited Members
bauiv1._uitypes.Window
get_root_widget