def _switch_to_score_screen(self, results: ba.GameResults) -> None:
        # pylint: disable=cyclic-import
        from efro.util import asserttype
        from bastd.activity.drawscore import DrawScoreScreenActivity
        from bastd.activity.multiteamvictory import (
            TeamSeriesVictoryScoreScreenActivity)
        from bastd.activity.freeforallvictory import (
            FreeForAllVictoryScoreScreenActivity)
        winners = results.winnergroups

        # If there's multiple players and everyone has the same score,
        # call it a draw.
        if len(self.sessionplayers) > 1 and len(winners) < 2:
            self.setactivity(
                _ba.newactivity(DrawScoreScreenActivity, {'results': results}))
        else:
            # Award different point amounts based on number of players.
            point_awards = self.get_ffa_point_awards()

            for i, winner in enumerate(winners):
                for team in winner.teams:
                    points = (point_awards[i] if i in point_awards else 0)
                    team.customdata['previous_score'] = (
                        team.customdata['score'])
                    team.customdata['score'] += points

            series_winners = [
                team for team in self.sessionteams
                if team.customdata['score'] >= self._ffa_series_length
            ]
            series_winners.sort(
                reverse=True,
                key=lambda t: asserttype(t.customdata['score'], int))
            if (len(series_winners) == 1
                    or (len(series_winners) > 1
                        and series_winners[0].customdata['score'] !=
                        series_winners[1].customdata['score'])):
                self.setactivity(
                    _ba.newactivity(TeamSeriesVictoryScoreScreenActivity,
                                    {'winner': series_winners[0]}))
            else:
                self.setactivity(
                    _ba.newactivity(FreeForAllVictoryScoreScreenActivity,
                                    {'results': results}))
    def winnergroups(self) -> list[WinnerGroup]:
        """Get an ordered list of winner groups."""
        if not self._game_set:
            raise RuntimeError("Can't get winners until game is set.")

        # Group by best scoring teams.
        winners: dict[int, list[ba.SessionTeam]] = {}
        scores = [
            score for score in self._scores.values()
            if score[0]() is not None and score[1] is not None
        ]
        for score in scores:
            assert score[1] is not None
            sval = winners.setdefault(score[1], [])
            team = score[0]()
            assert team is not None
            sval.append(team)
        results: list[tuple[Optional[int],
                            list[ba.SessionTeam]]] = list(winners.items())
        results.sort(reverse=not self._lower_is_better,
                     key=lambda x: asserttype(x[0], int))

        # Also group the 'None' scores.
        none_sessionteams: list[ba.SessionTeam] = []
        for score in self._scores.values():
            scoreteam = score[0]()
            if scoreteam is not None and score[1] is None:
                none_sessionteams.append(scoreteam)

        # Add the Nones to the list (either as winners or losers
        # depending on the rules).
        if none_sessionteams:
            nones: list[tuple[Optional[int], list[ba.SessionTeam]]] = [
                (None, none_sessionteams)
            ]
            if self._none_is_winner:
                results = nones + results
            else:
                results = results + nones

        return [WinnerGroup(score, team) for score, team in results]
Exemple #3
0
    def _show_standard_scores_to_beat_ui(self,
                                         scores: List[Dict[str, Any]]) -> None:
        from efro.util import asserttype
        from ba._gameutils import timestring, animate
        from ba._nodeactor import NodeActor
        from ba._enums import TimeFormat
        display_type = self.get_score_type()
        if scores is not None:

            # Sort by originating date so that the most recent is first.
            scores.sort(reverse=True, key=lambda s: asserttype(s['time'], int))

            # Now make a display for the most recent challenge.
            for score in scores:
                if score['type'] == 'score_challenge':
                    tval = (score['player'] + ':  ' + timestring(
                        int(score['value']) * 10,
                        timeformat=TimeFormat.MILLISECONDS).evaluate()
                            if display_type == 'time' else str(score['value']))
                    hattach = 'center' if display_type == 'time' else 'left'
                    halign = 'center' if display_type == 'time' else 'left'
                    pos = (20, -70) if display_type == 'time' else (20, -130)
                    txt = NodeActor(
                        _ba.newnode('text',
                                    attrs={
                                        'v_attach': 'top',
                                        'h_attach': hattach,
                                        'h_align': halign,
                                        'color': (0.7, 0.4, 1, 1),
                                        'shadow': 0.5,
                                        'flatness': 1.0,
                                        'position': pos,
                                        'scale': 0.6,
                                        'text': tval
                                    })).autoretain()
                    assert txt.node is not None
                    animate(txt.node, 'scale', {1.0: 0.0, 1.1: 0.7, 1.2: 0.6})
                    break
