def _rebuild_ui(self) -> None: # pylint: disable=too-many-statements # pylint: disable=too-many-locals from ba.internal import get_device_value # Clear existing UI. for widget in self._root_widget.get_children(): widget.delete() self._textwidgets: Dict[str, ba.Widget] = {} # If we were supplied with settings, we're a secondary joystick and # just operate on that. in the other (normal) case we make our own. if not self._is_secondary: # Fill our temp config with present values (for our primary and # secondary controls). self._settings = {} for skey in [ 'buttonJump', 'buttonJump_B', 'buttonPunch', 'buttonPunch_B', 'buttonBomb', 'buttonBomb_B', 'buttonPickUp', 'buttonPickUp_B', 'buttonStart', 'buttonStart_B', 'buttonStart2', 'buttonStart2_B', 'buttonUp', 'buttonUp_B', 'buttonDown', 'buttonDown_B', 'buttonLeft', 'buttonLeft_B', 'buttonRight', 'buttonRight_B', 'buttonRun1', 'buttonRun1_B', 'buttonRun2', 'buttonRun2_B', 'triggerRun1', 'triggerRun1_B', 'triggerRun2', 'triggerRun2_B', 'buttonIgnored', 'buttonIgnored_B', 'buttonIgnored2', 'buttonIgnored2_B', 'buttonIgnored3', 'buttonIgnored3_B', 'buttonIgnored4', 'buttonIgnored4_B', 'buttonVRReorient', 'buttonVRReorient_B', 'analogStickDeadZone', 'analogStickDeadZone_B', 'dpad', 'dpad_B', 'unassignedButtonsRun', 'unassignedButtonsRun_B', 'startButtonActivatesDefaultWidget', 'startButtonActivatesDefaultWidget_B', 'uiOnly', 'uiOnly_B', 'ignoreCompletely', 'ignoreCompletely_B', 'autoRecalibrateAnalogStick', 'autoRecalibrateAnalogStick_B', 'analogStickLR', 'analogStickLR_B', 'analogStickUD', 'analogStickUD_B', 'enableSecondary', ]: val = get_device_value(self._input, skey) if val != -1: self._settings[skey] = val back_button: Optional[ba.Widget] if self._is_secondary: back_button = ba.buttonwidget(parent=self._root_widget, position=(self._width - 180, self._height - 65), autoselect=True, size=(160, 60), label=ba.Lstr(resource='doneText'), scale=0.9, on_activate_call=self._save) ba.containerwidget(edit=self._root_widget, start_button=back_button, on_cancel_call=back_button.activate) cancel_button = None else: cancel_button = ba.buttonwidget( parent=self._root_widget, position=(51, self._height - 65), autoselect=True, size=(160, 60), label=ba.Lstr(resource='cancelText'), scale=0.9, on_activate_call=self._cancel) ba.containerwidget(edit=self._root_widget, cancel_button=cancel_button) save_button: Optional[ba.Widget] if not self._is_secondary: save_button = ba.buttonwidget( parent=self._root_widget, position=(self._width - (165 if self._is_secondary else 195), self._height - 65), size=((160 if self._is_secondary else 180), 60), autoselect=True, label=ba.Lstr(resource='doneText') if self._is_secondary else ba.Lstr(resource='saveText'), scale=0.9, on_activate_call=self._save) ba.containerwidget(edit=self._root_widget, start_button=save_button) else: save_button = None if not self._is_secondary: v = self._height - 59 ba.textwidget(parent=self._root_widget, position=(0, v + 5), size=(self._width, 25), text=ba.Lstr(resource=self._r + '.titleText'), color=ba.app.ui.title_color, maxwidth=310, h_align='center', v_align='center') v -= 48 ba.textwidget(parent=self._root_widget, position=(0, v + 3), size=(self._width, 25), text=self._name, color=ba.app.ui.infotextcolor, maxwidth=self._width * 0.9, h_align='center', v_align='center') v -= self._spacing * 1 ba.textwidget(parent=self._root_widget, position=(50, v + 10), size=(self._width - 100, 30), text=ba.Lstr(resource=self._r + '.appliesToAllText'), maxwidth=330, scale=0.65, color=(0.5, 0.6, 0.5, 1.0), h_align='center', v_align='center') v -= 70 self._enable_check_box = None else: v = self._height - 49 ba.textwidget(parent=self._root_widget, position=(0, v + 5), size=(self._width, 25), text=ba.Lstr(resource=self._r + '.secondaryText'), color=ba.app.ui.title_color, maxwidth=300, h_align='center', v_align='center') v -= self._spacing * 1 ba.textwidget(parent=self._root_widget, position=(50, v + 10), size=(self._width - 100, 30), text=ba.Lstr(resource=self._r + '.secondHalfText'), maxwidth=300, scale=0.65, color=(0.6, 0.8, 0.6, 1.0), h_align='center') self._enable_check_box = ba.checkboxwidget( parent=self._root_widget, position=(self._width * 0.5 - 80, v - 73), value=self.get_enable_secondary_value(), autoselect=True, on_value_change_call=self._enable_check_box_changed, size=(200, 30), text=ba.Lstr(resource=self._r + '.secondaryEnableText'), scale=1.2) v = self._height - 205 h_offs = 160 dist = 70 d_color = (0.4, 0.4, 0.8) sclx = 1.2 scly = 0.98 dpm = ba.Lstr(resource=self._r + '.pressAnyButtonOrDpadText') dpm2 = ba.Lstr(resource=self._r + '.ifNothingHappensTryAnalogText') self._capture_button(pos=(h_offs, v + scly * dist), color=d_color, button='buttonUp' + self._ext, texture=ba.gettexture('upButton'), scale=1.0, message=dpm, message2=dpm2) self._capture_button(pos=(h_offs - sclx * dist, v), color=d_color, button='buttonLeft' + self._ext, texture=ba.gettexture('leftButton'), scale=1.0, message=dpm, message2=dpm2) self._capture_button(pos=(h_offs + sclx * dist, v), color=d_color, button='buttonRight' + self._ext, texture=ba.gettexture('rightButton'), scale=1.0, message=dpm, message2=dpm2) self._capture_button(pos=(h_offs, v - scly * dist), color=d_color, button='buttonDown' + self._ext, texture=ba.gettexture('downButton'), scale=1.0, message=dpm, message2=dpm2) dpm3 = ba.Lstr(resource=self._r + '.ifNothingHappensTryDpadText') self._capture_button(pos=(h_offs + 130, v - 125), color=(0.4, 0.4, 0.6), button='analogStickLR' + self._ext, maxwidth=140, texture=ba.gettexture('analogStick'), scale=1.2, message=ba.Lstr(resource=self._r + '.pressLeftRightText'), message2=dpm3) self._capture_button(pos=(self._width * 0.5, v), color=(0.4, 0.4, 0.6), button='buttonStart' + self._ext, texture=ba.gettexture('startButton'), scale=0.7) h_offs = self._width - 160 self._capture_button(pos=(h_offs, v + scly * dist), color=(0.6, 0.4, 0.8), button='buttonPickUp' + self._ext, texture=ba.gettexture('buttonPickUp'), scale=1.0) self._capture_button(pos=(h_offs - sclx * dist, v), color=(0.7, 0.5, 0.1), button='buttonPunch' + self._ext, texture=ba.gettexture('buttonPunch'), scale=1.0) self._capture_button(pos=(h_offs + sclx * dist, v), color=(0.5, 0.2, 0.1), button='buttonBomb' + self._ext, texture=ba.gettexture('buttonBomb'), scale=1.0) self._capture_button(pos=(h_offs, v - scly * dist), color=(0.2, 0.5, 0.2), button='buttonJump' + self._ext, texture=ba.gettexture('buttonJump'), scale=1.0) self._advanced_button = ba.buttonwidget( parent=self._root_widget, autoselect=True, label=ba.Lstr(resource=self._r + '.advancedText'), text_scale=0.9, color=(0.45, 0.4, 0.5), textcolor=(0.65, 0.6, 0.7), position=(self._width - 300, 30), size=(130, 40), on_activate_call=self._do_advanced) try: if cancel_button is not None and save_button is not None: ba.widget(edit=cancel_button, right_widget=save_button) ba.widget(edit=save_button, left_widget=cancel_button) except Exception: ba.print_exception('Error wiring up gamepad config window.')
def _refresh(self, file_names: List[str], error: Optional[str]) -> None: # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals if not self._root_widget: return scrollwidget_selected = (self._scrollwidget is None or self._root_widget.get_selected_child() == self._scrollwidget) in_top_folder = (self._path == self._base_path) hide_top_folder = in_top_folder and self._show_base_path is False if hide_top_folder: folder_name = '' elif self._path == '/': folder_name = '/' else: assert self._path is not None folder_name = os.path.basename(self._path) b_color = (0.6, 0.53, 0.63) b_color_disabled = (0.65, 0.65, 0.65) if len(self._recent_paths) < 2: ba.buttonwidget(edit=self._back_button, color=b_color_disabled, textcolor=(0.5, 0.5, 0.5)) else: ba.buttonwidget(edit=self._back_button, color=b_color, textcolor=(0.75, 0.7, 0.8)) max_str_width = 300.0 str_width = min( max_str_width, _ba.get_string_width(folder_name, suppress_warning=True)) ba.textwidget(edit=self._path_text, text=folder_name, maxwidth=max_str_width) ba.imagewidget(edit=self._folder_icon, position=(self._folder_center - str_width * 0.5 - 40, self._height - 117), opacity=0.0 if hide_top_folder else 1.0) if self._scrollwidget is not None: self._scrollwidget.delete() if self._use_folder_button is not None: self._use_folder_button.delete() ba.widget(edit=self._cancel_button, right_widget=self._back_button) self._scrollwidget = ba.scrollwidget( parent=self._root_widget, position=((self._width - self._scroll_width) * 0.5, self._height - self._scroll_height - 119), size=(self._scroll_width, self._scroll_height)) if scrollwidget_selected: ba.containerwidget(edit=self._root_widget, selected_child=self._scrollwidget) # show error case.. if error is not None: self._subcontainer = ba.containerwidget(parent=self._scrollwidget, size=(self._scroll_width, self._scroll_height), background=False) ba.textwidget(parent=self._subcontainer, color=(1, 1, 0, 1), text=error, maxwidth=self._scroll_width * 0.9, position=(self._scroll_width * 0.48, self._scroll_height * 0.57), size=(0, 0), h_align='center', v_align='center') else: file_names = [f for f in file_names if not f.startswith('.')] file_names.sort(key=lambda x: x[0].lower()) entries = file_names entry_height = 35 folder_entry_height = 100 show_folder_entry = False show_use_folder_button = (self._allow_folders and not in_top_folder) self._subcontainerheight = entry_height * len(entries) + ( folder_entry_height if show_folder_entry else 0) v = self._subcontainerheight - (folder_entry_height if show_folder_entry else 0) self._subcontainer = ba.containerwidget( parent=self._scrollwidget, size=(self._scroll_width, self._subcontainerheight), background=False) ba.containerwidget(edit=self._scrollwidget, claims_left_right=False, claims_tab=False) ba.containerwidget(edit=self._subcontainer, claims_left_right=False, claims_tab=False, selection_loops=False, print_list_exit_instructions=False) ba.widget(edit=self._subcontainer, up_widget=self._back_button) if show_use_folder_button: self._use_folder_button = btn = ba.buttonwidget( parent=self._root_widget, position=(self._width - self._button_width - 35 - self._x_inset, self._height - 67), size=(self._button_width, 50), label=ba.Lstr(resource=self._r + '.useThisFolderButtonText'), on_activate_call=self._on_folder_entry_activated) ba.widget(edit=btn, left_widget=self._cancel_button, down_widget=self._scrollwidget) ba.widget(edit=self._cancel_button, right_widget=btn) ba.containerwidget(edit=self._root_widget, start_button=btn) folder_icon_size = 35 for num, entry in enumerate(entries): cnt = ba.containerwidget( parent=self._subcontainer, position=(0, v - entry_height), size=(self._scroll_width, entry_height), root_selectable=True, background=False, click_activate=True, on_activate_call=ba.Call(self._on_entry_activated, entry)) if num == 0: ba.widget(edit=cnt, up_widget=self._back_button) is_valid_file_path = self._is_valid_file_path(entry) assert self._path is not None is_dir = os.path.isdir(self._path + '/' + entry) if is_dir: ba.imagewidget(parent=cnt, size=(folder_icon_size, folder_icon_size), position=(10, 0.5 * entry_height - folder_icon_size * 0.5), draw_controller=cnt, texture=self._folder_tex, color=self._folder_color) else: ba.imagewidget(parent=cnt, size=(folder_icon_size, folder_icon_size), position=(10, 0.5 * entry_height - folder_icon_size * 0.5), opacity=1.0 if is_valid_file_path else 0.5, draw_controller=cnt, texture=self._file_tex, color=self._file_color) ba.textwidget(parent=cnt, draw_controller=cnt, text=entry, h_align='left', v_align='center', position=(10 + folder_icon_size * 1.05, entry_height * 0.5), size=(0, 0), maxwidth=self._scroll_width * 0.93 - 50, color=(1, 1, 1, 1) if (is_valid_file_path or is_dir) else (0.5, 0.5, 0.5, 1)) v -= entry_height
def _set_tab(self, tab_id: TabID) -> None: # pylint: disable=too-many-locals if self._current_tab == tab_id: return self._current_tab = tab_id # Preserve our current tab between runs. cfg = ba.app.config cfg['Watch Tab'] = tab_id.value cfg.commit() # Update tab colors based on which is selected. # tabs.update_tab_button_colors(self._tab_buttons, tab) self._tab_row.update_appearance(tab_id) if self._tab_container: self._tab_container.delete() scroll_left = (self._width - self._scroll_width) * 0.5 scroll_bottom = self._height - self._scroll_height - 79 - 48 # A place where tabs can store data to get cleared when # switching to a different tab self._tab_data = {} uiscale = ba.app.ui.uiscale if tab_id is self.TabID.MY_REPLAYS: c_width = self._scroll_width c_height = self._scroll_height - 20 sub_scroll_height = c_height - 63 self._my_replays_scroll_width = sub_scroll_width = ( 680 if uiscale is ba.UIScale.SMALL else 640) self._tab_container = cnt = ba.containerwidget( parent=self._root_widget, position=(scroll_left, scroll_bottom + (self._scroll_height - c_height) * 0.5), size=(c_width, c_height), background=False, selection_loops_to_parent=True) v = c_height - 30 ba.textwidget(parent=cnt, position=(c_width * 0.5, v), color=(0.6, 1.0, 0.6), scale=0.7, size=(0, 0), maxwidth=c_width * 0.9, h_align='center', v_align='center', text=ba.Lstr( resource='replayRenameWarningText', subs=[('${REPLAY}', ba.Lstr(resource='replayNameDefaultText')) ])) b_width = 140 if uiscale is ba.UIScale.SMALL else 178 b_height = (107 if uiscale is ba.UIScale.SMALL else 142 if uiscale is ba.UIScale.MEDIUM else 190) b_space_extra = (0 if uiscale is ba.UIScale.SMALL else -2 if uiscale is ba.UIScale.MEDIUM else -5) b_color = (0.6, 0.53, 0.63) b_textcolor = (0.75, 0.7, 0.8) btnv = (c_height - (48 if uiscale is ba.UIScale.SMALL else 45 if uiscale is ba.UIScale.MEDIUM else 40) - b_height) btnh = 40 if uiscale is ba.UIScale.SMALL else 40 smlh = 190 if uiscale is ba.UIScale.SMALL else 225 tscl = 1.0 if uiscale is ba.UIScale.SMALL else 1.2 self._my_replays_watch_replay_button = btn1 = ba.buttonwidget( parent=cnt, size=(b_width, b_height), position=(btnh, btnv), button_type='square', color=b_color, textcolor=b_textcolor, on_activate_call=self._on_my_replay_play_press, text_scale=tscl, label=ba.Lstr(resource=self._r + '.watchReplayButtonText'), autoselect=True) ba.widget(edit=btn1, up_widget=self._tab_row.tabs[tab_id].button) if uiscale is ba.UIScale.SMALL and ba.app.ui.use_toolbars: ba.widget(edit=btn1, left_widget=_ba.get_special_widget('back_button')) btnv -= b_height + b_space_extra ba.buttonwidget(parent=cnt, size=(b_width, b_height), position=(btnh, btnv), button_type='square', color=b_color, textcolor=b_textcolor, on_activate_call=self._on_my_replay_rename_press, text_scale=tscl, label=ba.Lstr(resource=self._r + '.renameReplayButtonText'), autoselect=True) btnv -= b_height + b_space_extra ba.buttonwidget(parent=cnt, size=(b_width, b_height), position=(btnh, btnv), button_type='square', color=b_color, textcolor=b_textcolor, on_activate_call=self._on_my_replay_delete_press, text_scale=tscl, label=ba.Lstr(resource=self._r + '.deleteReplayButtonText'), autoselect=True) v -= sub_scroll_height + 23 self._scrollwidget = scrlw = ba.scrollwidget( parent=cnt, position=(smlh, v), size=(sub_scroll_width, sub_scroll_height)) ba.containerwidget(edit=cnt, selected_child=scrlw) self._columnwidget = ba.columnwidget(parent=scrlw, left_border=10, border=2, margin=0) ba.widget(edit=scrlw, autoselect=True, left_widget=btn1, up_widget=self._tab_row.tabs[tab_id].button) ba.widget(edit=self._tab_row.tabs[tab_id].button, down_widget=scrlw) self._my_replay_selected = None self._refresh_my_replays()
def __init__(self, transition: str = 'in_right', origin_widget: ba.Widget = None): # pylint: disable=too-many-locals # pylint: disable=too-many-statements # If they provided an origin-widget, scale up from that. scale_origin: Optional[Tuple[float, float]] if origin_widget is not None: self._transition_out = 'out_scale' scale_origin = origin_widget.get_screen_space_center() transition = 'in_scale' else: self._transition_out = 'out_right' scale_origin = None self._r = 'editSoundtrackWindow' uiscale = ba.app.uiscale self._width = 800 if uiscale is ba.UIScale.SMALL else 600 x_inset = 100 if uiscale is ba.UIScale.SMALL else 0 self._height = (340 if uiscale is ba.UIScale.SMALL else 370 if uiscale is ba.UIScale.MEDIUM else 440) spacing = 40.0 v = self._height - 40.0 v -= spacing * 1.0 super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height), transition=transition, toolbar_visibility='menu_minimal', scale_origin_stack_offset=scale_origin, scale=(2.3 if uiscale is ba.UIScale.SMALL else 1.6 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -18) if uiscale is ba.UIScale.SMALL else (0, 0))) if ba.app.toolbars and uiscale is ba.UIScale.SMALL: self._back_button = None else: self._back_button = ba.buttonwidget( parent=self._root_widget, position=(45 + x_inset, self._height - 60), size=(120, 60), scale=0.8, label=ba.Lstr(resource='backText'), button_type='back', autoselect=True) ba.buttonwidget(edit=self._back_button, button_type='backSmall', size=(60, 60), label=ba.charstr(ba.SpecialChar.BACK)) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height - 35), size=(0, 0), maxwidth=300, text=ba.Lstr(resource=self._r + '.titleText'), color=ba.app.title_color, h_align='center', v_align='center') h = 43 + x_inset v = self._height - 60 b_color = (0.6, 0.53, 0.63) b_textcolor = (0.75, 0.7, 0.8) lock_tex = ba.gettexture('lock') self._lock_images: List[ba.Widget] = [] scl = (1.0 if uiscale is ba.UIScale.SMALL else 1.13 if uiscale is ba.UIScale.MEDIUM else 1.4) v -= 60.0 * scl self._new_button = btn = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(100, 55.0 * scl), on_activate_call=self._new_soundtrack, color=b_color, button_type='square', autoselect=True, textcolor=b_textcolor, text_scale=0.7, label=ba.Lstr(resource=self._r + '.newText')) self._lock_images.append( ba.imagewidget(parent=self._root_widget, size=(30, 30), draw_controller=btn, position=(h - 10, v + 55.0 * scl - 28), texture=lock_tex)) if self._back_button is None: ba.widget(edit=btn, left_widget=_ba.get_special_widget('back_button')) v -= 60.0 * scl self._edit_button = btn = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(100, 55.0 * scl), on_activate_call=self._edit_soundtrack, color=b_color, button_type='square', autoselect=True, textcolor=b_textcolor, text_scale=0.7, label=ba.Lstr(resource=self._r + '.editText')) self._lock_images.append( ba.imagewidget(parent=self._root_widget, size=(30, 30), draw_controller=btn, position=(h - 10, v + 55.0 * scl - 28), texture=lock_tex)) if self._back_button is None: ba.widget(edit=btn, left_widget=_ba.get_special_widget('back_button')) v -= 60.0 * scl self._duplicate_button = btn = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(100, 55.0 * scl), on_activate_call=self._duplicate_soundtrack, button_type='square', autoselect=True, color=b_color, textcolor=b_textcolor, text_scale=0.7, label=ba.Lstr(resource=self._r + '.duplicateText')) self._lock_images.append( ba.imagewidget(parent=self._root_widget, size=(30, 30), draw_controller=btn, position=(h - 10, v + 55.0 * scl - 28), texture=lock_tex)) if self._back_button is None: ba.widget(edit=btn, left_widget=_ba.get_special_widget('back_button')) v -= 60.0 * scl self._delete_button = btn = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(100, 55.0 * scl), on_activate_call=self._delete_soundtrack, color=b_color, button_type='square', autoselect=True, textcolor=b_textcolor, text_scale=0.7, label=ba.Lstr(resource=self._r + '.deleteText')) self._lock_images.append( ba.imagewidget(parent=self._root_widget, size=(30, 30), draw_controller=btn, position=(h - 10, v + 55.0 * scl - 28), texture=lock_tex)) if self._back_button is None: ba.widget(edit=btn, left_widget=_ba.get_special_widget('back_button')) # Keep our lock images up to date/etc. self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), timetype=ba.TimeType.REAL, repeat=True) self._update() v = self._height - 65 scroll_height = self._height - 105 v -= scroll_height self._scrollwidget = scrollwidget = ba.scrollwidget( parent=self._root_widget, position=(152 + x_inset, v), highlight=False, size=(self._width - (205 + 2 * x_inset), scroll_height)) ba.widget(edit=self._scrollwidget, left_widget=self._new_button, right_widget=_ba.get_special_widget('party_button') if ba.app.toolbars else self._scrollwidget) self._col = ba.columnwidget(parent=scrollwidget) self._soundtracks: Optional[Dict[str, Any]] = None self._selected_soundtrack: Optional[str] = None self._selected_soundtrack_index: Optional[int] = None self._soundtrack_widgets: List[ba.Widget] = [] self._allow_changing_soundtracks = False self._refresh() if self._back_button is not None: ba.buttonwidget(edit=self._back_button, on_activate_call=self._back) ba.containerwidget(edit=self._root_widget, cancel_button=self._back_button) else: ba.containerwidget(edit=self._root_widget, on_cancel_call=self._back)
def __init__(self, textwidget: ba.Widget, label: str, max_chars: int): # pylint: disable=too-many-locals # pylint: disable=too-many-statements self._target_text = textwidget self._width = 700 self._height = 400 uiscale = ba.app.ui.uiscale top_extra = 20 if uiscale is ba.UIScale.SMALL else 0 super().__init__(root_widget=ba.containerwidget( parent=_ba.get_special_widget('overlay_stack'), size=(self._width, self._height + top_extra), transition='in_scale', scale_origin_stack_offset=self._target_text. get_screen_space_center(), scale=(2.0 if uiscale is ba.UIScale.SMALL else 1.5 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, 0) if uiscale is ba.UIScale.SMALL else ( 0, 0) if uiscale is ba.UIScale.MEDIUM else (0, 0))) self._done_button = ba.buttonwidget(parent=self._root_widget, position=(self._width - 200, 44), size=(140, 60), autoselect=True, label=ba.Lstr(resource='doneText'), on_activate_call=self._done) ba.containerwidget(edit=self._root_widget, on_cancel_call=self._cancel, start_button=self._done_button) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height - 41), size=(0, 0), scale=0.95, text=label, maxwidth=self._width - 140, color=ba.app.ui.title_color, h_align='center', v_align='center') self._text_field = ba.textwidget( parent=self._root_widget, position=(70, self._height - 116), max_chars=max_chars, text=cast(str, ba.textwidget(query=self._target_text)), on_return_press_call=self._done, autoselect=True, size=(self._width - 140, 55), v_align='center', editable=True, maxwidth=self._width - 175, force_internal_editing=True, always_show_carat=True) self._shift_button = None self._double_press_shift = False self._num_mode_button = None self._emoji_button = None self._char_keys: List[ba.Widget] = [] self._mode = 'normal' self._last_mode = 'normal' v = self._height - 180 key_width = 46 key_height = 46 self._key_color_lit = (1.4, 1.2, 1.4) self._key_color = key_color = (0.69, 0.6, 0.74) self._key_color_dark = key_color_dark = (0.55, 0.55, 0.71) key_textcolor = (1, 1, 1) row_starts = (69, 95, 151) self._click_sound = ba.getsound('click01') # kill prev char keys for key in self._char_keys: key.delete() self._char_keys = [] # dummy data just used for row/column lengths... we don't actually # set things until refresh chars: List[Tuple[str, ...]] = [ ('q', 'u', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'), ('a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'), ('z', 'x', 'c', 'v', 'b', 'n', 'm') ] for row_num, row in enumerate(chars): h = row_starts[row_num] # shift key before row 3 if row_num == 2: self._shift_button = ba.buttonwidget( parent=self._root_widget, position=(h - key_width * 2.0, v), size=(key_width * 1.7, key_height), autoselect=True, textcolor=key_textcolor, color=key_color_dark, label=charstr(SpCh.SHIFT), enable_sound=False, extra_touch_border_scale=0.3, button_type='square', ) for _ in row: btn = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(key_width, key_height), autoselect=True, enable_sound=False, textcolor=key_textcolor, color=key_color, label='', button_type='square', extra_touch_border_scale=0.1, ) self._char_keys.append(btn) h += key_width + 10 # Add delete key at end of third row. if row_num == 2: ba.buttonwidget(parent=self._root_widget, position=(h + 4, v), size=(key_width * 1.8, key_height), autoselect=True, enable_sound=False, repeat=True, textcolor=key_textcolor, color=key_color_dark, label=charstr(SpCh.DELETE), button_type='square', on_activate_call=self._del) v -= (key_height + 9) # Do space bar and stuff. if row_num == 2: if self._num_mode_button is None: self._num_mode_button = ba.buttonwidget( parent=self._root_widget, position=(112, v - 8), size=(key_width * 2, key_height + 5), enable_sound=False, button_type='square', extra_touch_border_scale=0.3, autoselect=True, textcolor=key_textcolor, color=key_color_dark, label='', ) if self._emoji_button is None: self._emoji_button = ba.buttonwidget( parent=self._root_widget, position=(56, v - 8), size=(key_width, key_height + 5), autoselect=True, enable_sound=False, textcolor=key_textcolor, color=key_color_dark, label=charstr(SpCh.LOGO_FLAT), extra_touch_border_scale=0.3, button_type='square', ) btn1 = self._num_mode_button btn2 = ba.buttonwidget(parent=self._root_widget, position=(210, v - 12), size=(key_width * 6.1, key_height + 15), extra_touch_border_scale=0.3, enable_sound=False, autoselect=True, textcolor=key_textcolor, color=key_color_dark, label=ba.Lstr(resource='spaceKeyText'), on_activate_call=ba.Call( self._type_char, ' ')) btn3 = self._emoji_button ba.widget(edit=btn1, right_widget=btn2, left_widget=btn3) ba.widget(edit=btn2, left_widget=btn1, right_widget=self._done_button) ba.widget(edit=btn3, left_widget=btn1) ba.widget(edit=self._done_button, left_widget=btn2) ba.containerwidget(edit=self._root_widget, selected_child=self._char_keys[14]) self._refresh()
def __init__(self, gameclass: 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._gameclass = gameclass 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 = gameclass.get_supported_maps(sessiontype) if not valid_maps: ba.screenmessage(ba.Lstr(resource='noValidMapsErrorText')) raise Exception('No valid maps') self._settings_defs = gameclass.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('exception 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] = {} width = 720 if ba.app.small_ui else 620 x_inset = 50 if ba.app.small_ui else 0 height = (365 if ba.app.small_ui else 460 if ba.app.med_ui 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 ba.app.small_ui else 0 super().__init__(root_widget=ba.containerwidget( size=(width, height + top_extra), transition=transition, scale=( 2.19 if ba.app.small_ui else 1.35 if ba.app.med_ui else 1.0), stack_offset=(0, -17) if ba.app.small_ui 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.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=gameclass.get_display_string(), color=ba.app.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) self._subcontainer = cnt = ba.containerwidget( parent=self._scrollwidget, size=(scroll_width, scroll_height), background=False) # So selection loops through everything and doesn't get stuck in # sub-containers. ba.containerwidget(edit=self._scrollwidget, claims_left_right=True, claims_tab=True, selection_loop_to_parent=True) ba.containerwidget(edit=cnt, claims_left_right=True, claims_tab=True, selection_loop_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): # if 'choices' in setting: 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 instantiate(self, scrollwidget: ba.Widget, tab_button: ba.Widget) -> None: """Create the store.""" # pylint: disable=too-many-locals # pylint: disable=too-many-branches # pylint: disable=too-many-nested-blocks from bastd.ui.store import item as storeitemui title_spacing = 40 button_border = 20 button_spacing = 4 boffs_h = 40 self._height = 80.0 # Calc total height. for i, section in enumerate(self._sections): if section['title'] != '': assert self._height is not None self._height += title_spacing b_width, b_height = section['button_size'] b_column_count = int( math.floor((self._width - boffs_h - 20) / (b_width + button_spacing))) b_row_count = int( math.ceil( float(len(section['items'])) / b_column_count)) b_height_total = ( 2 * button_border + b_row_count * b_height + (b_row_count - 1) * section['v_spacing']) self._height += b_height_total assert self._height is not None cnt2 = ba.containerwidget(parent=scrollwidget, scale=1.0, size=(self._width, self._height), background=False) ba.containerwidget(edit=cnt2, claims_left_right=True, claims_tab=True, selection_loop_to_parent=True) v = self._height - 20 if self._tab == 'characters': txt = ba.Lstr( resource='store.howToSwitchCharactersText', subs=[ ('${SETTINGS}', ba.Lstr( resource='accountSettingsWindow.titleText' )), ('${PLAYER_PROFILES}', ba.Lstr( resource='playerProfilesWindow.titleText') ) ]) ba.textwidget(parent=cnt2, text=txt, size=(0, 0), position=(self._width * 0.5, self._height - 28), h_align='center', v_align='center', color=(0.7, 1, 0.7, 0.4), scale=0.7, shadow=0, flatness=1.0, maxwidth=700, transition_delay=0.4) elif self._tab == 'icons': txt = ba.Lstr( resource='store.howToUseIconsText', subs=[ ('${SETTINGS}', ba.Lstr(resource='mainMenu.settingsText')), ('${PLAYER_PROFILES}', ba.Lstr( resource='playerProfilesWindow.titleText') ) ]) ba.textwidget(parent=cnt2, text=txt, size=(0, 0), position=(self._width * 0.5, self._height - 28), h_align='center', v_align='center', color=(0.7, 1, 0.7, 0.4), scale=0.7, shadow=0, flatness=1.0, maxwidth=700, transition_delay=0.4) elif self._tab == 'maps': assert self._width is not None assert self._height is not None txt = ba.Lstr(resource='store.howToUseMapsText') ba.textwidget(parent=cnt2, text=txt, size=(0, 0), position=(self._width * 0.5, self._height - 28), h_align='center', v_align='center', color=(0.7, 1, 0.7, 0.4), scale=0.7, shadow=0, flatness=1.0, maxwidth=700, transition_delay=0.4) prev_row_buttons: Optional[List] = None this_row_buttons = [] delay = 0.3 for section in self._sections: if section['title'] != '': ba.textwidget( parent=cnt2, position=(60, v - title_spacing * 0.8), size=(0, 0), scale=1.0, transition_delay=delay, color=(0.7, 0.9, 0.7, 1), h_align="left", v_align="center", text=ba.Lstr(resource=section['title']), maxwidth=self._width * 0.7) v -= title_spacing delay = max(0.100, delay - 0.100) v -= button_border b_width, b_height = section['button_size'] b_count = len(section['items']) b_column_count = int( math.floor((self._width - boffs_h - 20) / (b_width + button_spacing))) col = 0 item: Dict[str, Any] assert self._store_window.button_infos is not None for i, item_name in enumerate(section['items']): item = self._store_window.button_infos[ item_name] = {} item['call'] = ba.WeakCall(self._store_window.buy, item_name) if 'x_offs' in section: boffs_h2 = section['x_offs'] else: boffs_h2 = 0 if 'y_offs' in section: boffs_v2 = section['y_offs'] else: boffs_v2 = 0 b_pos = (boffs_h + boffs_h2 + (b_width + button_spacing) * col, v - b_height + boffs_v2) storeitemui.instantiate_store_item_display( item_name, item, parent_widget=cnt2, b_pos=b_pos, boffs_h=boffs_h, b_width=b_width, b_height=b_height, boffs_h2=boffs_h2, boffs_v2=boffs_v2, delay=delay) btn = item['button'] delay = max(0.1, delay - 0.1) this_row_buttons.append(btn) # Wire this button to the equivalent in the # previous row. if prev_row_buttons is not None: # pylint: disable=unsubscriptable-object if len(prev_row_buttons) > col: ba.widget(edit=btn, up_widget=prev_row_buttons[col]) ba.widget(edit=prev_row_buttons[col], down_widget=btn) # If we're the last button in our row, # wire any in the previous row past # our position to go to us if down is # pressed. if (col + 1 == b_column_count or i == b_count - 1): for b_prev in prev_row_buttons[col + 1:]: ba.widget(edit=b_prev, down_widget=btn) else: ba.widget(edit=btn, up_widget=prev_row_buttons[-1]) else: ba.widget(edit=btn, up_widget=tab_button) col += 1 if col == b_column_count or i == b_count - 1: prev_row_buttons = this_row_buttons this_row_buttons = [] col = 0 v -= b_height if i < b_count - 1: v -= section['v_spacing'] v -= button_border # Set a timer to update these buttons periodically as long # as we're alive (so if we buy one it will grey out, etc). self._store_window.update_buttons_timer = ba.Timer( 0.5, ba.WeakCall(self._store_window.update_buttons), repeat=True, timetype=ba.TimeType.REAL) # Also update them immediately. self._store_window.update_buttons()
def on_activate( self, parent_widget: ba.Widget, tab_button: ba.Widget, region_width: float, region_height: float, region_left: float, region_bottom: float, ) -> ba.Widget: c_width = region_width c_height = region_height - 20 self._container = ba.containerwidget( parent=parent_widget, position=(region_left, region_bottom + (region_height - c_height) * 0.5), size=(c_width, c_height), background=False, selection_loops_to_parent=True) v = c_height - 30 self._join_text = ba.textwidget( parent=self._container, position=(c_width * 0.5 - 245, v - 13), color=(0.6, 1.0, 0.6), scale=1.3, size=(200, 30), maxwidth=250, h_align='left', v_align='center', click_activate=True, selectable=True, autoselect=True, on_activate_call=lambda: self._set_sub_tab( SubTabType.JOIN, region_width, region_height, playsound=True, ), text=ba.Lstr(resource='gatherWindow.' 'joinPublicPartyDescriptionText')) self._host_text = ba.textwidget( parent=self._container, position=(c_width * 0.5 + 45, v - 13), color=(0.6, 1.0, 0.6), scale=1.3, size=(200, 30), maxwidth=250, h_align='left', v_align='center', click_activate=True, selectable=True, autoselect=True, on_activate_call=lambda: self._set_sub_tab( SubTabType.HOST, region_width, region_height, playsound=True, ), text=ba.Lstr(resource='gatherWindow.' 'hostPublicPartyDescriptionText')) ba.widget(edit=self._join_text, up_widget=tab_button) ba.widget(edit=self._host_text, left_widget=self._join_text, up_widget=tab_button) ba.widget(edit=self._join_text, right_widget=self._host_text) # Attempt to fetch our local address so we have it for error messages. if self._local_address is None: AddrFetchThread(ba.WeakCall(self._fetch_local_addr_cb)).start() self._set_sub_tab(self._sub_tab, region_width, region_height) self._update_timer = ba.Timer(0.1, ba.WeakCall(self._update), repeat=True, timetype=ba.TimeType.REAL) return self._container
def _build_join_tab(self, region_width: float, region_height: float) -> None: c_width = region_width c_height = region_height - 20 sub_scroll_height = c_height - 125 sub_scroll_width = 830 v = c_height - 35 v -= 60 filter_txt = ba.Lstr(resource='filterText') self._filter_text = ba.textwidget(parent=self._container, text=self._filter_value, size=(350, 45), position=(290, v - 10), h_align='left', v_align='center', editable=True, description=filter_txt) ba.widget(edit=self._filter_text, up_widget=self._join_text) ba.textwidget(text=filter_txt, parent=self._container, size=(0, 0), position=(270, v + 13), maxwidth=150, scale=0.8, color=(0.5, 0.5, 0.5), flatness=1.0, shadow=0.0, h_align='right', v_align='center') ba.textwidget(text=ba.Lstr(resource='nameText'), parent=self._container, size=(0, 0), position=(90, v - 8), maxwidth=60, scale=0.6, color=(0.5, 0.5, 0.5), flatness=1.0, shadow=0.0, h_align='center', v_align='center') ba.textwidget(text=ba.Lstr(resource='gatherWindow.partySizeText'), parent=self._container, size=(0, 0), position=(755, v - 8), maxwidth=60, scale=0.6, color=(0.5, 0.5, 0.5), flatness=1.0, shadow=0.0, h_align='center', v_align='center') ba.textwidget(text=ba.Lstr(resource='gatherWindow.pingText'), parent=self._container, size=(0, 0), position=(825, v - 8), maxwidth=60, scale=0.6, color=(0.5, 0.5, 0.5), flatness=1.0, shadow=0.0, h_align='center', v_align='center') v -= sub_scroll_height + 23 self._host_scrollwidget = scrollw = ba.scrollwidget( parent=self._container, simple_culling_v=10, position=((c_width - sub_scroll_width) * 0.5, v), size=(sub_scroll_width, sub_scroll_height), claims_up_down=False, claims_left_right=True, autoselect=True) self._join_list_column = ba.containerwidget(parent=scrollw, background=False, size=(400, 400), claims_left_right=True) self._join_status_text = ba.textwidget(parent=self._container, text='', size=(0, 0), scale=0.9, flatness=1.0, shadow=0.0, h_align='center', v_align='top', maxwidth=c_width, color=(0.6, 0.6, 0.6), position=(c_width * 0.5, c_height * 0.5))
def __init__(self, position: Tuple[float, float], scale: float = None): # pylint: disable=too-many-locals if scale is None: scale = (2.3 if ba.app.small_ui else 1.65 if ba.app.med_ui else 1.23) self._transitioning_out = False self._width = 450 self._height = (300 if ba.app.small_ui else 370 if ba.app.med_ui else 450) bg_color = (0.5, 0.4, 0.6) # creates our _root_widget popup.PopupWindow.__init__(self, position=position, size=(self._width, self._height), scale=scale, bg_color=bg_color) self._cancel_button = ba.buttonwidget( parent=self.root_widget, position=(50, self._height - 30), size=(50, 50), scale=0.5, label='', color=bg_color, on_activate_call=self._on_cancel_press, autoselect=True, icon=ba.gettexture('crossOut'), iconscale=1.2) achievements = ba.app.achievements num_complete = len([a for a in achievements if a.complete]) txt_final = ba.Lstr( resource='accountSettingsWindow.achievementProgressText', subs=[('${COUNT}', str(num_complete)), ('${TOTAL}', str(len(achievements)))]) self._title_text = ba.textwidget(parent=self.root_widget, position=(self._width * 0.5, self._height - 20), size=(0, 0), h_align='center', v_align='center', scale=0.6, text=txt_final, maxwidth=200, color=(1, 1, 1, 0.4)) self._scrollwidget = ba.scrollwidget(parent=self.root_widget, size=(self._width - 60, self._height - 70), position=(30, 30), capture_arrows=True, simple_culling_v=10) ba.widget(edit=self._scrollwidget, autoselect=True) ba.containerwidget(edit=self.root_widget, cancel_button=self._cancel_button) incr = 36 sub_width = self._width - 90 sub_height = 40 + len(achievements) * incr eq_rsrc = 'coopSelectWindow.powerRankingPointsEqualsText' pts_rsrc = 'coopSelectWindow.powerRankingPointsText' self._subcontainer = ba.containerwidget(parent=self._scrollwidget, size=(sub_width, sub_height), background=False) total_pts = 0 for i, ach in enumerate(achievements): complete = ach.complete ba.textwidget(parent=self._subcontainer, position=(sub_width * 0.08 - 5, sub_height - 20 - incr * i), maxwidth=20, scale=0.5, color=(0.6, 0.6, 0.7) if complete else (0.6, 0.6, 0.7, 0.2), flatness=1.0, shadow=0.0, text=str(i + 1), size=(0, 0), h_align='right', v_align='center') ba.imagewidget(parent=self._subcontainer, position=(sub_width * 0.10 + 1, sub_height - 20 - incr * i - 9) if complete else (sub_width * 0.10 - 4, sub_height - 20 - incr * i - 14), size=(18, 18) if complete else (27, 27), opacity=1.0 if complete else 0.3, color=ach.get_icon_color(complete)[:3], texture=ach.get_icon_texture(complete)) if complete: ba.imagewidget(parent=self._subcontainer, position=(sub_width * 0.10 - 4, sub_height - 25 - incr * i - 9), size=(28, 28), color=(2, 1.4, 0), texture=ba.gettexture('achievementOutline')) ba.textwidget(parent=self._subcontainer, position=(sub_width * 0.19, sub_height - 19 - incr * i + 3), maxwidth=sub_width * 0.62, scale=0.6, flatness=1.0, shadow=0.0, color=(1, 1, 1) if complete else (1, 1, 1, 0.2), text=ach.display_name, size=(0, 0), h_align='left', v_align='center') ba.textwidget(parent=self._subcontainer, position=(sub_width * 0.19, sub_height - 19 - incr * i - 10), maxwidth=sub_width * 0.62, scale=0.4, flatness=1.0, shadow=0.0, color=(0.83, 0.8, 0.85) if complete else (0.8, 0.8, 0.8, 0.2), text=ach.description_full_complete if complete else ach.description_full, size=(0, 0), h_align='left', v_align='center') pts = ach.power_ranking_value ba.textwidget(parent=self._subcontainer, position=(sub_width * 0.92, sub_height - 20 - incr * i), maxwidth=sub_width * 0.15, color=(0.7, 0.8, 1.0) if complete else (0.9, 0.9, 1.0, 0.3), flatness=1.0, shadow=0.0, scale=0.6, text=ba.Lstr(resource=pts_rsrc, subs=[('${NUMBER}', str(pts))]), size=(0, 0), h_align='center', v_align='center') if complete: total_pts += pts ba.textwidget(parent=self._subcontainer, position=(sub_width * 1.0, sub_height - 20 - incr * len(achievements)), maxwidth=sub_width * 0.5, scale=0.7, color=(0.7, 0.8, 1.0), flatness=1.0, shadow=0.0, text=ba.Lstr( value='${A} ${B}', subs=[ ('${A}', ba.Lstr(resource='coopSelectWindow.totalText')), ('${B}', ba.Lstr(resource=eq_rsrc, subs=[('${NUMBER}', str(total_pts))])) ]), size=(0, 0), h_align='right', v_align='center')
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()
def _update_for_league_rank_data(self, data: Optional[Dict[str, Any]]) -> None: # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals if not self._root_widget: return accounts = ba.app.accounts in_top = (data is not None and data['rank'] is not None) eq_text = self._rdict.powerRankingPointsEqualsText pts_txt = self._rdict.powerRankingPointsText num_text = ba.Lstr(resource='numberText').evaluate() do_percent = False finished_season_unranked = False self._can_do_more_button = True extra_text = '' if _ba.get_account_state() != 'signed_in': status_text = '(' + ba.Lstr( resource='notSignedInText').evaluate() + ')' elif in_top: assert data is not None status_text = num_text.replace('${NUMBER}', str(data['rank'])) elif data is not None: try: # handle old seasons where we didn't wind up ranked # at the end.. if not data['scores']: status_text = ( self._rdict.powerRankingFinishedSeasonUnrankedText) extra_text = '' finished_season_unranked = True self._can_do_more_button = False else: our_points = accounts.get_league_rank_points(data) progress = float(our_points) / max(1, data['scores'][-1][1]) status_text = str(int(progress * 100.0)) + '%' extra_text = ( '\n' + self._rdict.powerRankingPointsToRankedText.replace( '${CURRENT}', str(our_points)).replace( '${REMAINING}', str(data['scores'][-1][1]))) do_percent = True except Exception: ba.print_exception('Error updating power ranking.') status_text = self._rdict.powerRankingNotInTopText.replace( '${NUMBER}', str(data['listSize'])) extra_text = '' else: status_text = '-' self._season = data['s'] if data is not None else None v = self._subcontainerheight - 20 popup_was_selected = False if self._season_popup_menu is not None: btn = self._season_popup_menu.get_button() assert self._subcontainer if self._subcontainer.get_selected_child() == btn: popup_was_selected = True btn.delete() season_choices = [] season_choices_display = [] did_first = False self._is_current_season = False if data is not None: # build our list of seasons we have available for ssn in data['sl']: season_choices.append(ssn) if ssn != 'a' and not did_first: season_choices_display.append( ba.Lstr(resource='league.currentSeasonText', subs=[('${NUMBER}', ssn)])) did_first = True # if we either did not specify a season or specified the # first, we're looking at the current.. if self._season in [ssn, None]: self._is_current_season = True elif ssn == 'a': season_choices_display.append( ba.Lstr(resource='league.allTimeText')) else: season_choices_display.append( ba.Lstr(resource='league.seasonText', subs=[('${NUMBER}', ssn)])) assert self._subcontainer self._season_popup_menu = popup_ui.PopupMenu( parent=self._subcontainer, position=(390, v - 45), width=150, button_size=(200, 50), choices=season_choices, on_value_change_call=ba.WeakCall(self._on_season_change), choices_display=season_choices_display, current_choice=self._season) if popup_was_selected: ba.containerwidget( edit=self._subcontainer, selected_child=self._season_popup_menu.get_button()) ba.widget(edit=self._see_more_button, show_buffer_bottom=100) ba.widget(edit=self._season_popup_menu.get_button(), up_widget=self._back_button) ba.widget(edit=self._back_button, down_widget=self._power_ranking_achievements_button, right_widget=self._season_popup_menu.get_button()) ba.textwidget(edit=self._league_title_text, text='' if self._season == 'a' else ba.Lstr( resource='league.leagueText')) if data is None: lname = '' lnum = '' lcolor = (1, 1, 1) self._league_url_arg = '' elif self._season == 'a': lname = ba.Lstr(resource='league.allTimeText').evaluate() lnum = '' lcolor = (1, 1, 1) self._league_url_arg = '' else: lnum = ('[' + str(data['l']['i']) + ']') if data['l']['i2'] else '' lname = ba.Lstr(translate=('leagueNames', data['l']['n'])).evaluate() lcolor = data['l']['c'] self._league_url_arg = (data['l']['n'] + '_' + str(data['l']['i'])).lower() to_end_string: Union[ba.Lstr, str] if data is None or self._season == 'a' or data['se'] is None: to_end_string = '' show_season_end = False else: show_season_end = True days_to_end = data['se'][0] minutes_to_end = data['se'][1] if days_to_end > 0: to_end_string = ba.Lstr(resource='league.seasonEndsDaysText', subs=[('${NUMBER}', str(days_to_end))]) elif days_to_end == 0 and minutes_to_end >= 60: to_end_string = ba.Lstr(resource='league.seasonEndsHoursText', subs=[('${NUMBER}', str(minutes_to_end // 60))]) elif days_to_end == 0 and minutes_to_end >= 0: to_end_string = ba.Lstr( resource='league.seasonEndsMinutesText', subs=[('${NUMBER}', str(minutes_to_end))]) else: to_end_string = ba.Lstr( resource='league.seasonEndedDaysAgoText', subs=[('${NUMBER}', str(-(days_to_end + 1)))]) ba.textwidget(edit=self._season_ends_text, text=to_end_string) ba.textwidget(edit=self._trophy_counts_reset_text, text=ba.Lstr(resource='league.trophyCountsResetText') if self._is_current_season and show_season_end else '') ba.textwidget(edit=self._league_text, text=lname, color=lcolor) l_text_width = min( self._league_text_maxwidth, _ba.get_string_width(lname, suppress_warning=True) * self._league_text_scale) ba.textwidget( edit=self._league_number_text, text=lnum, color=lcolor, position=(self._league_number_base_pos[0] + l_text_width * 0.5 + 8, self._league_number_base_pos[1] + 10)) ba.textwidget( edit=self._to_ranked_text, text=ba.Lstr(resource='coopSelectWindow.toRankedText').evaluate() + '' + extra_text if do_percent else '') ba.textwidget( edit=self._your_power_ranking_text, text=ba.Lstr( resource='rankText', fallback_resource='coopSelectWindow.yourPowerRankingText') if (not do_percent) else '') ba.textwidget(edit=self._power_ranking_rank_text, position=(473, v - 70 - (170 if do_percent else 220)), text=status_text, big=(in_top or do_percent), scale=3.0 if (in_top or do_percent) else 0.7 if finished_season_unranked else 1.0) if self._activity_mult_button is not None: if data is None or data['act'] is None: ba.buttonwidget(edit=self._activity_mult_button, textcolor=(0.7, 0.7, 0.8, 0.5), icon_color=(0.5, 0, 0.5, 0.3)) ba.textwidget(edit=self._activity_mult_text, text=' -') else: ba.buttonwidget(edit=self._activity_mult_button, textcolor=(0.7, 0.7, 0.8, 1.0), icon_color=(0.5, 0, 0.5, 1.0)) # pylint: disable=consider-using-f-string ba.textwidget(edit=self._activity_mult_text, text='x ' + ('%.2f' % data['act'])) have_pro = False if data is None else data['p'] pro_mult = 1.0 + float( _ba.get_account_misc_read_val('proPowerRankingBoost', 0.0)) * 0.01 # pylint: disable=consider-using-f-string ba.textwidget(edit=self._pro_mult_text, text=' -' if (data is None or not have_pro) else 'x ' + ('%.2f' % pro_mult)) ba.buttonwidget(edit=self._pro_mult_button, textcolor=(0.7, 0.7, 0.8, (1.0 if have_pro else 0.5)), icon_color=(0.5, 0, 0.5) if have_pro else (0.5, 0, 0.5, 0.2)) ba.buttonwidget(edit=self._power_ranking_achievements_button, label=('' if data is None else (str(data['a']) + ' ')) + ba.Lstr(resource='achievementsText').evaluate()) # for the achievement value, use the number they gave us for # non-current seasons; otherwise calc our own total_ach_value = 0 for ach in ba.app.ach.achievements: if ach.complete: total_ach_value += ach.power_ranking_value if self._season != 'a' and not self._is_current_season: if data is not None and 'at' in data: total_ach_value = data['at'] ba.textwidget(edit=self._power_ranking_achievement_total_text, text='-' if data is None else ('+ ' + pts_txt.replace('${NUMBER}', str(total_ach_value)))) total_trophies_count = (accounts.get_league_rank_points( data, 'trophyCount')) total_trophies_value = (accounts.get_league_rank_points( data, 'trophies')) ba.buttonwidget(edit=self._power_ranking_trophies_button, label=('' if data is None else (str(total_trophies_count) + ' ')) + ba.Lstr(resource='trophiesText').evaluate()) ba.textwidget( edit=self._power_ranking_trophies_total_text, text='-' if data is None else ('+ ' + pts_txt.replace('${NUMBER}', str(total_trophies_value)))) ba.textwidget( edit=self._power_ranking_total_text, text='-' if data is None else eq_text.replace( '${NUMBER}', str(accounts.get_league_rank_points(data)))) for widget in self._power_ranking_score_widgets: widget.delete() self._power_ranking_score_widgets = [] scores = data['scores'] if data is not None else [] tally_color = (0.5, 0.6, 0.8) w_parent = self._subcontainer v2 = self._power_ranking_score_v for score in scores: h2 = 680 is_us = score[3] self._power_ranking_score_widgets.append( ba.textwidget(parent=w_parent, position=(h2 - 20, v2), size=(0, 0), color=(1, 1, 1) if is_us else (0.6, 0.6, 0.7), maxwidth=40, flatness=1.0, shadow=0.0, text=num_text.replace('${NUMBER}', str(score[0])), h_align='right', v_align='center', scale=0.5)) self._power_ranking_score_widgets.append( ba.textwidget(parent=w_parent, position=(h2 + 20, v2), size=(0, 0), color=(1, 1, 1) if is_us else tally_color, maxwidth=60, text=str(score[1]), flatness=1.0, shadow=0.0, h_align='center', v_align='center', scale=0.7)) txt = ba.textwidget(parent=w_parent, position=(h2 + 60, v2 - (28 * 0.5) / 0.9), size=(210 / 0.9, 28), color=(1, 1, 1) if is_us else (0.6, 0.6, 0.6), maxwidth=210, flatness=1.0, shadow=0.0, autoselect=True, selectable=True, click_activate=True, text=score[2], h_align='left', v_align='center', scale=0.9) self._power_ranking_score_widgets.append(txt) ba.textwidget(edit=txt, on_activate_call=ba.Call(self._show_account_info, score[4], txt)) assert self._season_popup_menu is not None ba.widget(edit=txt, left_widget=self._season_popup_menu.get_button()) v2 -= 28
def __init__(self, transition: str = 'in_right', modal: bool = False, origin_widget: ba.Widget = None): ba.set_analytics_screen('League Rank Window') self._league_rank_data: Optional[Dict[str, Any]] = None self._modal = modal # If they provided an origin-widget, scale up from that. scale_origin: Optional[Tuple[float, float]] if origin_widget is not None: self._transition_out = 'out_scale' scale_origin = origin_widget.get_screen_space_center() transition = 'in_scale' else: self._transition_out = 'out_right' scale_origin = None uiscale = ba.app.ui.uiscale self._width = 1320 if uiscale is ba.UIScale.SMALL else 1120 x_inset = 100 if uiscale is ba.UIScale.SMALL else 0 self._height = (657 if uiscale is ba.UIScale.SMALL else 710 if uiscale is ba.UIScale.MEDIUM else 800) self._r = 'coopSelectWindow' self._rdict = ba.app.lang.get_resource(self._r) top_extra = 20 if uiscale is ba.UIScale.SMALL else 0 self._league_url_arg = '' self._is_current_season = False self._can_do_more_button = True super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height + top_extra), stack_offset=(0, -15) if uiscale is ba.UIScale.SMALL else ( 0, 10) if uiscale is ba.UIScale.MEDIUM else (0, 0), transition=transition, scale_origin_stack_offset=scale_origin, scale=(1.2 if uiscale is ba.UIScale.SMALL else 0.93 if uiscale is ba.UIScale.MEDIUM else 0.8))) self._back_button = btn = ba.buttonwidget( parent=self._root_widget, position=(75 + x_inset, self._height - 87 - (4 if uiscale is ba.UIScale.SMALL else 0)), size=(120, 60), scale=1.2, autoselect=True, label=ba.Lstr(resource='doneText' if self._modal else 'backText'), button_type=None if self._modal else 'back', on_activate_call=self._back) self._title_text = ba.textwidget( parent=self._root_widget, position=(self._width * 0.5, self._height - 56), size=(0, 0), text=ba.Lstr( resource='league.leagueRankText', fallback_resource='coopSelectWindow.powerRankingText'), h_align='center', color=ba.app.ui.title_color, scale=1.4, maxwidth=600, v_align='center') ba.buttonwidget(edit=btn, button_type='backSmall', position=(75 + x_inset, self._height - 87 - (2 if uiscale is ba.UIScale.SMALL else 0)), size=(60, 55), label=ba.charstr(ba.SpecialChar.BACK)) self._scroll_width = self._width - (130 + 2 * x_inset) self._scroll_height = self._height - 160 self._scrollwidget = ba.scrollwidget(parent=self._root_widget, highlight=False, position=(65 + x_inset, 70), size=(self._scroll_width, self._scroll_height), center_small_content=True) ba.widget(edit=self._scrollwidget, autoselect=True) ba.containerwidget(edit=self._scrollwidget, claims_left_right=True) ba.containerwidget(edit=self._root_widget, cancel_button=self._back_button, selected_child=self._back_button) self._last_power_ranking_query_time: Optional[float] = None self._doing_power_ranking_query = False self._subcontainer: Optional[ba.Widget] = None self._subcontainerwidth = 800 self._subcontainerheight = 483 self._power_ranking_score_widgets: List[ba.Widget] = [] self._season_popup_menu: Optional[popup_ui.PopupMenu] = None self._requested_season: Optional[str] = None self._season: Optional[str] = None # take note of our account state; we'll refresh later if this changes self._account_state = _ba.get_account_state() self._refresh() self._restore_state() # if we've got cached power-ranking data already, display it info = ba.app.accounts.get_cached_league_rank_data() if info is not None: self._update_for_league_rank_data(info) self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), timetype=ba.TimeType.REAL, repeat=True) self._update(show=(info is None))
def _refresh(self) -> None: # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals # pylint: disable=cyclic-import from bastd.ui import confirm account_state = _ba.get_v1_account_state() account_type = (_ba.get_v1_account_type() if account_state == 'signed_in' else 'unknown') is_google = account_type == 'Google Play' show_local_signed_in_as = False local_signed_in_as_space = 50.0 show_signed_in_as = self._signed_in signed_in_as_space = 95.0 show_sign_in_benefits = not self._signed_in sign_in_benefits_space = 80.0 show_signing_in_text = account_state == 'signing_in' signing_in_text_space = 80.0 show_google_play_sign_in_button = (account_state == 'signed_out' and 'Google Play' in self._show_sign_in_buttons) show_game_circle_sign_in_button = (account_state == 'signed_out' and 'Game Circle' in self._show_sign_in_buttons) show_device_sign_in_button = (account_state == 'signed_out' and 'Local' in self._show_sign_in_buttons) show_v2_sign_in_button = (account_state == 'signed_out' and 'V2' in self._show_sign_in_buttons) sign_in_button_space = 70.0 show_game_service_button = (self._signed_in and account_type in ['Game Center', 'Game Circle']) game_service_button_space = 60.0 show_linked_accounts_text = (self._signed_in and _ba.get_v1_account_misc_read_val( 'allowAccountLinking2', False)) linked_accounts_text_space = 60.0 show_achievements_button = ( self._signed_in and account_type in ('Google Play', 'Alibaba', 'Local', 'OUYA')) achievements_button_space = 60.0 show_achievements_text = (self._signed_in and not show_achievements_button) achievements_text_space = 27.0 show_leaderboards_button = (self._signed_in and is_google) leaderboards_button_space = 60.0 show_campaign_progress = self._signed_in campaign_progress_space = 27.0 show_tickets = self._signed_in tickets_space = 27.0 show_reset_progress_button = False reset_progress_button_space = 70.0 show_player_profiles_button = self._signed_in player_profiles_button_space = 100.0 show_link_accounts_button = (self._signed_in and _ba.get_v1_account_misc_read_val( 'allowAccountLinking2', False)) link_accounts_button_space = 70.0 show_unlink_accounts_button = show_link_accounts_button unlink_accounts_button_space = 90.0 show_sign_out_button = (self._signed_in and account_type in ['Local', 'Google Play', 'V2']) sign_out_button_space = 70.0 show_cancel_v2_sign_in_button = ( account_state == 'signing_in' and ba.app.accounts_v2.have_primary_credentials()) cancel_v2_sign_in_button_space = 70.0 if self._subcontainer is not None: self._subcontainer.delete() self._sub_height = 60.0 if show_local_signed_in_as: self._sub_height += local_signed_in_as_space if show_signed_in_as: self._sub_height += signed_in_as_space if show_signing_in_text: self._sub_height += signing_in_text_space if show_google_play_sign_in_button: self._sub_height += sign_in_button_space if show_game_circle_sign_in_button: self._sub_height += sign_in_button_space if show_device_sign_in_button: self._sub_height += sign_in_button_space if show_v2_sign_in_button: self._sub_height += sign_in_button_space if show_game_service_button: self._sub_height += game_service_button_space if show_linked_accounts_text: self._sub_height += linked_accounts_text_space if show_achievements_text: self._sub_height += achievements_text_space if show_achievements_button: self._sub_height += achievements_button_space if show_leaderboards_button: self._sub_height += leaderboards_button_space if show_campaign_progress: self._sub_height += campaign_progress_space if show_tickets: self._sub_height += tickets_space if show_sign_in_benefits: self._sub_height += sign_in_benefits_space if show_reset_progress_button: self._sub_height += reset_progress_button_space if show_player_profiles_button: self._sub_height += player_profiles_button_space if show_link_accounts_button: self._sub_height += link_accounts_button_space if show_unlink_accounts_button: self._sub_height += unlink_accounts_button_space if show_sign_out_button: self._sub_height += sign_out_button_space if show_cancel_v2_sign_in_button: self._sub_height += cancel_v2_sign_in_button_space self._subcontainer = ba.containerwidget(parent=self._scrollwidget, size=(self._sub_width, self._sub_height), background=False, claims_left_right=True, claims_tab=True, selection_loops_to_parent=True) first_selectable = None v = self._sub_height - 10.0 if show_local_signed_in_as: v -= local_signed_in_as_space * 0.6 ba.textwidget( parent=self._subcontainer, position=(self._sub_width * 0.5, v), size=(0, 0), text=ba.Lstr( resource='accountSettingsWindow.deviceSpecificAccountText', subs=[('${NAME}', _ba.get_v1_account_display_string())]), scale=0.7, color=(0.5, 0.5, 0.6), maxwidth=self._sub_width * 0.9, flatness=1.0, h_align='center', v_align='center') v -= local_signed_in_as_space * 0.4 self._account_name_text: Optional[ba.Widget] if show_signed_in_as: v -= signed_in_as_space * 0.2 txt = ba.Lstr( resource='accountSettingsWindow.youAreSignedInAsText', fallback_resource='accountSettingsWindow.youAreLoggedInAsText') ba.textwidget(parent=self._subcontainer, position=(self._sub_width * 0.5, v), size=(0, 0), text=txt, scale=0.9, color=ba.app.ui.title_color, maxwidth=self._sub_width * 0.9, h_align='center', v_align='center') v -= signed_in_as_space * 0.4 self._account_name_text = ba.textwidget( parent=self._subcontainer, position=(self._sub_width * 0.5, v), size=(0, 0), scale=1.5, maxwidth=self._sub_width * 0.9, res_scale=1.5, color=(1, 1, 1, 1), h_align='center', v_align='center') self._refresh_account_name_text() v -= signed_in_as_space * 0.4 else: self._account_name_text = None if self._back_button is None: bbtn = _ba.get_special_widget('back_button') else: bbtn = self._back_button if show_sign_in_benefits: v -= sign_in_benefits_space app = ba.app extra: Optional[Union[str, ba.Lstr]] if (app.platform in ['mac', 'ios'] and app.subplatform == 'appstore'): extra = ba.Lstr( value='\n${S}', subs=[('${S}', ba.Lstr(resource='signInWithGameCenterText'))]) else: extra = '' ba.textwidget(parent=self._subcontainer, position=(self._sub_width * 0.5, v + sign_in_benefits_space * 0.4), size=(0, 0), text=ba.Lstr(value='${A}${B}', subs=[('${A}', ba.Lstr(resource=self._r + '.signInInfoText')), ('${B}', extra)]), max_height=sign_in_benefits_space * 0.9, scale=0.9, color=(0.75, 0.7, 0.8), maxwidth=self._sub_width * 0.8, h_align='center', v_align='center') if show_signing_in_text: v -= signing_in_text_space ba.textwidget( parent=self._subcontainer, position=(self._sub_width * 0.5, v + signing_in_text_space * 0.5), size=(0, 0), text=ba.Lstr(resource='accountSettingsWindow.signingInText'), scale=0.9, color=(0, 1, 0), maxwidth=self._sub_width * 0.8, h_align='center', v_align='center') if show_google_play_sign_in_button: button_width = 350 v -= sign_in_button_space self._sign_in_google_play_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v - 20), autoselect=True, size=(button_width, 60), label=ba.Lstr( value='${A}${B}', subs=[('${A}', ba.charstr(ba.SpecialChar.GOOGLE_PLAY_GAMES_LOGO)), ('${B}', ba.Lstr(resource=self._r + '.signInWithGooglePlayText'))]), on_activate_call=lambda: self._sign_in_press('Google Play')) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) ba.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100) self._sign_in_text = None if show_game_circle_sign_in_button: button_width = 350 v -= sign_in_button_space self._sign_in_game_circle_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v - 20), autoselect=True, size=(button_width, 60), label=ba.Lstr(value='${A}${B}', subs=[('${A}', ba.charstr( ba.SpecialChar.GAME_CIRCLE_LOGO)), ('${B}', ba.Lstr(resource=self._r + '.signInWithGameCircleText'))]), on_activate_call=lambda: self._sign_in_press('Game Circle')) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) ba.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100) self._sign_in_text = None if show_v2_sign_in_button: button_width = 350 v -= sign_in_button_space self._sign_in_v2_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v - 20), autoselect=True, size=(button_width, 60), label='', on_activate_call=self._v2_sign_in_press) ba.textwidget( parent=self._subcontainer, draw_controller=btn, h_align='center', v_align='center', size=(0, 0), position=(self._sub_width * 0.5, v + 17), text=ba.Lstr( value='${A}${B}', subs=[('${A}', ba.charstr(ba.SpecialChar.V2_LOGO)), ('${B}', ba.Lstr(resource=self._r + '.signInWithV2Text'))]), maxwidth=button_width * 0.8, color=(0.75, 1.0, 0.7)) ba.textwidget(parent=self._subcontainer, draw_controller=btn, h_align='center', v_align='center', size=(0, 0), position=(self._sub_width * 0.5, v - 4), text=ba.Lstr(resource=self._r + '.signInWithV2InfoText'), flatness=1.0, scale=0.57, maxwidth=button_width * 0.9, color=(0.55, 0.8, 0.5)) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) ba.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100) self._sign_in_text = None if show_device_sign_in_button: button_width = 350 v -= sign_in_button_space self._sign_in_device_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v - 20), autoselect=True, size=(button_width, 60), label='', on_activate_call=lambda: self._sign_in_press('Local')) ba.textwidget(parent=self._subcontainer, draw_controller=btn, h_align='center', v_align='center', size=(0, 0), position=(self._sub_width * 0.5, v + 17), text=ba.Lstr( value='${A}${B}', subs=[('${A}', ba.charstr(ba.SpecialChar.LOCAL_ACCOUNT)), ('${B}', ba.Lstr(resource=self._r + '.signInWithDeviceText'))]), maxwidth=button_width * 0.8, color=(0.75, 1.0, 0.7)) ba.textwidget(parent=self._subcontainer, draw_controller=btn, h_align='center', v_align='center', size=(0, 0), position=(self._sub_width * 0.5, v - 4), text=ba.Lstr(resource=self._r + '.signInWithDeviceInfoText'), flatness=1.0, scale=0.57, maxwidth=button_width * 0.9, color=(0.55, 0.8, 0.5)) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) ba.widget(edit=btn, show_buffer_bottom=40, show_buffer_top=100) self._sign_in_text = None if show_player_profiles_button: button_width = 300 v -= player_profiles_button_space self._player_profiles_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v + 30), autoselect=True, size=(button_width, 60), label=ba.Lstr(resource='playerProfilesWindow.titleText'), color=(0.55, 0.5, 0.6), icon=ba.gettexture('cuteSpaz'), textcolor=(0.75, 0.7, 0.8), on_activate_call=self._player_profiles_press) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=0) # the button to go to OS-Specific leaderboards/high-score-lists/etc. if show_game_service_button: button_width = 300 v -= game_service_button_space * 0.85 account_type = _ba.get_v1_account_type() if account_type == 'Game Center': account_type_name = ba.Lstr(resource='gameCenterText') elif account_type == 'Game Circle': account_type_name = ba.Lstr(resource='gameCircleText') else: raise ValueError("unknown account type: '" + str(account_type) + "'") self._game_service_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v), color=(0.55, 0.5, 0.6), textcolor=(0.75, 0.7, 0.8), autoselect=True, on_activate_call=_ba.show_online_score_ui, size=(button_width, 50), label=account_type_name) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) v -= game_service_button_space * 0.15 else: self.game_service_button = None self._achievements_text: Optional[ba.Widget] if show_achievements_text: v -= achievements_text_space * 0.5 self._achievements_text = ba.textwidget( parent=self._subcontainer, position=(self._sub_width * 0.5, v), size=(0, 0), scale=0.9, color=(0.75, 0.7, 0.8), maxwidth=self._sub_width * 0.8, h_align='center', v_align='center') v -= achievements_text_space * 0.5 else: self._achievements_text = None self._achievements_button: Optional[ba.Widget] if show_achievements_button: button_width = 300 v -= achievements_button_space * 0.85 self._achievements_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v), color=(0.55, 0.5, 0.6), textcolor=(0.75, 0.7, 0.8), autoselect=True, icon=ba.gettexture('googlePlayAchievementsIcon' if is_google else 'achievementsIcon'), icon_color=(0.8, 0.95, 0.7) if is_google else (0.85, 0.8, 0.9), on_activate_call=self._on_achievements_press, size=(button_width, 50), label='') if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) v -= achievements_button_space * 0.15 else: self._achievements_button = None if show_achievements_text or show_achievements_button: self._refresh_achievements() self._leaderboards_button: Optional[ba.Widget] if show_leaderboards_button: button_width = 300 v -= leaderboards_button_space * 0.85 self._leaderboards_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v), color=(0.55, 0.5, 0.6), textcolor=(0.75, 0.7, 0.8), autoselect=True, icon=ba.gettexture('googlePlayLeaderboardsIcon'), icon_color=(0.8, 0.95, 0.7), on_activate_call=self._on_leaderboards_press, size=(button_width, 50), label=ba.Lstr(resource='leaderboardsText')) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) v -= leaderboards_button_space * 0.15 else: self._leaderboards_button = None self._campaign_progress_text: Optional[ba.Widget] if show_campaign_progress: v -= campaign_progress_space * 0.5 self._campaign_progress_text = ba.textwidget( parent=self._subcontainer, position=(self._sub_width * 0.5, v), size=(0, 0), scale=0.9, color=(0.75, 0.7, 0.8), maxwidth=self._sub_width * 0.8, h_align='center', v_align='center') v -= campaign_progress_space * 0.5 self._refresh_campaign_progress_text() else: self._campaign_progress_text = None self._tickets_text: Optional[ba.Widget] if show_tickets: v -= tickets_space * 0.5 self._tickets_text = ba.textwidget(parent=self._subcontainer, position=(self._sub_width * 0.5, v), size=(0, 0), scale=0.9, color=(0.75, 0.7, 0.8), maxwidth=self._sub_width * 0.8, flatness=1.0, h_align='center', v_align='center') v -= tickets_space * 0.5 self._refresh_tickets_text() else: self._tickets_text = None # bit of spacing before the reset/sign-out section v -= 5 button_width = 250 if show_reset_progress_button: confirm_text = (ba.Lstr(resource=self._r + '.resetProgressConfirmText') if self._can_reset_achievements else ba.Lstr( resource=self._r + '.resetProgressConfirmNoAchievementsText')) v -= reset_progress_button_space self._reset_progress_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v), color=(0.55, 0.5, 0.6), textcolor=(0.75, 0.7, 0.8), autoselect=True, size=(button_width, 60), label=ba.Lstr(resource=self._r + '.resetProgressText'), on_activate_call=lambda: confirm.ConfirmWindow( text=confirm_text, width=500, height=200, action=self._reset_progress)) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn) self._linked_accounts_text: Optional[ba.Widget] if show_linked_accounts_text: v -= linked_accounts_text_space * 0.8 self._linked_accounts_text = ba.textwidget( parent=self._subcontainer, position=(self._sub_width * 0.5, v), size=(0, 0), scale=0.9, color=(0.75, 0.7, 0.8), maxwidth=self._sub_width * 0.95, h_align='center', v_align='center') v -= linked_accounts_text_space * 0.2 self._update_linked_accounts_text() else: self._linked_accounts_text = None if show_link_accounts_button: v -= link_accounts_button_space self._link_accounts_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v), autoselect=True, size=(button_width, 60), label='', color=(0.55, 0.5, 0.6), on_activate_call=self._link_accounts_press) ba.textwidget(parent=self._subcontainer, draw_controller=btn, h_align='center', v_align='center', size=(0, 0), position=(self._sub_width * 0.5, v + 17 + 20), text=ba.Lstr(resource=self._r + '.linkAccountsText'), maxwidth=button_width * 0.8, color=(0.75, 0.7, 0.8)) ba.textwidget(parent=self._subcontainer, draw_controller=btn, h_align='center', v_align='center', size=(0, 0), position=(self._sub_width * 0.5, v - 4 + 20), text=ba.Lstr(resource=self._r + '.linkAccountsInfoText'), flatness=1.0, scale=0.5, maxwidth=button_width * 0.8, color=(0.75, 0.7, 0.8)) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=50) self._unlink_accounts_button: Optional[ba.Widget] if show_unlink_accounts_button: v -= unlink_accounts_button_space self._unlink_accounts_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v + 25), autoselect=True, size=(button_width, 60), label='', color=(0.55, 0.5, 0.6), on_activate_call=self._unlink_accounts_press) self._unlink_accounts_button_label = ba.textwidget( parent=self._subcontainer, draw_controller=btn, h_align='center', v_align='center', size=(0, 0), position=(self._sub_width * 0.5, v + 55), text=ba.Lstr(resource=self._r + '.unlinkAccountsText'), maxwidth=button_width * 0.8, color=(0.75, 0.7, 0.8)) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=50) self._update_unlink_accounts_button() else: self._unlink_accounts_button = None if show_sign_out_button: v -= sign_out_button_space self._sign_out_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v), size=(button_width, 60), label=ba.Lstr(resource=self._r + '.signOutText'), color=(0.55, 0.5, 0.6), textcolor=(0.75, 0.7, 0.8), autoselect=True, on_activate_call=self._sign_out_press) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=15) if show_cancel_v2_sign_in_button: v -= cancel_v2_sign_in_button_space self._cancel_v2_sign_in_button = btn = ba.buttonwidget( parent=self._subcontainer, position=((self._sub_width - button_width) * 0.5, v), size=(button_width, 60), label=ba.Lstr(resource='cancelText'), color=(0.55, 0.5, 0.6), textcolor=(0.75, 0.7, 0.8), autoselect=True, on_activate_call=self._cancel_v2_sign_in_press) if first_selectable is None: first_selectable = btn if ba.app.ui.use_toolbars: ba.widget(edit=btn, right_widget=_ba.get_special_widget('party_button')) ba.widget(edit=btn, left_widget=bbtn, show_buffer_bottom=15) # Whatever the topmost selectable thing is, we want it to scroll all # the way up when we select it. if first_selectable is not None: ba.widget(edit=first_selectable, up_widget=bbtn, show_buffer_top=400) # (this should re-scroll us to the top..) ba.containerwidget(edit=self._subcontainer, visible_child=first_selectable) self._needs_refresh = False
def __init__(self, transition: str = 'in_right', in_main_menu: bool = True, selected_profile: str = None, origin_widget: ba.Widget = None): # pylint: disable=too-many-statements # pylint: disable=too-many-locals from ba.internal import ensure_have_account_player_profile self._in_main_menu = in_main_menu if self._in_main_menu: back_label = ba.Lstr(resource='backText') else: back_label = ba.Lstr(resource='doneText') self._width = 700.0 if ba.app.small_ui else 600.0 x_inset = 50.0 if ba.app.small_ui else 0.0 self._height = (360.0 if ba.app.small_ui else 385.0 if ba.app.med_ui else 410.0) # If we're being called up standalone, handle pause/resume ourself. if not self._in_main_menu: ba.app.pause() # If they provided an origin-widget, scale up from that. scale_origin: Optional[Tuple[float, float]] if origin_widget is not None: self._transition_out = 'out_scale' scale_origin = origin_widget.get_screen_space_center() transition = 'in_scale' else: self._transition_out = 'out_right' scale_origin = None self._r = 'playerProfilesWindow' # Ensure we've got an account-profile in cases where we're signed in. ensure_have_account_player_profile() top_extra = 20 if ba.app.small_ui else 0 super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height + top_extra), transition=transition, scale_origin_stack_offset=scale_origin, scale=(2.2 if ba.app.small_ui else 1.6 if ba.app.med_ui else 1.0), stack_offset=(0, -14) if ba.app.small_ui else (0, 0))) self._back_button = btn = ba.buttonwidget( parent=self._root_widget, position=(40 + x_inset, self._height - 59), size=(120, 60), scale=0.8, label=back_label, button_type='back' if self._in_main_menu else None, autoselect=True, on_activate_call=self._back) ba.containerwidget(edit=self._root_widget, cancel_button=btn) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height - 36), size=(0, 0), text=ba.Lstr(resource=self._r + '.titleText'), maxwidth=300, color=ba.app.title_color, scale=0.9, h_align="center", v_align="center") if self._in_main_menu: ba.buttonwidget(edit=btn, button_type='backSmall', size=(60, 60), label=ba.charstr(ba.SpecialChar.BACK)) scroll_height = self._height - 140.0 self._scroll_width = self._width - (188 + x_inset * 2) v = self._height - 84.0 h = 50 + x_inset b_color = (0.6, 0.53, 0.63) scl = (1.055 if ba.app.small_ui else 1.18 if ba.app.med_ui else 1.3) v -= 70.0 * scl self._new_button = ba.buttonwidget(parent=self._root_widget, position=(h, v), size=(80, 66.0 * scl), on_activate_call=self._new_profile, color=b_color, button_type='square', autoselect=True, textcolor=(0.75, 0.7, 0.8), text_scale=0.7, label=ba.Lstr(resource=self._r + '.newButtonText')) v -= 70.0 * scl self._edit_button = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(80, 66.0 * scl), on_activate_call=self._edit_profile, color=b_color, button_type='square', autoselect=True, textcolor=(0.75, 0.7, 0.8), text_scale=0.7, label=ba.Lstr(resource=self._r + '.editButtonText')) v -= 70.0 * scl self._delete_button = ba.buttonwidget( parent=self._root_widget, position=(h, v), size=(80, 66.0 * scl), on_activate_call=self._delete_profile, color=b_color, button_type='square', autoselect=True, textcolor=(0.75, 0.7, 0.8), text_scale=0.7, label=ba.Lstr(resource=self._r + '.deleteButtonText')) v = self._height - 87 ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height - 71), size=(0, 0), text=ba.Lstr(resource=self._r + '.explanationText'), color=ba.app.infotextcolor, maxwidth=self._width * 0.83, scale=0.6, h_align="center", v_align="center") self._scrollwidget = ba.scrollwidget(parent=self._root_widget, highlight=False, position=(140 + x_inset, v - scroll_height), size=(self._scroll_width, scroll_height)) ba.widget(edit=self._scrollwidget, autoselect=True, left_widget=self._new_button) ba.containerwidget(edit=self._root_widget, selected_child=self._scrollwidget) self._columnwidget = ba.columnwidget(parent=self._scrollwidget) v -= 255 self._profiles: Optional[Dict[str, Dict[str, Any]]] = None self._selected_profile = selected_profile self._profile_widgets: List[ba.Widget] = [] self._refresh() self._restore_state()
def _build_host_tab(self, region_width: float, region_height: float) -> None: c_width = region_width c_height = region_height - 20 v = c_height - 35 v -= 25 is_public_enabled = _ba.get_public_party_enabled() v -= 30 party_name_text = ba.Lstr( resource='gatherWindow.partyNameText', fallback_resource='editGameListWindow.nameText') ba.textwidget(parent=self._container, size=(0, 0), h_align='right', v_align='center', maxwidth=200, scale=0.8, color=ba.app.ui.infotextcolor, position=(210, v - 9), text=party_name_text) self._host_name_text = ba.textwidget(parent=self._container, editable=True, size=(535, 40), position=(230, v - 30), text=ba.app.config.get( 'Public Party Name', ''), maxwidth=494, shadow=0.3, flatness=1.0, description=party_name_text, autoselect=True, v_align='center', corner_scale=1.0) v -= 60 ba.textwidget(parent=self._container, size=(0, 0), h_align='right', v_align='center', maxwidth=200, scale=0.8, color=ba.app.ui.infotextcolor, position=(210, v - 9), text=ba.Lstr(resource='maxPartySizeText', fallback_resource='maxConnectionsText')) self._host_max_party_size_value = ba.textwidget( parent=self._container, size=(0, 0), h_align='center', v_align='center', scale=1.2, color=(1, 1, 1), position=(240, v - 9), text=str(_ba.get_public_party_max_size())) btn1 = self._host_max_party_size_minus_button = (ba.buttonwidget( parent=self._container, size=(40, 40), on_activate_call=ba.WeakCall( self._on_max_public_party_size_minus_press), position=(280, v - 26), label='-', autoselect=True)) btn2 = self._host_max_party_size_plus_button = (ba.buttonwidget( parent=self._container, size=(40, 40), on_activate_call=ba.WeakCall( self._on_max_public_party_size_plus_press), position=(350, v - 26), label='+', autoselect=True)) v -= 50 v -= 70 if is_public_enabled: label = ba.Lstr( resource='gatherWindow.makePartyPrivateText', fallback_resource='gatherWindow.stopAdvertisingText') else: label = ba.Lstr( resource='gatherWindow.makePartyPublicText', fallback_resource='gatherWindow.startAdvertisingText') self._host_toggle_button = ba.buttonwidget( parent=self._container, label=label, size=(400, 80), on_activate_call=self._on_stop_advertising_press if is_public_enabled else self._on_start_advertizing_press, position=(c_width * 0.5 - 200, v), autoselect=True, up_widget=btn2) ba.widget(edit=self._host_name_text, down_widget=btn2) ba.widget(edit=btn2, up_widget=self._host_name_text) ba.widget(edit=btn1, up_widget=self._host_name_text) ba.widget(edit=self._join_text, down_widget=self._host_name_text) v -= 10 self._host_status_text = ba.textwidget( parent=self._container, text=ba.Lstr(resource='gatherWindow.' 'partyStatusNotPublicText'), size=(0, 0), scale=0.7, flatness=1.0, shadow=0.0, h_align='center', v_align='top', maxwidth=c_width, color=(0.6, 0.6, 0.6), position=(c_width * 0.5, v)) v -= 90 ba.textwidget( parent=self._container, text=ba.Lstr(resource='gatherWindow.dedicatedServerInfoText'), size=(0, 0), scale=0.7, flatness=1.0, shadow=0.0, h_align='center', v_align='center', maxwidth=c_width * 0.9, color=ba.app.ui.infotextcolor, position=(c_width * 0.5, v)) # If public sharing is already on, # launch a status-check immediately. if _ba.get_public_party_enabled(): self._do_status_check()
def __init__(self, parent: PartyQueueWindow, distance: float, initial_offset: float, is_player: bool, account_id: str, name: str): self.claimed = False self._line_left = parent.get_line_left() self._line_width = parent.get_line_width() self._line_bottom = parent.get_line_bottom() self._target_distance = distance self._distance = distance + initial_offset self._boost_brightness = 0.0 self._debug = False self._sc = sc = 1.1 if is_player else 0.6 + random.random() * 0.2 self._y_offs = -30.0 if is_player else -47.0 * sc self._last_boost_time = 0.0 self._color = (0.2, 1.0, 0.2) if is_player else (0.5 + 0.3 * random.random(), 0.4 + 0.2 * random.random(), 0.5 + 0.3 * random.random()) self._eye_color = (0.7 * 1.0 + 0.3 * self._color[0], 0.7 * 1.0 + 0.3 * self._color[1], 0.7 * 1.0 + 0.3 * self._color[2]) self._body_image = ba.buttonwidget( parent=parent.get_root_widget(), selectable=True, label='', size=(sc * 60, sc * 80), color=self._color, texture=parent.lineup_tex, model_transparent=parent.lineup_1_transparent_model) ba.buttonwidget(edit=self._body_image, on_activate_call=ba.WeakCall( parent.on_account_press, account_id, self._body_image)) ba.widget(edit=self._body_image, autoselect=True) self._eyes_image = ba.imagewidget( parent=parent.get_root_widget(), size=(sc * 36, sc * 18), texture=parent.lineup_tex, color=self._eye_color, model_transparent=parent.eyes_model) self._name_text = ba.textwidget(parent=parent.get_root_widget(), size=(0, 0), shadow=0, flatness=1.0, text=name, maxwidth=100, h_align='center', v_align='center', scale=0.75, color=(1, 1, 1, 0.6)) self._update_image() # DEBUG: vis target pos.. self._body_image_target: Optional[ba.Widget] self._eyes_image_target: Optional[ba.Widget] if self._debug: self._body_image_target = ba.imagewidget( parent=parent.get_root_widget(), size=(sc * 60, sc * 80), color=self._color, texture=parent.lineup_tex, model_transparent=parent.lineup_1_transparent_model) self._eyes_image_target = ba.imagewidget( parent=parent.get_root_widget(), size=(sc * 36, sc * 18), texture=parent.lineup_tex, color=self._eye_color, model_transparent=parent.eyes_model) # (updates our image positions) self.set_target_distance(self._target_distance) else: self._body_image_target = self._eyes_image_target = None
def update(self, index: int, party: PartyEntry, sub_scroll_width: float, sub_scroll_height: float, lineheight: float, columnwidget: ba.Widget, join_text: ba.Widget, filter_text: ba.Widget, existing_selection: Optional[Selection], tab: PublicGatherTab) -> None: """Update for the given data.""" # pylint: disable=too-many-locals # Quick-out: if we've been marked clean for a certain index and # we're still at that index, we're done. if party.clean_display_index == index: return ping_good = _ba.get_account_misc_read_val('pingGood', 100) ping_med = _ba.get_account_misc_read_val('pingMed', 500) self._clear() hpos = 20 vpos = sub_scroll_height - lineheight * index - 50 self._name_widget = ba.textwidget( text=ba.Lstr(value=party.name), parent=columnwidget, size=(sub_scroll_width * 0.63, 20), position=(0 + hpos, 4 + vpos), selectable=True, on_select_call=ba.WeakCall( tab.set_public_party_selection, Selection(party.get_key(), SelectionComponent.NAME)), on_activate_call=ba.WeakCall(tab.on_public_party_activate, party), click_activate=True, maxwidth=sub_scroll_width * 0.45, corner_scale=1.4, autoselect=True, color=(1, 1, 1, 0.3 if party.ping is None else 1.0), h_align='left', v_align='center') ba.widget(edit=self._name_widget, left_widget=join_text, show_buffer_top=64.0, show_buffer_bottom=64.0) if existing_selection == Selection(party.get_key(), SelectionComponent.NAME): ba.containerwidget(edit=columnwidget, selected_child=self._name_widget) if party.stats_addr: url = party.stats_addr.replace( '${ACCOUNT}', _ba.get_account_misc_read_val_2('resolvedAccountID', 'UNKNOWN')) self._stats_button = ba.buttonwidget( color=(0.3, 0.6, 0.94), textcolor=(1.0, 1.0, 1.0), label=ba.Lstr(resource='statsText'), parent=columnwidget, autoselect=True, on_activate_call=ba.Call(ba.open_url, url), on_select_call=ba.WeakCall( tab.set_public_party_selection, Selection(party.get_key(), SelectionComponent.STATS_BUTTON)), size=(120, 40), position=(sub_scroll_width * 0.66 + hpos, 1 + vpos), scale=0.9) if existing_selection == Selection( party.get_key(), SelectionComponent.STATS_BUTTON): ba.containerwidget(edit=columnwidget, selected_child=self._stats_button) self._size_widget = ba.textwidget( text=str(party.size) + '/' + str(party.size_max), parent=columnwidget, size=(0, 0), position=(sub_scroll_width * 0.86 + hpos, 20 + vpos), scale=0.7, color=(0.8, 0.8, 0.8), h_align='right', v_align='center') if index == 0: ba.widget(edit=self._name_widget, up_widget=filter_text) if self._stats_button: ba.widget(edit=self._stats_button, up_widget=filter_text) self._ping_widget = ba.textwidget(parent=columnwidget, size=(0, 0), position=(sub_scroll_width * 0.94 + hpos, 20 + vpos), scale=0.7, h_align='right', v_align='center') if party.ping is None: ba.textwidget(edit=self._ping_widget, text='-', color=(0.5, 0.5, 0.5)) else: ba.textwidget(edit=self._ping_widget, text=str(int(party.ping)), color=(0, 1, 0) if party.ping <= ping_good else (1, 1, 0) if party.ping <= ping_med else (1, 0, 0)) party.clean_display_index = index
def __init__(self, transition: str = 'in_right', modal: bool = False, show_tab: str = None, on_close_call: Callable[[], Any] = None, back_location: str = None, origin_widget: ba.Widget = None): # pylint: disable=too-many-statements # pylint: disable=too-many-locals from bastd.ui import tabs from ba import SpecialChar app = ba.app ba.set_analytics_screen('Store Window') scale_origin: Optional[Tuple[float, float]] # If they provided an origin-widget, scale up from that. if origin_widget is not None: self._transition_out = 'out_scale' scale_origin = origin_widget.get_screen_space_center() transition = 'in_scale' else: self._transition_out = 'out_right' scale_origin = None self.button_infos: Optional[Dict[str, Dict[str, Any]]] = None self.update_buttons_timer: Optional[ba.Timer] = None self._status_textwidget_update_timer = None self._back_location = back_location self._on_close_call = on_close_call self._show_tab = show_tab self._modal = modal self._width = 1240 if app.small_ui else 1040 self._x_inset = x_inset = 100 if app.small_ui else 0 self._height = (578 if app.small_ui else 645 if app.med_ui else 800) self._current_tab: Optional[str] = None extra_top = 30 if app.small_ui else 0 self._request: Any = None self._r = 'store' self._last_buy_time: Optional[float] = None super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height + extra_top), transition=transition, toolbar_visibility='menu_full', scale=1.3 if app.small_ui else 0.9 if app.med_ui else 0.8, scale_origin_stack_offset=scale_origin, stack_offset=(0, -5) if app.small_ui else (0, 0) if app.med_ui else (0, 0))) self._back_button = btn = ba.buttonwidget( parent=self._root_widget, position=(70 + x_inset, self._height - 74), size=(140, 60), scale=1.1, autoselect=True, label=ba.Lstr(resource='doneText' if self._modal else 'backText'), button_type=None if self._modal else 'back', on_activate_call=self._back) ba.containerwidget(edit=self._root_widget, cancel_button=btn) self._get_tickets_button = ba.buttonwidget( parent=self._root_widget, size=(210, 65), on_activate_call=self._on_get_more_tickets_press, autoselect=True, scale=0.9, text_scale=1.4, left_widget=self._back_button, color=(0.7, 0.5, 0.85), textcolor=(0.2, 1.0, 0.2), label=ba.Lstr(resource='getTicketsWindow.titleText')) # Move this dynamically to keep it out of the way of the party icon. self._update_get_tickets_button_pos() self._get_ticket_pos_update_timer = ba.Timer( 1.0, ba.WeakCall(self._update_get_tickets_button_pos), repeat=True, timetype=ba.TimeType.REAL) ba.widget(edit=self._back_button, right_widget=self._get_tickets_button) self._ticket_text_update_timer = ba.Timer( 1.0, ba.WeakCall(self._update_tickets_text), timetype=ba.TimeType.REAL, repeat=True) self._update_tickets_text() app = ba.app if app.platform in ['mac', 'ios'] and app.subplatform == 'appstore': ba.buttonwidget( parent=self._root_widget, position=(self._width * 0.5 - 70, 16), size=(230, 50), scale=0.65, on_activate_call=ba.WeakCall(self._restore_purchases), color=(0.35, 0.3, 0.4), selectable=False, textcolor=(0.55, 0.5, 0.6), label=ba.Lstr( resource='getTicketsWindow.restorePurchasesText')) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height - 44), size=(0, 0), color=app.title_color, scale=1.5, h_align="center", v_align="center", text=ba.Lstr(resource='storeText'), maxwidth=420) if not self._modal: ba.buttonwidget(edit=self._back_button, button_type='backSmall', size=(60, 60), label=ba.charstr(SpecialChar.BACK)) scroll_buffer_h = 130 + 2 * x_inset tab_buffer_h = 250 + 2 * x_inset tabs_def = [ ('extras', ba.Lstr(resource=self._r + '.extrasText')), ('maps', ba.Lstr(resource=self._r + '.mapsText')), ('minigames', ba.Lstr(resource=self._r + '.miniGamesText')), ('characters', ba.Lstr(resource=self._r + '.charactersText')), ('icons', ba.Lstr(resource=self._r + '.iconsText')), ] tab_results = tabs.create_tab_buttons(self._root_widget, tabs_def, pos=(tab_buffer_h * 0.5, self._height - 130), size=(self._width - tab_buffer_h, 50), on_select_call=self._set_tab, return_extra_info=True) self._purchasable_count_widgets: Dict[str, Dict[str, Any]] = {} # Create our purchasable-items tags and have them update over time. for i, tab in enumerate(tabs_def): pos = tab_results['positions'][i] size = tab_results['sizes'][i] button = tab_results['buttons_indexed'][i] rad = 10 center = (pos[0] + 0.1 * size[0], pos[1] + 0.9 * size[1]) img = ba.imagewidget(parent=self._root_widget, position=(center[0] - rad * 1.04, center[1] - rad * 1.15), size=(rad * 2.2, rad * 2.2), texture=ba.gettexture('circleShadow'), color=(1, 0, 0)) txt = ba.textwidget(parent=self._root_widget, position=center, size=(0, 0), h_align='center', v_align='center', maxwidth=1.4 * rad, scale=0.6, shadow=1.0, flatness=1.0) rad = 20 sale_img = ba.imagewidget(parent=self._root_widget, position=(center[0] - rad, center[1] - rad), size=(rad * 2, rad * 2), draw_controller=button, texture=ba.gettexture('circleZigZag'), color=(0.5, 0, 1.0)) sale_title_text = ba.textwidget(parent=self._root_widget, position=(center[0], center[1] + 0.24 * rad), size=(0, 0), h_align='center', v_align='center', draw_controller=button, maxwidth=1.4 * rad, scale=0.6, shadow=0.0, flatness=1.0, color=(0, 1, 0)) sale_time_text = ba.textwidget(parent=self._root_widget, position=(center[0], center[1] - 0.29 * rad), size=(0, 0), h_align='center', v_align='center', draw_controller=button, maxwidth=1.4 * rad, scale=0.4, shadow=0.0, flatness=1.0, color=(0, 1, 0)) self._purchasable_count_widgets[tab[0]] = { 'img': img, 'text': txt, 'sale_img': sale_img, 'sale_title_text': sale_title_text, 'sale_time_text': sale_time_text } self._tab_update_timer = ba.Timer(1.0, ba.WeakCall(self._update_tabs), timetype=ba.TimeType.REAL, repeat=True) self._update_tabs() self._tab_buttons = tab_results['buttons'] if self._get_tickets_button is not None: last_tab_button = self._tab_buttons[tabs_def[-1][0]] ba.widget(edit=self._get_tickets_button, down_widget=last_tab_button) ba.widget(edit=last_tab_button, up_widget=self._get_tickets_button, right_widget=self._get_tickets_button) self._scroll_width = self._width - scroll_buffer_h self._scroll_height = self._height - 180 self._scrollwidget: Optional[ba.Widget] = None self._status_textwidget: Optional[ba.Widget] = None self._restore_state()
def __init__(self, transition: str = 'in_right', origin_widget: ba.Widget = None): # pylint: disable=too-many-statements # pylint: disable=too-many-locals import threading # Preload some modules we use in a background thread so we won't # have a visual hitch when the user taps them. threading.Thread(target=self._preload_modules).start() # We can currently be used either for main menu duty or for selecting # playlists (should make this more elegant/general). self._is_main_menu = not ba.app.ui.selecting_private_party_playlist uiscale = ba.app.ui.uiscale width = 1000 if uiscale is ba.UIScale.SMALL else 800 x_offs = 100 if uiscale is ba.UIScale.SMALL else 0 height = 550 button_width = 400 scale_origin: Optional[tuple[float, float]] if origin_widget is not None: self._transition_out = 'out_scale' scale_origin = origin_widget.get_screen_space_center() transition = 'in_scale' else: self._transition_out = 'out_right' scale_origin = None self._r = 'playWindow' super().__init__(root_widget=ba.containerwidget( size=(width, height), transition=transition, toolbar_visibility='menu_full', scale_origin_stack_offset=scale_origin, scale=(1.6 if uiscale is ba.UIScale.SMALL else 0.9 if uiscale is ba.UIScale.MEDIUM else 0.8), stack_offset=(0, 0) if uiscale is ba.UIScale.SMALL else (0, 0))) self._back_button = back_button = btn = ba.buttonwidget( parent=self._root_widget, position=(55 + x_offs, height - 132), size=(120, 60), scale=1.1, text_res_scale=1.5, text_scale=1.2, autoselect=True, label=ba.Lstr(resource='backText'), button_type='back') txt = ba.textwidget( parent=self._root_widget, position=(width * 0.5, height - 101), # position=(width * 0.5, height - # (101 if main_menu else 61)), size=(0, 0), text=ba.Lstr(resource=( self._r + '.titleText') if self._is_main_menu else 'playlistsText'), scale=1.7, res_scale=2.0, maxwidth=400, color=ba.app.ui.heading_color, h_align='center', v_align='center') ba.buttonwidget(edit=btn, button_type='backSmall', size=(60, 60), label=ba.charstr(ba.SpecialChar.BACK)) if ba.app.ui.use_toolbars and uiscale is ba.UIScale.SMALL: ba.textwidget(edit=txt, text='') v = height - (110 if self._is_main_menu else 90) v -= 100 clr = (0.6, 0.7, 0.6, 1.0) v -= 280 if self._is_main_menu else 180 v += (30 if ba.app.ui.use_toolbars and uiscale is ba.UIScale.SMALL else 0) hoffs = x_offs + 80 if self._is_main_menu else x_offs - 100 scl = 1.13 if self._is_main_menu else 0.68 self._lineup_tex = ba.gettexture('playerLineup') angry_computer_transparent_model = ba.getmodel( 'angryComputerTransparent') self._lineup_1_transparent_model = ba.getmodel( 'playerLineup1Transparent') self._lineup_2_transparent_model = ba.getmodel( 'playerLineup2Transparent') self._lineup_3_transparent_model = ba.getmodel( 'playerLineup3Transparent') self._lineup_4_transparent_model = ba.getmodel( 'playerLineup4Transparent') self._eyes_model = ba.getmodel('plasticEyesTransparent') self._coop_button: Optional[ba.Widget] = None # Only show coop button in main-menu variant. if self._is_main_menu: self._coop_button = btn = ba.buttonwidget( parent=self._root_widget, position=(hoffs, v + (scl * 15 if self._is_main_menu else 0)), size=(scl * button_width, scl * (300 if self._is_main_menu else 360)), extra_touch_border_scale=0.1, autoselect=True, label='', button_type='square', text_scale=1.13, on_activate_call=self._coop) if ba.app.ui.use_toolbars and uiscale is ba.UIScale.SMALL: ba.widget(edit=btn, left_widget=_ba.get_special_widget('back_button')) ba.widget(edit=btn, up_widget=_ba.get_special_widget('account_button')) ba.widget( edit=btn, down_widget=_ba.get_special_widget('settings_button')) self._draw_dude(0, btn, hoffs, v, scl, position=(140, 30), color=(0.72, 0.4, 1.0)) self._draw_dude(1, btn, hoffs, v, scl, position=(185, 53), color=(0.71, 0.5, 1.0)) self._draw_dude(2, btn, hoffs, v, scl, position=(220, 27), color=(0.67, 0.44, 1.0)) self._draw_dude(3, btn, hoffs, v, scl, position=(255, 57), color=(0.7, 0.3, 1.0)) ba.imagewidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * 230, v + scl * 153), size=(scl * 115, scl * 115), texture=self._lineup_tex, model_transparent=angry_computer_transparent_model) ba.textwidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (-10), v + scl * 95), size=(scl * button_width, scl * 50), text=ba.Lstr( resource='playModes.singlePlayerCoopText', fallback_resource='playModes.coopText'), maxwidth=scl * button_width * 0.7, res_scale=1.5, h_align='center', v_align='center', color=(0.7, 0.9, 0.7, 1.0), scale=scl * 2.3) ba.textwidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (-10), v + (scl * 54)), size=(scl * button_width, scl * 30), text=ba.Lstr(resource=self._r + '.oneToFourPlayersText'), h_align='center', v_align='center', scale=0.83 * scl, flatness=1.0, maxwidth=scl * button_width * 0.7, color=clr) scl = 0.5 if self._is_main_menu else 0.68 hoffs += 440 if self._is_main_menu else 216 v += 180 if self._is_main_menu else -68 self._teams_button = btn = ba.buttonwidget( parent=self._root_widget, position=(hoffs, v + (scl * 15 if self._is_main_menu else 0)), size=(scl * button_width, scl * (300 if self._is_main_menu else 360)), extra_touch_border_scale=0.1, autoselect=True, label='', button_type='square', text_scale=1.13, on_activate_call=self._team_tourney) if ba.app.ui.use_toolbars: ba.widget(edit=btn, up_widget=_ba.get_special_widget('tickets_plus_button'), right_widget=_ba.get_special_widget('party_button')) xxx = -14 self._draw_dude(2, btn, hoffs, v, scl, position=(xxx + 148, 30), color=(0.2, 0.4, 1.0)) self._draw_dude(3, btn, hoffs, v, scl, position=(xxx + 181, 53), color=(0.3, 0.4, 1.0)) self._draw_dude(1, btn, hoffs, v, scl, position=(xxx + 216, 33), color=(0.3, 0.5, 1.0)) self._draw_dude(0, btn, hoffs, v, scl, position=(xxx + 245, 57), color=(0.3, 0.5, 1.0)) xxx = 155 self._draw_dude(0, btn, hoffs, v, scl, position=(xxx + 151, 30), color=(1.0, 0.5, 0.4)) self._draw_dude(1, btn, hoffs, v, scl, position=(xxx + 189, 53), color=(1.0, 0.58, 0.58)) self._draw_dude(3, btn, hoffs, v, scl, position=(xxx + 223, 27), color=(1.0, 0.5, 0.5)) self._draw_dude(2, btn, hoffs, v, scl, position=(xxx + 257, 57), color=(1.0, 0.5, 0.5)) ba.textwidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (-10), v + scl * 95), size=(scl * button_width, scl * 50), text=ba.Lstr(resource='playModes.teamsText', fallback_resource='teamsText'), res_scale=1.5, maxwidth=scl * button_width * 0.7, h_align='center', v_align='center', color=(0.7, 0.9, 0.7, 1.0), scale=scl * 2.3) ba.textwidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (-10), v + (scl * 54)), size=(scl * button_width, scl * 30), text=ba.Lstr(resource=self._r + '.twoToEightPlayersText'), h_align='center', v_align='center', res_scale=1.5, scale=0.9 * scl, flatness=1.0, maxwidth=scl * button_width * 0.7, color=clr) hoffs += 0 if self._is_main_menu else 300 v -= 155 if self._is_main_menu else 0 self._free_for_all_button = btn = ba.buttonwidget( parent=self._root_widget, position=(hoffs, v + (scl * 15 if self._is_main_menu else 0)), size=(scl * button_width, scl * (300 if self._is_main_menu else 360)), extra_touch_border_scale=0.1, autoselect=True, label='', button_type='square', text_scale=1.13, on_activate_call=self._free_for_all) xxx = -5 self._draw_dude(0, btn, hoffs, v, scl, position=(xxx + 140, 30), color=(0.4, 1.0, 0.4)) self._draw_dude(3, btn, hoffs, v, scl, position=(xxx + 185, 53), color=(1.0, 0.4, 0.5)) self._draw_dude(1, btn, hoffs, v, scl, position=(xxx + 220, 27), color=(0.4, 0.5, 1.0)) self._draw_dude(2, btn, hoffs, v, scl, position=(xxx + 255, 57), color=(0.5, 1.0, 0.4)) xxx = 140 self._draw_dude(2, btn, hoffs, v, scl, position=(xxx + 148, 30), color=(1.0, 0.9, 0.4)) self._draw_dude(0, btn, hoffs, v, scl, position=(xxx + 182, 53), color=(0.7, 1.0, 0.5)) self._draw_dude(3, btn, hoffs, v, scl, position=(xxx + 233, 27), color=(0.7, 0.5, 0.9)) self._draw_dude(1, btn, hoffs, v, scl, position=(xxx + 266, 53), color=(0.4, 0.5, 0.8)) ba.textwidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (-10), v + scl * 95), size=(scl * button_width, scl * 50), text=ba.Lstr(resource='playModes.freeForAllText', fallback_resource='freeForAllText'), maxwidth=scl * button_width * 0.7, h_align='center', v_align='center', color=(0.7, 0.9, 0.7, 1.0), scale=scl * 1.9) ba.textwidget(parent=self._root_widget, draw_controller=btn, position=(hoffs + scl * (-10), v + (scl * 54)), size=(scl * button_width, scl * 30), text=ba.Lstr(resource=self._r + '.twoToEightPlayersText'), h_align='center', v_align='center', scale=0.9 * scl, flatness=1.0, maxwidth=scl * button_width * 0.7, color=clr) if ba.app.ui.use_toolbars and uiscale is ba.UIScale.SMALL: back_button.delete() ba.containerwidget(edit=self._root_widget, on_cancel_call=self._back, selected_child=self._coop_button if self._is_main_menu else self._teams_button) else: ba.buttonwidget(edit=back_button, on_activate_call=self._back) ba.containerwidget(edit=self._root_widget, cancel_button=back_button, selected_child=self._coop_button if self._is_main_menu else self._teams_button) self._restore_state()
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 _refresh(self) -> None: from ba.deprecated import get_resource for widget in self._col.get_children(): widget.delete() types = [ 'Menu', 'CharSelect', 'ToTheDeath', 'Onslaught', 'Keep Away', 'Race', 'Epic Race', 'ForwardMarch', 'FlagCatcher', 'Survival', 'Epic', 'Hockey', 'Football', 'Flying', 'Scary', 'Marching', 'GrandRomp', 'Chosen One', 'Scores', 'Victory', ] # FIXME: We should probably convert this to use translations. type_names_translated = get_resource('soundtrackTypeNames') prev_type_button: Optional[ba.Widget] = None prev_test_button: Optional[ba.Widget] = None for index, song_type in enumerate(types): row = ba.rowwidget(parent=self._col, size=(self._width - 40, 40)) ba.containerwidget(edit=row, claims_left_right=True, claims_tab=True, selection_loop_to_parent=True) try: type_name = type_names_translated[song_type] except Exception: type_name = song_type ba.textwidget(parent=row, size=(230, 25), always_highlight=True, text=type_name, scale=0.7, h_align='left', v_align='center', maxwidth=190) if song_type in self._soundtrack: entry = self._soundtrack[song_type] else: entry = None if entry is not None: # make sure they don't muck with this after it gets to us entry = copy.deepcopy(entry) icon_type = self._get_entry_button_display_icon_type(entry) self._song_type_buttons[song_type] = btn = ba.buttonwidget( parent=row, size=(230, 32), label=self._get_entry_button_display_name(entry), text_scale=0.6, on_activate_call=ba.Call(self._get_entry, song_type, entry, type_name), icon=(self._file_tex if icon_type == 'file' else self._folder_tex if icon_type == 'folder' else None), icon_color=(1.1, 0.8, 0.2) if icon_type == 'folder' else (1, 1, 1), left_widget=self._text_field, iconscale=0.7, autoselect=True, up_widget=prev_type_button) if index == 0: ba.widget(edit=btn, up_widget=self._text_field) ba.widget(edit=btn, down_widget=btn) if (self._last_edited_song_type is not None and song_type == self._last_edited_song_type): ba.containerwidget(edit=row, selected_child=btn, visible_child=btn) ba.containerwidget(edit=self._col, selected_child=row, visible_child=row) ba.containerwidget(edit=self._scrollwidget, selected_child=self._col, visible_child=self._col) ba.containerwidget(edit=self._root_widget, selected_child=self._scrollwidget, visible_child=self._scrollwidget) if prev_type_button is not None: ba.widget(edit=prev_type_button, down_widget=btn) prev_type_button = btn ba.textwidget(parent=row, size=(10, 32), text='') # spacing btn = ba.buttonwidget( parent=row, size=(50, 32), label=ba.Lstr(resource=self._r + '.testText'), text_scale=0.6, on_activate_call=ba.Call(self._test, ba.MusicType(song_type)), up_widget=prev_test_button if prev_test_button is not None else self._text_field) if prev_test_button is not None: ba.widget(edit=prev_test_button, down_widget=btn) ba.widget(edit=btn, down_widget=btn, right_widget=btn) prev_test_button = btn
def _refresh(self, select_soundtrack: str = None) -> None: 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: x[0].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 __init__(self, existing_soundtrack: Optional[Union[str, Dict[str, Any]]], transition: str = 'in_right'): # pylint: disable=too-many-statements bs_config = ba.app.config self._r = 'editSoundtrackWindow' self._folder_tex = ba.gettexture('folder') self._file_tex = ba.gettexture('file') self._width = 848 if ba.app.small_ui else 648 x_inset = 100 if ba.app.small_ui else 0 self._height = (395 if ba.app.small_ui else 450 if ba.app.med_ui else 560) super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height), transition=transition, scale=(2.08 if ba.app.small_ui else 1.5 if ba.app.med_ui else 1.0), stack_offset=(0, -48) if ba.app.small_ui else ( 0, 15) if ba.app.med_ui else (0, 0))) cancel_button = ba.buttonwidget(parent=self._root_widget, position=(38 + x_inset, self._height - 60), size=(160, 60), autoselect=True, label=ba.Lstr(resource='cancelText'), scale=0.8) save_button = ba.buttonwidget(parent=self._root_widget, position=(self._width - (168 + x_inset), self._height - 60), autoselect=True, size=(160, 60), label=ba.Lstr(resource='saveText'), scale=0.8) ba.widget(edit=save_button, left_widget=cancel_button) ba.widget(edit=cancel_button, right_widget=save_button) ba.textwidget( parent=self._root_widget, position=(0, self._height - 50), size=(self._width, 25), text=ba.Lstr(resource=self._r + ('.editSoundtrackText' if existing_soundtrack is not None else '.newSoundtrackText')), color=ba.app.title_color, h_align='center', v_align='center', maxwidth=280) v = self._height - 110 if 'Soundtracks' not in bs_config: bs_config['Soundtracks'] = {} self._soundtrack_name: Optional[str] self._existing_soundtrack_name: Optional[str] if existing_soundtrack is not None: # if they passed just a name, pull info from that soundtrack if isinstance(existing_soundtrack, str): self._soundtrack = copy.deepcopy( bs_config['Soundtracks'][existing_soundtrack]) self._soundtrack_name = existing_soundtrack self._existing_soundtrack_name = existing_soundtrack self._last_edited_song_type = None else: # otherwise they can pass info on an in-progress edit self._soundtrack = existing_soundtrack['soundtrack'] self._soundtrack_name = existing_soundtrack['name'] self._existing_soundtrack_name = ( existing_soundtrack['existing_name']) self._last_edited_song_type = ( existing_soundtrack['last_edited_song_type']) else: self._soundtrack_name = None self._existing_soundtrack_name = None self._soundtrack = {} self._last_edited_song_type = None ba.textwidget(parent=self._root_widget, text=ba.Lstr(resource=self._r + '.nameText'), maxwidth=80, scale=0.8, position=(105 + x_inset, v + 19), color=(0.8, 0.8, 0.8, 0.5), size=(0, 0), h_align='right', v_align='center') # if there's no initial value, find a good initial unused name if existing_soundtrack is None: i = 1 st_name_text = ba.Lstr(resource=self._r + '.newSoundtrackNameText').evaluate() if '${COUNT}' not in st_name_text: # make sure we insert number *somewhere* st_name_text = st_name_text + ' ${COUNT}' while True: self._soundtrack_name = st_name_text.replace( '${COUNT}', str(i)) if self._soundtrack_name not in bs_config['Soundtracks']: break i += 1 self._text_field = ba.textwidget( parent=self._root_widget, position=(120 + x_inset, v - 5), size=(self._width - (160 + 2 * x_inset), 43), text=self._soundtrack_name, h_align='left', v_align='center', max_chars=32, autoselect=True, description=ba.Lstr(resource=self._r + '.nameText'), editable=True, padding=4, on_return_press_call=self._do_it_with_sound) scroll_height = self._height - 180 self._scrollwidget = scrollwidget = ba.scrollwidget( parent=self._root_widget, highlight=False, position=(40 + x_inset, v - (scroll_height + 10)), size=(self._width - (80 + 2 * x_inset), scroll_height), simple_culling_v=10) ba.widget(edit=self._text_field, down_widget=self._scrollwidget) self._col = ba.columnwidget(parent=scrollwidget) ba.containerwidget(edit=self._scrollwidget, claims_left_right=True, claims_tab=True, selection_loop_to_parent=True) ba.containerwidget(edit=self._col, claims_left_right=True, claims_tab=True, selection_loop_to_parent=True) self._song_type_buttons: Dict[str, ba.Widget] = {} self._refresh() ba.buttonwidget(edit=cancel_button, on_activate_call=self._cancel) ba.containerwidget(edit=self._root_widget, cancel_button=cancel_button) ba.buttonwidget(edit=save_button, on_activate_call=self._do_it) ba.containerwidget(edit=self._root_widget, start_button=save_button) ba.widget(edit=self._text_field, up_widget=cancel_button) ba.widget(edit=cancel_button, down_widget=self._text_field)
def __init__(self, origin: Sequence[float] = (0, 0)): _ba.set_party_window_open(True) self._r = 'partyWindow' self._popup_type: Optional[str] = None self._popup_party_member_client_id: Optional[int] = None self._popup_party_member_is_host: Optional[bool] = None self._width = 500 uiscale = ba.app.ui.uiscale self._height = (365 if uiscale is ba.UIScale.SMALL else 480 if uiscale is ba.UIScale.MEDIUM else 600) super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height), transition='in_scale', color=(0.40, 0.55, 0.20), parent=_ba.get_special_widget('overlay_stack'), on_outside_click_call=self.close_with_sound, scale_origin_stack_offset=origin, scale=(2.0 if uiscale is ba.UIScale.SMALL else 1.35 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -10) if uiscale is ba.UIScale.SMALL else ( 240, 0) if uiscale is ba.UIScale.MEDIUM else (330, 20))) self._cancel_button = ba.buttonwidget(parent=self._root_widget, scale=0.7, position=(30, self._height - 47), size=(50, 50), label='', on_activate_call=self.close, autoselect=True, color=(0.45, 0.63, 0.15), icon=ba.gettexture('crossOut'), iconscale=1.2) ba.containerwidget(edit=self._root_widget, cancel_button=self._cancel_button) self._menu_button = ba.buttonwidget( parent=self._root_widget, scale=0.7, position=(self._width - 60, self._height - 47), size=(50, 50), label='...', autoselect=True, button_type='square', on_activate_call=ba.WeakCall(self._on_menu_button_press), color=(0.55, 0.73, 0.25), iconscale=1.2) info = _ba.get_connection_to_host_info() if info.get('name', '') != '': title = ba.Lstr(value=info['name']) else: title = ba.Lstr(resource=self._r + '.titleText') self._title_text = ba.textwidget(parent=self._root_widget, scale=0.9, color=(0.5, 0.7, 0.5), text=title, size=(0, 0), position=(self._width * 0.5, self._height - 29), maxwidth=self._width * 0.7, h_align='center', v_align='center') self._empty_str = ba.textwidget(parent=self._root_widget, scale=0.75, size=(0, 0), position=(self._width * 0.5, self._height - 65), maxwidth=self._width * 0.85, h_align='center', v_align='center') self._scroll_width = self._width - 50 self._scrollwidget = ba.scrollwidget(parent=self._root_widget, size=(self._scroll_width, self._height - 200), position=(30, 80), color=(0.4, 0.6, 0.3)) self._columnwidget = ba.columnwidget(parent=self._scrollwidget, border=2, margin=0) ba.widget(edit=self._menu_button, down_widget=self._columnwidget) self._muted_text = ba.textwidget( parent=self._root_widget, position=(self._width * 0.5, self._height * 0.5), size=(0, 0), h_align='center', v_align='center', text=ba.Lstr(resource='chatMutedText')) self._chat_texts: List[ba.Widget] = [] # add all existing messages if chat is not muted if not ba.app.config.resolve('Chat Muted'): msgs = _ba.get_chat_messages() for msg in msgs: self._add_msg(msg) self._text_field = txt = ba.textwidget( parent=self._root_widget, editable=True, size=(530, 40), position=(44, 39), text='', maxwidth=494, shadow=0.3, flatness=1.0, description=ba.Lstr(resource=self._r + '.chatMessageText'), autoselect=True, v_align='center', corner_scale=0.7) ba.widget(edit=self._scrollwidget, autoselect=True, left_widget=self._cancel_button, up_widget=self._cancel_button, down_widget=self._text_field) ba.widget(edit=self._columnwidget, autoselect=True, up_widget=self._cancel_button, down_widget=self._text_field) ba.containerwidget(edit=self._root_widget, selected_child=txt) btn = ba.buttonwidget(parent=self._root_widget, size=(50, 35), label=ba.Lstr(resource=self._r + '.sendText'), button_type='square', autoselect=True, position=(self._width - 70, 35), on_activate_call=self._send_chat_message) ba.textwidget(edit=txt, on_return_press_call=btn.activate) self._name_widgets: List[ba.Widget] = [] self._roster: Optional[List[Dict[str, Any]]] = None self._update_timer = ba.Timer(1.0, ba.WeakCall(self._update), repeat=True, timetype=ba.TimeType.REAL) self._update()
def _rebuild_ui(self) -> None: from ba.internal import get_device_value for widget in self._root_widget.get_children(): widget.delete() # Fill our temp config with present values. self._settings: Dict[str, int] = {} for button in [ 'buttonJump', 'buttonPunch', 'buttonBomb', 'buttonPickUp', 'buttonStart', 'buttonStart2', 'buttonUp', 'buttonDown', 'buttonLeft', 'buttonRight' ]: self._settings[button] = get_device_value(self._input, button) cancel_button = ba.buttonwidget(parent=self._root_widget, autoselect=True, position=(38, self._height - 85), size=(170, 60), label=ba.Lstr(resource='cancelText'), scale=0.9, on_activate_call=self._cancel) save_button = ba.buttonwidget(parent=self._root_widget, autoselect=True, position=(self._width - 190, self._height - 85), size=(180, 60), label=ba.Lstr(resource='saveText'), scale=0.9, text_scale=0.9, on_activate_call=self._save) ba.containerwidget(edit=self._root_widget, cancel_button=cancel_button, start_button=save_button) ba.widget(edit=cancel_button, right_widget=save_button) ba.widget(edit=save_button, left_widget=cancel_button) v = self._height - 74.0 ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, v + 15), size=(0, 0), text=ba.Lstr(resource=self._r + '.configuringText', subs=[('${DEVICE}', self._displayname)]), color=ba.app.ui.title_color, h_align='center', v_align='center', maxwidth=270, scale=0.83) v -= 20 if self._unique_id != '#1': v -= 20 v -= self._spacing ba.textwidget(parent=self._root_widget, position=(0, v + 19), size=(self._width, 50), text=ba.Lstr(resource=self._r + '.keyboard2NoteText'), scale=0.7, maxwidth=self._width * 0.75, max_height=110, color=ba.app.ui.infotextcolor, h_align='center', v_align='top') v -= 40 v -= 10 v -= self._spacing * 2.2 v += 25 v -= 42 h_offs = 160 dist = 70 d_color = (0.4, 0.4, 0.8) self._capture_button(pos=(h_offs, v + 0.95 * dist), color=d_color, button='buttonUp', texture=ba.gettexture('upButton'), scale=1.0) self._capture_button(pos=(h_offs - 1.2 * dist, v), color=d_color, button='buttonLeft', texture=ba.gettexture('leftButton'), scale=1.0) self._capture_button(pos=(h_offs + 1.2 * dist, v), color=d_color, button='buttonRight', texture=ba.gettexture('rightButton'), scale=1.0) self._capture_button(pos=(h_offs, v - 0.95 * dist), color=d_color, button='buttonDown', texture=ba.gettexture('downButton'), scale=1.0) if self._unique_id == '#2': self._capture_button(pos=(self._width * 0.5, v + 0.1 * dist), color=(0.4, 0.4, 0.6), button='buttonStart', texture=ba.gettexture('startButton'), scale=0.8) h_offs = self._width - 160 self._capture_button(pos=(h_offs, v + 0.95 * dist), color=(0.6, 0.4, 0.8), button='buttonPickUp', texture=ba.gettexture('buttonPickUp'), scale=1.0) self._capture_button(pos=(h_offs - 1.2 * dist, v), color=(0.7, 0.5, 0.1), button='buttonPunch', texture=ba.gettexture('buttonPunch'), scale=1.0) self._capture_button(pos=(h_offs + 1.2 * dist, v), color=(0.5, 0.2, 0.1), button='buttonBomb', texture=ba.gettexture('buttonBomb'), scale=1.0) self._capture_button(pos=(h_offs, v - 0.95 * dist), color=(0.2, 0.5, 0.2), button='buttonJump', texture=ba.gettexture('buttonJump'), scale=1.0)
def __init__(self, path: str, callback: Callable[[Optional[str]], Any] = None, show_base_path: bool = True, valid_file_extensions: Sequence[str] = None, allow_folders: bool = False): if valid_file_extensions is None: valid_file_extensions = [] uiscale = ba.app.uiscale self._width = 700 if uiscale is ba.UIScale.SMALL else 600 self._x_inset = x_inset = 50 if uiscale is ba.UIScale.SMALL else 0 self._height = 365 if uiscale is ba.UIScale.SMALL else 418 self._callback = callback self._base_path = path self._path: Optional[str] = None self._recent_paths: List[str] = [] self._show_base_path = show_base_path self._valid_file_extensions = [ '.' + ext for ext in valid_file_extensions ] self._allow_folders = allow_folders self._subcontainer: Optional[ba.Widget] = None self._subcontainerheight: Optional[float] = None self._scroll_width = self._width - (80 + 2 * x_inset) self._scroll_height = self._height - 170 self._r = 'fileSelectorWindow' super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height), transition='in_right', scale=(2.23 if uiscale is ba.UIScale.SMALL else 1.4 if uiscale is ba.UIScale.MEDIUM else 1.0), stack_offset=(0, -35) if uiscale is ba.UIScale.SMALL else (0, 0))) ba.textwidget( parent=self._root_widget, position=(self._width * 0.5, self._height - 42), size=(0, 0), color=ba.app.title_color, h_align='center', v_align='center', text=ba.Lstr(resource=self._r + '.titleFolderText') if (allow_folders and not valid_file_extensions) else ba.Lstr( resource=self._r + '.titleFileText') if not allow_folders else ba.Lstr( resource=self._r + '.titleFileFolderText'), maxwidth=210) self._button_width = 146 self._cancel_button = ba.buttonwidget( parent=self._root_widget, position=(35 + x_inset, self._height - 67), autoselect=True, size=(self._button_width, 50), label=ba.Lstr(resource='cancelText'), on_activate_call=self._cancel) ba.widget(edit=self._cancel_button, left_widget=self._cancel_button) b_color = (0.6, 0.53, 0.63) self._back_button = ba.buttonwidget( parent=self._root_widget, button_type='square', position=(43 + x_inset, self._height - 113), color=b_color, textcolor=(0.75, 0.7, 0.8), enable_sound=False, size=(55, 35), label=ba.charstr(ba.SpecialChar.LEFT_ARROW), on_activate_call=self._on_back_press) self._folder_tex = ba.gettexture('folder') self._folder_color = (1.1, 0.8, 0.2) self._file_tex = ba.gettexture('file') self._file_color = (1, 1, 1) self._use_folder_button: Optional[ba.Widget] = None self._folder_center = self._width * 0.5 + 15 self._folder_icon = ba.imagewidget(parent=self._root_widget, size=(40, 40), position=(40, self._height - 117), texture=self._folder_tex, color=self._folder_color) self._path_text = ba.textwidget(parent=self._root_widget, position=(self._folder_center, self._height - 98), size=(0, 0), color=ba.app.title_color, h_align='center', v_align='center', text=self._path, maxwidth=self._width * 0.9) self._scrollwidget: Optional[ba.Widget] = None ba.containerwidget(edit=self._root_widget, cancel_button=self._cancel_button) self._set_path(path)
def _refresh(self) -> None: # pylint: disable=too-many-locals 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() try: self._profiles = ba.app.config['Player Profiles'] except Exception: self._profiles = {} assert self._profiles is not None items = list(self._profiles.items()) items.sort(key=lambda x: x[0].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 txtw = ba.textwidget( parent=self._columnwidget, position=(0, 32), size=((self._width - 40) / scl, 28), text=ba.Lstr( value=account_name if p_name == '__account__' else get_player_profile_icon(p_name) + p_name), 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())
def __init__(self, transition: Optional[str] = 'in_right', origin_widget: ba.Widget = None): # pylint: disable=too-many-locals # pylint: disable=too-many-statements from bastd.ui.tabs import TabRow ba.set_analytics_screen('Watch Window') scale_origin: Optional[Tuple[float, float]] if origin_widget is not None: self._transition_out = 'out_scale' scale_origin = origin_widget.get_screen_space_center() transition = 'in_scale' else: self._transition_out = 'out_right' scale_origin = None ba.app.ui.set_main_menu_location('Watch') self._tab_data: Dict[str, Any] = {} self._my_replays_scroll_width: Optional[float] = None self._my_replays_watch_replay_button: Optional[ba.Widget] = None self._scrollwidget: Optional[ba.Widget] = None self._columnwidget: Optional[ba.Widget] = None self._my_replay_selected: Optional[str] = None self._my_replays_rename_window: Optional[ba.Widget] = None self._my_replay_rename_text: Optional[ba.Widget] = None self._r = 'watchWindow' uiscale = ba.app.ui.uiscale self._width = 1240 if uiscale is ba.UIScale.SMALL else 1040 x_inset = 100 if uiscale is ba.UIScale.SMALL else 0 self._height = (578 if uiscale is ba.UIScale.SMALL else 670 if uiscale is ba.UIScale.MEDIUM else 800) self._current_tab: Optional[WatchWindow.TabID] = None extra_top = 20 if uiscale is ba.UIScale.SMALL else 0 super().__init__(root_widget=ba.containerwidget( size=(self._width, self._height + extra_top), transition=transition, toolbar_visibility='menu_minimal', scale_origin_stack_offset=scale_origin, scale=(1.3 if uiscale is ba.UIScale.SMALL else 0.97 if uiscale is ba.UIScale.MEDIUM else 0.8), stack_offset=(0, -10) if uiscale is ba.UIScale.SMALL else ( 0, 15) if uiscale is ba.UIScale.MEDIUM else (0, 0))) if uiscale is ba.UIScale.SMALL and ba.app.ui.use_toolbars: ba.containerwidget(edit=self._root_widget, on_cancel_call=self._back) self._back_button = None else: self._back_button = btn = ba.buttonwidget( parent=self._root_widget, autoselect=True, position=(70 + x_inset, self._height - 74), size=(140, 60), scale=1.1, label=ba.Lstr(resource='backText'), button_type='back', on_activate_call=self._back) ba.containerwidget(edit=self._root_widget, cancel_button=btn) ba.buttonwidget(edit=btn, button_type='backSmall', size=(60, 60), label=ba.charstr(ba.SpecialChar.BACK)) ba.textwidget(parent=self._root_widget, position=(self._width * 0.5, self._height - 38), size=(0, 0), color=ba.app.ui.title_color, scale=1.5, h_align='center', v_align='center', text=ba.Lstr(resource=self._r + '.titleText'), maxwidth=400) tabdefs = [ (self.TabID.MY_REPLAYS, ba.Lstr(resource=self._r + '.myReplaysText')), # (self.TabID.TEST_TAB, ba.Lstr(value='Testing')), ] scroll_buffer_h = 130 + 2 * x_inset tab_buffer_h = 750 + 2 * x_inset self._tab_row = TabRow(self._root_widget, tabdefs, pos=(tab_buffer_h * 0.5, self._height - 130), size=(self._width - tab_buffer_h, 50), on_select_call=self._set_tab) if ba.app.ui.use_toolbars: first_tab = self._tab_row.tabs[tabdefs[0][0]] last_tab = self._tab_row.tabs[tabdefs[-1][0]] ba.widget(edit=last_tab.button, right_widget=_ba.get_special_widget('party_button')) if uiscale is ba.UIScale.SMALL: bbtn = _ba.get_special_widget('back_button') ba.widget(edit=first_tab.button, up_widget=bbtn, left_widget=bbtn) self._scroll_width = self._width - scroll_buffer_h self._scroll_height = self._height - 180 # Not actually using a scroll widget anymore; just an image. scroll_left = (self._width - self._scroll_width) * 0.5 scroll_bottom = self._height - self._scroll_height - 79 - 48 buffer_h = 10 buffer_v = 4 ba.imagewidget(parent=self._root_widget, position=(scroll_left - buffer_h, scroll_bottom - buffer_v), size=(self._scroll_width + 2 * buffer_h, self._scroll_height + 2 * buffer_v), texture=ba.gettexture('scrollWidget'), model_transparent=ba.getmodel('softEdgeOutside')) self._tab_container: Optional[ba.Widget] = None self._restore_state()
def __init__(self, transition: str = 'in_right', origin_widget: ba.Widget = None): # pylint: disable=too-many-locals # pylint: disable=too-many-branches # pylint: disable=too-many-statements from bastd.ui import popup from bastd.ui.config import ConfigCheckBox, ConfigNumberEdit # if they provided an origin-widget, scale up from that scale_origin: Optional[Tuple[float, float]] if origin_widget is not None: self._transition_out = 'out_scale' scale_origin = origin_widget.get_screen_space_center() transition = 'in_scale' else: self._transition_out = 'out_right' scale_origin = None self._r = 'graphicsSettingsWindow' app = ba.app spacing = 32 self._have_selected_child = False uiscale = app.ui.uiscale width = 450.0 height = 302.0 self._show_fullscreen = False fullscreen_spacing_top = spacing * 0.2 fullscreen_spacing = spacing * 1.2 if uiscale == ba.UIScale.LARGE and app.platform != 'android': self._show_fullscreen = True height += fullscreen_spacing + fullscreen_spacing_top show_gamma = False gamma_spacing = spacing * 1.3 if _ba.has_gamma_control(): show_gamma = True height += gamma_spacing show_vsync = False if app.platform == 'mac': show_vsync = True show_resolution = True if app.vr_mode: show_resolution = (app.platform == 'android' and app.subplatform == 'cardboard') uiscale = ba.app.ui.uiscale base_scale = (2.4 if uiscale is ba.UIScale.SMALL else 1.5 if uiscale is ba.UIScale.MEDIUM else 1.0) popup_menu_scale = base_scale * 1.2 v = height - 50 v -= spacing * 1.15 super().__init__(root_widget=ba.containerwidget( size=(width, height), transition=transition, scale_origin_stack_offset=scale_origin, scale=base_scale, stack_offset=(0, -30) if uiscale is ba.UIScale.SMALL else (0, 0))) btn = ba.buttonwidget(parent=self._root_widget, position=(35, height - 50), size=(120, 60), scale=0.8, text_scale=1.2, autoselect=True, label=ba.Lstr(resource='backText'), button_type='back', on_activate_call=self._back) ba.containerwidget(edit=self._root_widget, cancel_button=btn) ba.textwidget(parent=self._root_widget, position=(0, height - 44), size=(width, 25), text=ba.Lstr(resource=self._r + '.titleText'), color=ba.app.ui.title_color, h_align='center', v_align='top') ba.buttonwidget(edit=btn, button_type='backSmall', size=(60, 60), label=ba.charstr(ba.SpecialChar.BACK)) self._fullscreen_checkbox: Optional[ba.Widget] if self._show_fullscreen: v -= fullscreen_spacing_top self._fullscreen_checkbox = ConfigCheckBox( parent=self._root_widget, position=(100, v), maxwidth=200, size=(300, 30), configkey='Fullscreen', displayname=ba.Lstr(resource=self._r + ('.fullScreenCmdText' if app.platform == 'mac' else '.fullScreenCtrlText'))).widget if not self._have_selected_child: ba.containerwidget(edit=self._root_widget, selected_child=self._fullscreen_checkbox) self._have_selected_child = True v -= fullscreen_spacing else: self._fullscreen_checkbox = None self._gamma_controls: Optional[ConfigNumberEdit] if show_gamma: self._gamma_controls = gmc = ConfigNumberEdit( parent=self._root_widget, position=(90, v), configkey='Screen Gamma', displayname=ba.Lstr(resource=self._r + '.gammaText'), minval=0.1, maxval=2.0, increment=0.1, xoffset=-70, textscale=0.85) if ba.app.ui.use_toolbars: ba.widget(edit=gmc.plusbutton, right_widget=_ba.get_special_widget('party_button')) if not self._have_selected_child: ba.containerwidget(edit=self._root_widget, selected_child=gmc.minusbutton) self._have_selected_child = True v -= gamma_spacing else: self._gamma_controls = None self._selected_color = (0.5, 1, 0.5, 1) self._unselected_color = (0.7, 0.7, 0.7, 1) # quality ba.textwidget(parent=self._root_widget, position=(60, v), size=(160, 25), text=ba.Lstr(resource=self._r + '.visualsText'), color=ba.app.ui.heading_color, scale=0.65, maxwidth=150, h_align='center', v_align='center') popup.PopupMenu( parent=self._root_widget, position=(60, v - 50), width=150, scale=popup_menu_scale, choices=['Auto', 'Higher', 'High', 'Medium', 'Low'], choices_disabled=['Higher', 'High'] if _ba.get_max_graphics_quality() == 'Medium' else [], choices_display=[ ba.Lstr(resource='autoText'), ba.Lstr(resource=self._r + '.higherText'), ba.Lstr(resource=self._r + '.highText'), ba.Lstr(resource=self._r + '.mediumText'), ba.Lstr(resource=self._r + '.lowText') ], current_choice=ba.app.config.resolve('Graphics Quality'), on_value_change_call=self._set_quality) # texture controls ba.textwidget(parent=self._root_widget, position=(230, v), size=(160, 25), text=ba.Lstr(resource=self._r + '.texturesText'), color=ba.app.ui.heading_color, scale=0.65, maxwidth=150, h_align='center', v_align='center') textures_popup = popup.PopupMenu( parent=self._root_widget, position=(230, v - 50), width=150, scale=popup_menu_scale, choices=['Auto', 'High', 'Medium', 'Low'], choices_display=[ ba.Lstr(resource='autoText'), ba.Lstr(resource=self._r + '.highText'), ba.Lstr(resource=self._r + '.mediumText'), ba.Lstr(resource=self._r + '.lowText') ], current_choice=ba.app.config.resolve('Texture Quality'), on_value_change_call=self._set_textures) if ba.app.ui.use_toolbars: ba.widget(edit=textures_popup.get_button(), right_widget=_ba.get_special_widget('party_button')) v -= 80 h_offs = 0 if show_resolution: # resolution ba.textwidget(parent=self._root_widget, position=(h_offs + 60, v), size=(160, 25), text=ba.Lstr(resource=self._r + '.resolutionText'), color=ba.app.ui.heading_color, scale=0.65, maxwidth=150, h_align='center', v_align='center') # on standard android we have 'Auto', 'Native', and a few # HD standards if app.platform == 'android': # on cardboard/daydream android we have a few # render-target-scale options if app.subplatform == 'cardboard': current_res_cardboard = (str(min(100, max(10, int(round( ba.app.config.resolve('GVR Render Target Scale') * 100.0))))) + '%') # yapf: disable popup.PopupMenu( parent=self._root_widget, position=(h_offs + 60, v - 50), width=120, scale=popup_menu_scale, choices=['100%', '75%', '50%', '35%'], current_choice=current_res_cardboard, on_value_change_call=self._set_gvr_render_target_scale) else: native_res = _ba.get_display_resolution() assert native_res is not None choices = ['Auto', 'Native'] choices_display = [ ba.Lstr(resource='autoText'), ba.Lstr(resource='nativeText') ] for res in [1440, 1080, 960, 720, 480]: # nav bar is 72px so lets allow for that in what # choices we show if native_res[1] >= res - 72: res_str = str(res) + 'p' choices.append(res_str) choices_display.append(ba.Lstr(value=res_str)) current_res_android = ba.app.config.resolve( 'Resolution (Android)') popup.PopupMenu(parent=self._root_widget, position=(h_offs + 60, v - 50), width=120, scale=popup_menu_scale, choices=choices, choices_display=choices_display, current_choice=current_res_android, on_value_change_call=self._set_android_res) else: # if we're on a system that doesn't allow setting resolution, # set pixel-scale instead current_res = _ba.get_display_resolution() if current_res is None: current_res2 = (str(min(100, max(10, int(round( ba.app.config.resolve('Screen Pixel Scale') * 100.0))))) + '%') # yapf: disable popup.PopupMenu( parent=self._root_widget, position=(h_offs + 60, v - 50), width=120, scale=popup_menu_scale, choices=['100%', '88%', '75%', '63%', '50%'], current_choice=current_res2, on_value_change_call=self._set_pixel_scale) else: raise Exception('obsolete path; discrete resolutions' ' no longer supported') # vsync if show_vsync: ba.textwidget(parent=self._root_widget, position=(230, v), size=(160, 25), text=ba.Lstr(resource=self._r + '.verticalSyncText'), color=ba.app.ui.heading_color, scale=0.65, maxwidth=150, h_align='center', v_align='center') popup.PopupMenu( parent=self._root_widget, position=(230, v - 50), width=150, scale=popup_menu_scale, choices=['Auto', 'Always', 'Never'], choices_display=[ ba.Lstr(resource='autoText'), ba.Lstr(resource=self._r + '.alwaysText'), ba.Lstr(resource=self._r + '.neverText') ], current_choice=ba.app.config.resolve('Vertical Sync'), on_value_change_call=self._set_vsync) v -= 90 fpsc = ConfigCheckBox(parent=self._root_widget, position=(69, v - 6), size=(210, 30), scale=0.86, configkey='Show FPS', displayname=ba.Lstr(resource=self._r + '.showFPSText'), maxwidth=130) # (tv mode doesnt apply to vr) if not ba.app.vr_mode: tvc = ConfigCheckBox(parent=self._root_widget, position=(240, v - 6), size=(210, 30), scale=0.86, configkey='TV Border', displayname=ba.Lstr(resource=self._r + '.tvBorderText'), maxwidth=130) # grumble.. ba.widget(edit=fpsc.widget, right_widget=tvc.widget) try: pass except Exception: ba.print_exception('Exception wiring up graphics settings UI:') v -= spacing # make a timer to update our controls in case the config changes # under us self._update_timer = ba.Timer(0.25, ba.WeakCall(self._update_controls), repeat=True, timetype=ba.TimeType.REAL)