def _refresh(self, select_get_more_maps_button: bool = False) -> None: # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals from ba.internal import (get_unowned_maps, get_map_class, get_map_display_string) # Kill old. if self._subcontainer is not None: self._subcontainer.delete() model_opaque = ba.getmodel('level_select_button_opaque') model_transparent = ba.getmodel('level_select_button_transparent') self._maps = [] map_list = self._gametype.get_supported_maps(self._sessiontype) map_list_sorted = list(map_list) map_list_sorted.sort() unowned_maps = get_unowned_maps() for mapname in map_list_sorted: # Disallow ones we don't own. if mapname in unowned_maps: continue map_tex_name = (get_map_class(mapname).get_preview_texture_name()) if map_tex_name is not None: try: map_tex = ba.gettexture(map_tex_name) self._maps.append((mapname, map_tex)) except Exception: print(f'Invalid map preview texture: "{map_tex_name}".') else: print('Error: no map preview texture for map:', mapname) count = len(self._maps) columns = 2 rows = int(math.ceil(float(count) / columns)) button_width = 220 button_height = button_width * 0.5 button_buffer_h = 16 button_buffer_v = 19 self._sub_width = self._scroll_width * 0.95 self._sub_height = 5 + rows * (button_height + 2 * button_buffer_v) + 100 self._subcontainer = ba.containerwidget(parent=self._scrollwidget, size=(self._sub_width, self._sub_height), background=False) index = 0 mask_texture = ba.gettexture('mapPreviewMask') h_offs = 130 if len(self._maps) == 1 else 0 for y in range(rows): for x in range(columns): pos = (x * (button_width + 2 * button_buffer_h) + button_buffer_h + h_offs, self._sub_height - (y + 1) * (button_height + 2 * button_buffer_v) + 12) btn = ba.buttonwidget(parent=self._subcontainer, button_type='square', size=(button_width, button_height), autoselect=True, texture=self._maps[index][1], mask_texture=mask_texture, model_opaque=model_opaque, model_transparent=model_transparent, label='', color=(1, 1, 1), on_activate_call=ba.Call( self._select_with_delay, self._maps[index][0]), position=pos) if x == 0: ba.widget(edit=btn, left_widget=self._cancel_button) if y == 0: ba.widget(edit=btn, up_widget=self._cancel_button) if x == columns - 1 and ba.app.ui.use_toolbars: ba.widget( edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, show_buffer_top=60, show_buffer_bottom=60) if self._maps[index][0] == self._previous_map: ba.containerwidget(edit=self._subcontainer, selected_child=btn, visible_child=btn) name = get_map_display_string(self._maps[index][0]) ba.textwidget(parent=self._subcontainer, text=name, position=(pos[0] + button_width * 0.5, pos[1] - 12), size=(0, 0), scale=0.5, maxwidth=button_width, draw_controller=btn, h_align='center', v_align='center', color=(0.8, 0.8, 0.8, 0.8)) index += 1 if index >= count: break if index >= count: break self._get_more_maps_button = btn = ba.buttonwidget( parent=self._subcontainer, size=(self._sub_width * 0.8, 60), position=(self._sub_width * 0.1, 30), label=ba.Lstr(resource='mapSelectGetMoreMapsText'), on_activate_call=self._on_store_press, color=(0.6, 0.53, 0.63), textcolor=(0.75, 0.7, 0.8), autoselect=True) ba.widget(edit=btn, show_buffer_top=30, show_buffer_bottom=30) if select_get_more_maps_button: ba.containerwidget(edit=self._subcontainer, selected_child=btn, visible_child=btn)
def __init__(self, sessiontype: Type[ba.Session], playlist: str, scale_origin: Tuple[float, float], delegate: Any = None): # FIXME: Tidy this up. # pylint: disable=too-many-branches # pylint: disable=too-many-statements # pylint: disable=too-many-locals from ba.internal import (getclass, have_pro, get_default_teams_playlist, get_default_free_for_all_playlist, filter_playlist) from ba.internal import get_map_class from bastd.ui.playlist import PlaylistTypeVars self._r = 'gameListWindow' self._delegate = delegate self._pvars = PlaylistTypeVars(sessiontype) self._transitioning_out = False self._do_randomize_val = (ba.app.config.get( self._pvars.config_name + ' Playlist Randomize', 0)) self._sessiontype = sessiontype self._playlist = playlist self._width = 500.0 self._height = 330.0 - 50.0 # In teams games, show the custom names/colors button. if self._sessiontype is ba.DualTeamSession: self._height += 50.0 self._row_height = 45.0 # Grab our maps to display. model_opaque = ba.getmodel('level_select_button_opaque') model_transparent = ba.getmodel('level_select_button_transparent') mask_tex = ba.gettexture('mapPreviewMask') # Poke into this playlist and see if we can display some of its maps. map_textures = [] map_texture_entries = [] rows = 0 columns = 0 game_count = 0 scl = 0.35 c_width_total = 0.0 try: max_columns = 5 name = playlist if name == '__default__': if self._sessiontype is ba.FreeForAllSession: plst = get_default_free_for_all_playlist() elif self._sessiontype is ba.DualTeamSession: plst = get_default_teams_playlist() else: raise Exception('unrecognized session-type: ' + str(self._sessiontype)) else: try: plst = ba.app.config[self._pvars.config_name + ' Playlists'][name] except Exception: print('ERROR INFO: self._config_name is:', self._pvars.config_name) print( 'ERROR INFO: playlist names are:', list(ba.app.config[self._pvars.config_name + ' Playlists'].keys())) raise plst = filter_playlist(plst, self._sessiontype, remove_unowned=False, mark_unowned=True) game_count = len(plst) for entry in plst: mapname = entry['settings']['map'] maptype: Optional[Type[ba.Map]] try: maptype = get_map_class(mapname) except Exception: 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) rows = (max(0, len(map_textures) - 1) // max_columns) + 1 columns = min(max_columns, len(map_textures)) if len(map_textures) == 1: scl = 1.1 elif len(map_textures) == 2: scl = 0.7 elif len(map_textures) == 3: scl = 0.55 else: scl = 0.35 self._row_height = 128.0 * scl c_width_total = scl * 250.0 * columns if map_textures: self._height += self._row_height * rows except Exception: ba.print_exception('error listing playlist maps') show_shuffle_check_box = game_count > 1 if show_shuffle_check_box: self._height += 40 # Creates our _root_widget. scale = (1.69 if ba.app.small_ui else 1.1 if ba.app.med_ui else 0.85) super().__init__(position=scale_origin, size=(self._width, self._height), scale=scale) playlist_name: Union[str, ba.Lstr] = (self._pvars.default_list_name if playlist == '__default__' else playlist) self._title_text = ba.textwidget(parent=self.root_widget, position=(self._width * 0.5, self._height - 89 + 51), size=(0, 0), text=playlist_name, scale=1.4, color=(1, 1, 1), maxwidth=self._width * 0.7, h_align='center', v_align='center') self._cancel_button = ba.buttonwidget( parent=self.root_widget, position=(25, self._height - 53), size=(50, 50), scale=0.7, label='', color=(0.42, 0.73, 0.2), on_activate_call=self._on_cancel_press, autoselect=True, icon=ba.gettexture('crossOut'), iconscale=1.2) h_offs_img = self._width * 0.5 - c_width_total * 0.5 v_offs_img = self._height - 118 - scl * 125.0 + 50 bottom_row_buttons = [] self._have_at_least_one_owned = False for row in range(rows): for col in range(columns): tex_index = row * columns + col if tex_index < len(map_textures): tex_name = map_textures[tex_index] h = h_offs_img + scl * 250 * col v = v_offs_img - self._row_height * row 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'])) if owned: self._have_at_least_one_owned = True try: desc = getclass(entry['type'], subclassof=ba.GameActivity ).get_config_display_string(entry) if not owned: desc = ba.Lstr( value='${DESC}\n${UNLOCK}', subs=[ ('${DESC}', desc), ('${UNLOCK}', ba.Lstr( resource='unlockThisInTheStoreText')) ]) desc_color = (0, 1, 0) if owned else (1, 0, 0) except Exception: desc = ba.Lstr(value='(invalid)') desc_color = (1, 0, 0) btn = ba.buttonwidget( parent=self.root_widget, size=(scl * 240.0, scl * 120.0), position=(h, v), texture=ba.gettexture(tex_name if owned else 'empty'), model_opaque=model_opaque if owned else None, on_activate_call=ba.Call(ba.screenmessage, desc, desc_color), label='', color=(1, 1, 1), autoselect=True, extra_touch_border_scale=0.0, model_transparent=model_transparent if owned else None, mask_texture=mask_tex if owned else None) if row == 0 and col == 0: ba.widget(edit=self._cancel_button, down_widget=btn) if row == rows - 1: bottom_row_buttons.append(btn) if not owned: # Ewww; buttons don't currently have alpha so in this # case we draw an image over our button with an empty # texture on it. ba.imagewidget(parent=self.root_widget, size=(scl * 260.0, scl * 130.0), position=(h - 10.0 * scl, v - 4.0 * scl), draw_controller=btn, color=(1, 1, 1), texture=ba.gettexture(tex_name), model_opaque=model_opaque, opacity=0.25, model_transparent=model_transparent, mask_texture=mask_tex) ba.imagewidget(parent=self.root_widget, size=(scl * 100, scl * 100), draw_controller=btn, position=(h + scl * 70, v + scl * 10), texture=ba.gettexture('lock')) # Team names/colors. self._custom_colors_names_button: Optional[ba.Widget] if self._sessiontype is ba.DualTeamSession: y_offs = 50 if show_shuffle_check_box else 0 self._custom_colors_names_button = ba.buttonwidget( parent=self.root_widget, position=(100, 200 + y_offs), size=(290, 35), on_activate_call=ba.WeakCall(self._custom_colors_names_press), autoselect=True, textcolor=(0.8, 0.8, 0.8), label=ba.Lstr(resource='teamNamesColorText')) if not have_pro(): ba.imagewidget( parent=self.root_widget, size=(30, 30), position=(95, 202 + y_offs), texture=ba.gettexture('lock'), draw_controller=self._custom_colors_names_button) else: self._custom_colors_names_button = None # Shuffle. def _cb_callback(val: bool) -> None: self._do_randomize_val = val cfg = ba.app.config cfg[self._pvars.config_name + ' Playlist Randomize'] = self._do_randomize_val cfg.commit() if show_shuffle_check_box: self._shuffle_check_box = ba.checkboxwidget( parent=self.root_widget, position=(110, 200), scale=1.0, size=(250, 30), autoselect=True, text=ba.Lstr(resource=self._r + '.shuffleGameOrderText'), maxwidth=300, textcolor=(0.8, 0.8, 0.8), value=self._do_randomize_val, on_value_change_call=_cb_callback) # Show tutorial. try: show_tutorial = ba.app.config['Show Tutorial'] except Exception: show_tutorial = True def _cb_callback_2(val: bool) -> None: cfg = ba.app.config cfg['Show Tutorial'] = val cfg.commit() self._show_tutorial_check_box = ba.checkboxwidget( parent=self.root_widget, position=(110, 151), scale=1.0, size=(250, 30), autoselect=True, text=ba.Lstr(resource=self._r + '.showTutorialText'), maxwidth=300, textcolor=(0.8, 0.8, 0.8), value=show_tutorial, on_value_change_call=_cb_callback_2) # Grumble: current autoselect doesn't do a very good job # with checkboxes. if self._custom_colors_names_button is not None: for btn in bottom_row_buttons: ba.widget(edit=btn, down_widget=self._custom_colors_names_button) if show_shuffle_check_box: ba.widget(edit=self._custom_colors_names_button, down_widget=self._shuffle_check_box) ba.widget(edit=self._shuffle_check_box, up_widget=self._custom_colors_names_button) else: ba.widget(edit=self._custom_colors_names_button, down_widget=self._show_tutorial_check_box) ba.widget(edit=self._show_tutorial_check_box, up_widget=self._custom_colors_names_button) self._play_button = ba.buttonwidget( parent=self.root_widget, position=(70, 44), size=(200, 45), scale=1.8, text_res_scale=1.5, on_activate_call=self._on_play_press, autoselect=True, label=ba.Lstr(resource='playText')) ba.widget(edit=self._play_button, up_widget=self._show_tutorial_check_box) ba.containerwidget(edit=self.root_widget, start_button=self._play_button, cancel_button=self._cancel_button, selected_child=self._play_button) # Update now and once per second. self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), timetype=ba.TimeType.REAL, repeat=True) self._update()
def __init__(self, gametype: Type[ba.GameActivity], sessiontype: Type[ba.Session], config: Optional[Dict[str, Any]], completion_call: Callable[[Optional[Dict[str, Any]]], Any], default_selection: str = None, transition: str = 'in_right', edit_info: Dict[str, Any] = None): # pylint: disable=too-many-branches # pylint: disable=too-many-statements # pylint: disable=too-many-locals from ba.internal import (get_unowned_maps, get_filtered_map_name, get_map_class, get_map_display_string) self._gametype = gametype self._sessiontype = sessiontype # If we're within an editing session we get passed edit_info # (returning from map selection window, etc). if edit_info is not None: self._edit_info = edit_info # ..otherwise determine whether we're adding or editing a game based # on whether an existing config was passed to us. else: if config is None: self._edit_info = {'editType': 'add'} else: self._edit_info = {'editType': 'edit'} self._r = 'gameSettingsWindow' valid_maps = gametype.get_supported_maps(sessiontype) if not valid_maps: ba.screenmessage(ba.Lstr(resource='noValidMapsErrorText')) raise Exception('No valid maps') self._settings_defs = gametype.get_available_settings(sessiontype) self._completion_call = completion_call # To start with, pick a random map out of the ones we own. unowned_maps = get_unowned_maps() valid_maps_owned = [m for m in valid_maps if m not in unowned_maps] if valid_maps_owned: self._map = valid_maps[random.randrange(len(valid_maps_owned))] # Hmmm.. we own none of these maps.. just pick a random un-owned one # I guess.. should this ever happen? else: self._map = valid_maps[random.randrange(len(valid_maps))] is_add = (self._edit_info['editType'] == 'add') # If there's a valid map name in the existing config, use that. try: if (config is not None and 'settings' in config and 'map' in config['settings']): filtered_map_name = get_filtered_map_name( config['settings']['map']) if filtered_map_name in valid_maps: self._map = filtered_map_name except Exception: ba.print_exception('Error getting map for editor.') if config is not None and 'settings' in config: self._settings = config['settings'] else: self._settings = {} self._choice_selections: Dict[str, int] = {} uiscale = ba.app.ui.uiscale width = 720 if uiscale is ba.UIScale.SMALL else 620 x_inset = 50 if uiscale is ba.UIScale.SMALL else 0 height = (365 if uiscale is ba.UIScale.SMALL else 460 if uiscale is ba.UIScale.MEDIUM else 550) spacing = 52 y_extra = 15 y_extra2 = 21 map_tex_name = (get_map_class(self._map).get_preview_texture_name()) if map_tex_name is None: raise Exception('no map preview tex found for' + self._map) map_tex = ba.gettexture(map_tex_name) top_extra = 20 if uiscale is ba.UIScale.SMALL else 0 super().__init__(root_widget=ba.containerwidget( size=(width, height + top_extra), transition=transition, scale=(2.19 if uiscale is ba.UIScale.SMALL else 1.35 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -17) if uiscale is ba.UIScale.SMALL else (0, 0))) btn = ba.buttonwidget( parent=self._root_widget, position=(45 + x_inset, height - 82 + y_extra2), size=(180, 70) if is_add else (180, 65), label=ba.Lstr(resource='backText') if is_add else ba.Lstr( resource='cancelText'), button_type='back' if is_add else None, autoselect=True, scale=0.75, text_scale=1.3, on_activate_call=ba.Call(self._cancel)) ba.containerwidget(edit=self._root_widget, cancel_button=btn) add_button = ba.buttonwidget( parent=self._root_widget, position=(width - (193 + x_inset), height - 82 + y_extra2), size=(200, 65), scale=0.75, text_scale=1.3, label=ba.Lstr(resource=self._r + '.addGameText') if is_add else ba.Lstr( resource='doneText')) if ba.app.ui.use_toolbars: pbtn = _ba.get_special_widget('party_button') ba.widget(edit=add_button, right_widget=pbtn, up_widget=pbtn) ba.textwidget(parent=self._root_widget, position=(-8, height - 70 + y_extra2), size=(width, 25), text=gametype.get_display_string(), color=ba.app.ui.title_color, maxwidth=235, scale=1.1, h_align='center', v_align='center') map_height = 100 scroll_height = map_height + 10 # map select and margin # Calc our total height we'll need scroll_height += spacing * len(self._settings_defs) scroll_width = width - (86 + 2 * x_inset) self._scrollwidget = ba.scrollwidget(parent=self._root_widget, position=(44 + x_inset, 35 + y_extra), size=(scroll_width, height - 116), highlight=False, claims_left_right=True, claims_tab=True, selection_loops_to_parent=True) self._subcontainer = ba.containerwidget(parent=self._scrollwidget, size=(scroll_width, scroll_height), background=False, claims_left_right=True, claims_tab=True, selection_loops_to_parent=True) v = scroll_height - 5 h = -40 # Keep track of all the selectable widgets we make so we can wire # them up conveniently. widget_column: List[List[ba.Widget]] = [] # Map select button. ba.textwidget(parent=self._subcontainer, position=(h + 49, v - 63), size=(100, 30), maxwidth=110, text=ba.Lstr(resource='mapText'), h_align='left', color=(0.8, 0.8, 0.8, 1.0), v_align='center') ba.imagewidget( parent=self._subcontainer, size=(256 * 0.7, 125 * 0.7), position=(h + 261 - 128 + 128.0 * 0.56, v - 90), texture=map_tex, model_opaque=ba.getmodel('level_select_button_opaque'), model_transparent=ba.getmodel('level_select_button_transparent'), mask_texture=ba.gettexture('mapPreviewMask')) map_button = btn = ba.buttonwidget( parent=self._subcontainer, size=(140, 60), position=(h + 448, v - 72), on_activate_call=ba.Call(self._select_map), scale=0.7, label=ba.Lstr(resource='mapSelectText')) widget_column.append([btn]) ba.textwidget(parent=self._subcontainer, position=(h + 363 - 123, v - 114), size=(100, 30), flatness=1.0, shadow=1.0, scale=0.55, maxwidth=256 * 0.7 * 0.8, text=get_map_display_string(self._map), h_align='center', color=(0.6, 1.0, 0.6, 1.0), v_align='center') v -= map_height for setting in self._settings_defs: value = setting.default value_type = type(value) # Now, if there's an existing value for it in the config, # override with that. try: if (config is not None and 'settings' in config and setting.name in config['settings']): value = value_type(config['settings'][setting.name]) except Exception: ba.print_exception() # Shove the starting value in there to start. self._settings[setting.name] = value name_translated = self._get_localized_setting_name(setting.name) mw1 = 280 mw2 = 70 # Handle types with choices specially: if isinstance(setting, ba.ChoiceSetting): for choice in setting.choices: if len(choice) != 2: raise ValueError( "Expected 2-member tuples for 'choices'; got: " + repr(choice)) if not isinstance(choice[0], str): raise TypeError( 'First value for choice tuple must be a str; got: ' + repr(choice)) if not isinstance(choice[1], value_type): raise TypeError( 'Choice type does not match default value; choice:' + repr(choice) + '; setting:' + repr(setting)) if value_type not in (int, float): raise TypeError( 'Choice type setting must have int or float default; ' 'got: ' + repr(setting)) # Start at the choice corresponding to the default if possible. self._choice_selections[setting.name] = 0 for index, choice in enumerate(setting.choices): if choice[1] == value: self._choice_selections[setting.name] = index break v -= spacing ba.textwidget(parent=self._subcontainer, position=(h + 50, v), size=(100, 30), maxwidth=mw1, text=name_translated, h_align='left', color=(0.8, 0.8, 0.8, 1.0), v_align='center') txt = ba.textwidget( parent=self._subcontainer, position=(h + 509 - 95, v), size=(0, 28), text=self._get_localized_setting_name(setting.choices[ self._choice_selections[setting.name]][0]), editable=False, color=(0.6, 1.0, 0.6, 1.0), maxwidth=mw2, h_align='right', v_align='center', padding=2) btn1 = ba.buttonwidget(parent=self._subcontainer, position=(h + 509 - 50 - 1, v), size=(28, 28), label='<', autoselect=True, on_activate_call=ba.Call( self._choice_inc, setting.name, txt, setting, -1), repeat=True) btn2 = ba.buttonwidget(parent=self._subcontainer, position=(h + 509 + 5, v), size=(28, 28), label='>', autoselect=True, on_activate_call=ba.Call( self._choice_inc, setting.name, txt, setting, 1), repeat=True) widget_column.append([btn1, btn2]) elif isinstance(setting, (ba.IntSetting, ba.FloatSetting)): v -= spacing min_value = setting.min_value max_value = setting.max_value increment = setting.increment ba.textwidget(parent=self._subcontainer, position=(h + 50, v), size=(100, 30), text=name_translated, h_align='left', color=(0.8, 0.8, 0.8, 1.0), v_align='center', maxwidth=mw1) txt = ba.textwidget(parent=self._subcontainer, position=(h + 509 - 95, v), size=(0, 28), text=str(value), editable=False, color=(0.6, 1.0, 0.6, 1.0), maxwidth=mw2, h_align='right', v_align='center', padding=2) btn1 = ba.buttonwidget(parent=self._subcontainer, position=(h + 509 - 50 - 1, v), size=(28, 28), label='-', autoselect=True, on_activate_call=ba.Call( self._inc, txt, min_value, max_value, -increment, value_type, setting.name), repeat=True) btn2 = ba.buttonwidget(parent=self._subcontainer, position=(h + 509 + 5, v), size=(28, 28), label='+', autoselect=True, on_activate_call=ba.Call( self._inc, txt, min_value, max_value, increment, value_type, setting.name), repeat=True) widget_column.append([btn1, btn2]) elif value_type == bool: v -= spacing ba.textwidget(parent=self._subcontainer, position=(h + 50, v), size=(100, 30), text=name_translated, h_align='left', color=(0.8, 0.8, 0.8, 1.0), v_align='center', maxwidth=mw1) txt = ba.textwidget( parent=self._subcontainer, position=(h + 509 - 95, v), size=(0, 28), text=ba.Lstr(resource='onText') if value else ba.Lstr( resource='offText'), editable=False, color=(0.6, 1.0, 0.6, 1.0), maxwidth=mw2, h_align='right', v_align='center', padding=2) cbw = ba.checkboxwidget(parent=self._subcontainer, text='', position=(h + 505 - 50 - 5, v - 2), size=(200, 30), autoselect=True, textcolor=(0.8, 0.8, 0.8), value=value, on_value_change_call=ba.Call( self._check_value_change, setting.name, txt)) widget_column.append([cbw]) else: raise Exception() # Ok now wire up the column. try: # pylint: disable=unsubscriptable-object prev_widgets: Optional[List[ba.Widget]] = None for cwdg in widget_column: if prev_widgets is not None: # Wire our rightmost to their rightmost. ba.widget(edit=prev_widgets[-1], down_widget=cwdg[-1]) ba.widget(cwdg[-1], up_widget=prev_widgets[-1]) # Wire our leftmost to their leftmost. ba.widget(edit=prev_widgets[0], down_widget=cwdg[0]) ba.widget(cwdg[0], up_widget=prev_widgets[0]) prev_widgets = cwdg except Exception: ba.print_exception( 'Error wiring up game-settings-select widget column.') ba.buttonwidget(edit=add_button, on_activate_call=ba.Call(self._add)) ba.containerwidget(edit=self._root_widget, selected_child=add_button, start_button=add_button) if default_selection == 'map': ba.containerwidget(edit=self._root_widget, selected_child=self._scrollwidget) ba.containerwidget(edit=self._subcontainer, selected_child=map_button)
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 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: x2[0].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.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.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.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.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()