Exemple #4
0
    def _refresh(self) -> None:
        # pylint: disable=too-many-locals
        from efro.util import asserttype
        from ba.internal import (PlayerProfilesChangedMessage,
                                 get_player_profile_colors,
                                 get_player_profile_icon)
        old_selection = self._selected_profile

        # Delete old.
        while self._profile_widgets:
            self._profile_widgets.pop().delete()
        self._profiles = ba.app.config.get('Player Profiles', {})
        assert self._profiles is not None
        items = list(self._profiles.items())
        items.sort(key=lambda x: asserttype(x[0], str).lower())
        index = 0
        account_name: Optional[str]
        if _ba.get_account_state() == 'signed_in':
            account_name = _ba.get_account_display_string()
        else:
            account_name = None
        widget_to_select = None
        for p_name, _ in items:
            if p_name == '__account__' and account_name is None:
                continue
            color, _highlight = get_player_profile_colors(p_name)
            scl = 1.1
            tval = (account_name if p_name == '__account__' else
                    get_player_profile_icon(p_name) + p_name)
            assert isinstance(tval, str)
            txtw = ba.textwidget(
                parent=self._columnwidget,
                position=(0, 32),
                size=((self._width - 40) / scl, 28),
                text=ba.Lstr(value=tval),
                h_align='left',
                v_align='center',
                on_select_call=ba.WeakCall(self._select, p_name, index),
                maxwidth=self._scroll_width * 0.92,
                corner_scale=scl,
                color=ba.safecolor(color, 0.4),
                always_highlight=True,
                on_activate_call=ba.Call(self._edit_button.activate),
                selectable=True)
            if index == 0:
                ba.widget(edit=txtw, up_widget=self._back_button)
            ba.widget(edit=txtw, show_buffer_top=40, show_buffer_bottom=40)
            self._profile_widgets.append(txtw)

            # Select/show this one if it was previously selected
            # (but defer till after this loop since our height is
            # still changing).
            if p_name == old_selection:
                widget_to_select = txtw

            index += 1

        if widget_to_select is not None:
            ba.columnwidget(edit=self._columnwidget,
                            selected_child=widget_to_select,
                            visible_child=widget_to_select)

        # If there's a team-chooser in existence, tell it the profile-list
        # has probably changed.
        session = _ba.get_foreground_host_session()
        if session is not None:
            session.handlemessage(PlayerProfilesChangedMessage())
Exemple #5
0
    def _refresh(self, select_soundtrack: str = None) -> None:
        from efro.util import asserttype
        self._allow_changing_soundtracks = False
        old_selection = self._selected_soundtrack

        # If there was no prev selection, look in prefs.
        if old_selection is None:
            old_selection = ba.app.config.get('Soundtrack')
        old_selection_index = self._selected_soundtrack_index

        # Delete old.
        while self._soundtrack_widgets:
            self._soundtrack_widgets.pop().delete()

        self._soundtracks = ba.app.config.get('Soundtracks', {})
        assert self._soundtracks is not None
        items = list(self._soundtracks.items())
        items.sort(key=lambda x: asserttype(x[0], str).lower())
        items = [('__default__', None)] + items  # default is always first
        index = 0
        for pname, _pval in items:
            assert pname is not None
            txtw = ba.textwidget(
                parent=self._col,
                size=(self._width - 40, 24),
                text=self._get_soundtrack_display_name(pname),
                h_align='left',
                v_align='center',
                maxwidth=self._width - 110,
                always_highlight=True,
                on_select_call=ba.WeakCall(self._select, pname, index),
                on_activate_call=self._edit_soundtrack_with_sound,
                selectable=True)
            if index == 0:
                ba.widget(edit=txtw, up_widget=self._back_button)
            self._soundtrack_widgets.append(txtw)

            # Select this one if the user requested it
            if select_soundtrack is not None:
                if pname == select_soundtrack:
                    ba.columnwidget(edit=self._col,
                                    selected_child=txtw,
                                    visible_child=txtw)
            else:
                # Select this one if it was previously selected.
                # Go by index if there's one.
                if old_selection_index is not None:
                    if index == old_selection_index:
                        ba.columnwidget(edit=self._col,
                                        selected_child=txtw,
                                        visible_child=txtw)
                else:  # Otherwise look by name.
                    if pname == old_selection:
                        ba.columnwidget(edit=self._col,
                                        selected_child=txtw,
                                        visible_child=txtw)
            index += 1

        # Explicitly run select callback on current one and re-enable
        # callbacks.

        # Eww need to run this in a timer so it happens after our select
        # callbacks. With a small-enough time sometimes it happens before
        # anyway. Ew. need a way to just schedule a callable i guess.
        ba.timer(0.1,
                 ba.WeakCall(self._set_allow_changing),
                 timetype=ba.TimeType.REAL)
    def _refresh(self, select_playlist: str = None) -> None:
        from efro.util import asserttype
        old_selection = self._selected_playlist_name

        # If there was no prev selection, look in prefs.
        if old_selection is None:
            old_selection = ba.app.config.get(self._pvars.config_name +
                                              ' Playlist Selection')

        old_selection_index = self._selected_playlist_index

        # Delete old.
        while self._playlist_widgets:
            self._playlist_widgets.pop().delete()

        items = list(ba.app.config[self._config_name_full].items())

        # Make sure everything is unicode now.
        items = [(i[0].decode(), i[1]) if not isinstance(i[0], str) else i
                 for i in items]

        items.sort(key=lambda x: asserttype(x[0], str).lower())

        items = [['__default__', None]] + items  # Default is always first.
        index = 0
        for pname, _ in items:
            assert pname is not None
            txtw = ba.textwidget(
                parent=self._columnwidget,
                size=(self._width - 40, 30),
                maxwidth=self._width - 110,
                text=self._get_playlist_display_name(pname),
                h_align='left',
                v_align='center',
                color=(0.6, 0.6, 0.7, 1.0) if pname == '__default__' else
                (0.85, 0.85, 0.85, 1),
                always_highlight=True,
                on_select_call=ba.Call(self._select, pname, index),
                on_activate_call=ba.Call(self._edit_button.activate),
                selectable=True)
            ba.widget(edit=txtw, show_buffer_top=50, show_buffer_bottom=50)

            # Hitting up from top widget should jump to 'back'
            if index == 0:
                ba.widget(edit=txtw, up_widget=self._back_button)

            self._playlist_widgets.append(txtw)

            # Select this one if the user requested it.
            if select_playlist is not None:
                if pname == select_playlist:
                    ba.columnwidget(edit=self._columnwidget,
                                    selected_child=txtw,
                                    visible_child=txtw)
            else:
                # Select this one if it was previously selected.
                # Go by index if there's one.
                if old_selection_index is not None:
                    if index == old_selection_index:
                        ba.columnwidget(edit=self._columnwidget,
                                        selected_child=txtw,
                                        visible_child=txtw)
                else:  # Otherwise look by name.
                    if pname == old_selection:
                        ba.columnwidget(edit=self._columnwidget,
                                        selected_child=txtw,
                                        visible_child=txtw)

            index += 1
Exemple #7
0
    def _on_got_scores_to_beat(self,
                               scores: Optional[List[Dict[str, Any]]]) -> None:
        # pylint: disable=too-many-locals
        # pylint: disable=too-many-statements
        from efro.util import asserttype
        from bastd.actor.text import Text

        # Sort by originating date so that the most recent is first.
        if scores is not None:
            scores.sort(reverse=True,
                        key=lambda score: asserttype(score['time'], int))

        # We only show achievements and challenges for CoopGameActivities.
        session = self.session
        assert isinstance(session, ba.CoopSession)
        gameinstance = session.get_current_game_instance()
        if isinstance(gameinstance, ba.CoopGameActivity):
            score_type = gameinstance.get_score_type()
            if scores is not None:
                achievement_challenges = [
                    a for a in scores if a['type'] == 'achievement_challenge'
                ]
                score_challenges = [
                    a for a in scores if a['type'] == 'score_challenge'
                ]
            else:
                achievement_challenges = score_challenges = []

            delay = 1.0
            vpos = -140.0
            spacing = 25
            delay_inc = 0.1

            def _add_t(
                text: Union[str, ba.Lstr],
                h_offs: float = 0.0,
                scale: float = 1.0,
                color: Sequence[float] = (1.0, 1.0, 1.0, 0.46)
            ) -> None:
                Text(text,
                     scale=scale * 0.76,
                     h_align=Text.HAlign.LEFT,
                     h_attach=Text.HAttach.LEFT,
                     v_attach=Text.VAttach.TOP,
                     transition=Text.Transition.FADE_IN,
                     transition_delay=delay,
                     color=color,
                     position=(60 + h_offs, vpos)).autoretain()

            if score_challenges:
                _add_t(ba.Lstr(value='${A}:',
                               subs=[('${A}',
                                      ba.Lstr(resource='scoreChallengesText'))
                                     ]),
                       scale=1.1)
                delay += delay_inc
                vpos -= spacing
                for chal in score_challenges:
                    _add_t(str(chal['value'] if score_type == 'points' else ba.
                               timestring(int(chal['value']) * 10,
                                          timeformat=ba.TimeFormat.MILLISECONDS
                                          ).evaluate()) + '  (1 player)',
                           h_offs=30,
                           color=(0.9, 0.7, 1.0, 0.8))
                    delay += delay_inc
                    vpos -= 0.6 * spacing
                    _add_t(chal['player'],
                           h_offs=40,
                           color=(0.8, 1, 0.8, 0.6),
                           scale=0.8)
                    delay += delay_inc
                    vpos -= 1.2 * spacing
                vpos -= 0.5 * spacing

            if achievement_challenges:
                _add_t(ba.Lstr(
                    value='${A}:',
                    subs=[('${A}',
                           ba.Lstr(resource='achievementChallengesText'))]),
                       scale=1.1)
                delay += delay_inc
                vpos -= spacing
                for chal in achievement_challenges:
                    _add_t(str(chal['value']),
                           h_offs=30,
                           color=(0.9, 0.7, 1.0, 0.8))
                    delay += delay_inc
                    vpos -= 0.6 * spacing
                    _add_t(chal['player'],
                           h_offs=40,
                           color=(0.8, 1, 0.8, 0.6),
                           scale=0.8)
                    delay += delay_inc
                    vpos -= 1.2 * spacing
                vpos -= 0.5 * spacing

            # Now list our remaining achievements for this level.
            assert self.session.campaign is not None
            assert isinstance(self.session, ba.CoopSession)
            levelname = (self.session.campaign.name + ':' +
                         self.session.campaign_level_name)
            ts_h_offs = 60

            if not (ba.app.demo_mode or ba.app.arcade_mode):
                achievements = [
                    a
                    for a in ba.app.ach.achievements_for_coop_level(levelname)
                    if not a.complete
                ]
                have_achievements = bool(achievements)
                achievements = [a for a in achievements if not a.complete]
                vrmode = ba.app.vr_mode
                if have_achievements:
                    Text(ba.Lstr(resource='achievementsRemainingText'),
                         host_only=True,
                         position=(ts_h_offs - 10, vpos),
                         transition=Text.Transition.FADE_IN,
                         scale=1.1 * 0.76,
                         h_attach=Text.HAttach.LEFT,
                         v_attach=Text.VAttach.TOP,
                         color=(1, 1, 1.2, 1) if vrmode else (0.8, 0.8, 1, 1),
                         shadow=1.0,
                         flatness=1.0 if vrmode else 0.6,
                         transition_delay=delay).autoretain()
                    hval = ts_h_offs + 50
                    vpos -= 35
                    for ach in achievements:
                        delay += 0.05
                        ach.create_display(hval, vpos, delay, style='in_game')
                        vpos -= 55
                    if not achievements:
                        Text(ba.Lstr(resource='noAchievementsRemainingText'),
                             host_only=True,
                             position=(ts_h_offs + 15, vpos + 10),
                             transition=Text.Transition.FADE_IN,
                             scale=0.7,
                             h_attach=Text.HAttach.LEFT,
                             v_attach=Text.VAttach.TOP,
                             color=(1, 1, 1, 0.5),
                             transition_delay=delay + 0.5).autoretain()
Exemple #8
0
    def _refresh(self) -> None:
        # FIXME: Should tidy this up.
        # pylint: disable=too-many-statements
        # pylint: disable=too-many-branches
        # pylint: disable=too-many-locals
        # pylint: disable=too-many-nested-blocks
        from efro.util import asserttype
        from ba.internal import (get_map_class,
                                 get_default_free_for_all_playlist,
                                 get_default_teams_playlist, filter_playlist)
        if not self._root_widget:
            return
        if self._subcontainer is not None:
            self._save_state()
            self._subcontainer.delete()

        # Make sure config exists.
        if self._config_name_full not in ba.app.config:
            ba.app.config[self._config_name_full] = {}

        items = list(ba.app.config[self._config_name_full].items())

        # Make sure everything is unicode.
        items = [(i[0].decode(), i[1]) if not isinstance(i[0], str) else i
                 for i in items]

        items.sort(key=lambda x2: asserttype(x2[0], str).lower())
        items = [['__default__', None]] + items  # default is always first

        count = len(items)
        columns = 3
        rows = int(math.ceil(float(count) / columns))
        button_width = 230
        button_height = 230
        button_buffer_h = -3
        button_buffer_v = 0

        self._sub_width = self._scroll_width
        self._sub_height = 40 + rows * (button_height +
                                        2 * button_buffer_v) + 90
        assert self._sub_width is not None
        assert self._sub_height is not None
        self._subcontainer = ba.containerwidget(parent=self._scrollwidget,
                                                size=(self._sub_width,
                                                      self._sub_height),
                                                background=False)

        children = self._subcontainer.get_children()
        for child in children:
            child.delete()

        ba.textwidget(parent=self._subcontainer,
                      text=ba.Lstr(resource='playlistsText'),
                      position=(40, self._sub_height - 26),
                      size=(0, 0),
                      scale=1.0,
                      maxwidth=400,
                      color=ba.app.ui.title_color,
                      h_align='left',
                      v_align='center')

        index = 0
        appconfig = ba.app.config

        model_opaque = ba.getmodel('level_select_button_opaque')
        model_transparent = ba.getmodel('level_select_button_transparent')
        mask_tex = ba.gettexture('mapPreviewMask')

        h_offs = 225 if count == 1 else 115 if count == 2 else 0
        h_offs_bottom = 0

        uiscale = ba.app.ui.uiscale
        for y in range(rows):
            for x in range(columns):
                name = items[index][0]
                assert name is not None
                pos = (x * (button_width + 2 * button_buffer_h) +
                       button_buffer_h + 8 + h_offs, self._sub_height - 47 -
                       (y + 1) * (button_height + 2 * button_buffer_v))
                btn = ba.buttonwidget(parent=self._subcontainer,
                                      button_type='square',
                                      size=(button_width, button_height),
                                      autoselect=True,
                                      label='',
                                      position=pos)

                if (x == 0 and ba.app.ui.use_toolbars
                        and uiscale is ba.UIScale.SMALL):
                    ba.widget(
                        edit=btn,
                        left_widget=_ba.get_special_widget('back_button'))
                if (x == columns - 1 and ba.app.ui.use_toolbars
                        and uiscale is ba.UIScale.SMALL):
                    ba.widget(
                        edit=btn,
                        right_widget=_ba.get_special_widget('party_button'))
                ba.buttonwidget(
                    edit=btn,
                    on_activate_call=ba.Call(self._on_playlist_press, btn,
                                             name),
                    on_select_call=ba.Call(self._on_playlist_select, name))
                ba.widget(edit=btn, show_buffer_top=50, show_buffer_bottom=50)

                if self._selected_playlist == name:
                    ba.containerwidget(edit=self._subcontainer,
                                       selected_child=btn,
                                       visible_child=btn)

                if self._back_button is not None:
                    if y == 0:
                        ba.widget(edit=btn, up_widget=self._back_button)
                    if x == 0:
                        ba.widget(edit=btn, left_widget=self._back_button)

                print_name: Optional[Union[str, ba.Lstr]]
                if name == '__default__':
                    print_name = self._pvars.default_list_name
                else:
                    print_name = name
                ba.textwidget(parent=self._subcontainer,
                              text=print_name,
                              position=(pos[0] + button_width * 0.5,
                                        pos[1] + button_height * 0.79),
                              size=(0, 0),
                              scale=button_width * 0.003,
                              maxwidth=button_width * 0.7,
                              draw_controller=btn,
                              h_align='center',
                              v_align='center')

                # Poke into this playlist and see if we can display some of
                # its maps.
                map_images = []
                try:
                    map_textures = []
                    map_texture_entries = []
                    if name == '__default__':
                        if self._sessiontype is ba.FreeForAllSession:
                            playlist = (get_default_free_for_all_playlist())
                        elif self._sessiontype is ba.DualTeamSession:
                            playlist = get_default_teams_playlist()
                        else:
                            raise Exception('unrecognized session-type: ' +
                                            str(self._sessiontype))
                    else:
                        if name not in appconfig[self._pvars.config_name +
                                                 ' Playlists']:
                            print(
                                'NOT FOUND ERR',
                                appconfig[self._pvars.config_name +
                                          ' Playlists'])
                        playlist = appconfig[self._pvars.config_name +
                                             ' Playlists'][name]
                    playlist = filter_playlist(playlist,
                                               self._sessiontype,
                                               remove_unowned=False,
                                               mark_unowned=True)
                    for entry in playlist:
                        mapname = entry['settings']['map']
                        maptype: Optional[Type[ba.Map]]
                        try:
                            maptype = get_map_class(mapname)
                        except ba.NotFoundError:
                            maptype = None
                        if maptype is not None:
                            tex_name = maptype.get_preview_texture_name()
                            if tex_name is not None:
                                map_textures.append(tex_name)
                                map_texture_entries.append(entry)
                        if len(map_textures) >= 6:
                            break

                    if len(map_textures) > 4:
                        img_rows = 3
                        img_columns = 2
                        scl = 0.33
                        h_offs_img = 30
                        v_offs_img = 126
                    elif len(map_textures) > 2:
                        img_rows = 2
                        img_columns = 2
                        scl = 0.35
                        h_offs_img = 24
                        v_offs_img = 110
                    elif len(map_textures) > 1:
                        img_rows = 2
                        img_columns = 1
                        scl = 0.5
                        h_offs_img = 47
                        v_offs_img = 105
                    else:
                        img_rows = 1
                        img_columns = 1
                        scl = 0.75
                        h_offs_img = 20
                        v_offs_img = 65

                    v = None
                    for row in range(img_rows):
                        for col in range(img_columns):
                            tex_index = row * img_columns + col
                            if tex_index < len(map_textures):
                                entry = map_texture_entries[tex_index]

                                owned = not (('is_unowned_map' in entry
                                              and entry['is_unowned_map']) or
                                             ('is_unowned_game' in entry
                                              and entry['is_unowned_game']))

                                tex_name = map_textures[tex_index]
                                h = pos[0] + h_offs_img + scl * 250 * col
                                v = pos[1] + v_offs_img - scl * 130 * row
                                map_images.append(
                                    ba.imagewidget(
                                        parent=self._subcontainer,
                                        size=(scl * 250.0, scl * 125.0),
                                        position=(h, v),
                                        texture=ba.gettexture(tex_name),
                                        opacity=1.0 if owned else 0.25,
                                        draw_controller=btn,
                                        model_opaque=model_opaque,
                                        model_transparent=model_transparent,
                                        mask_texture=mask_tex))
                                if not owned:
                                    ba.imagewidget(
                                        parent=self._subcontainer,
                                        size=(scl * 100.0, scl * 100.0),
                                        position=(h + scl * 75, v + scl * 10),
                                        texture=ba.gettexture('lock'),
                                        draw_controller=btn)
                        if v is not None:
                            v -= scl * 130.0

                except Exception:
                    ba.print_exception('Error listing playlist maps.')

                if not map_images:
                    ba.textwidget(parent=self._subcontainer,
                                  text='???',
                                  scale=1.5,
                                  size=(0, 0),
                                  color=(1, 1, 1, 0.5),
                                  h_align='center',
                                  v_align='center',
                                  draw_controller=btn,
                                  position=(pos[0] + button_width * 0.5,
                                            pos[1] + button_height * 0.5))

                index += 1

                if index >= count:
                    break
            if index >= count:
                break
        self._customize_button = btn = ba.buttonwidget(
            parent=self._subcontainer,
            size=(100, 30),
            position=(34 + h_offs_bottom, 50),
            text_scale=0.6,
            label=ba.Lstr(resource='customizeText'),
            on_activate_call=self._on_customize_press,
            color=(0.54, 0.52, 0.67),
            textcolor=(0.7, 0.65, 0.7),
            autoselect=True)
        ba.widget(edit=btn, show_buffer_top=22, show_buffer_bottom=28)
        self._restore_state()