Converted gather window to new TabRow class and killed old tabs functionality

This commit is contained in:
Eric Froemling 2020-10-20 15:13:58 -07:00
parent cc1ec205c0
commit 6bcd827ced
4 changed files with 80 additions and 120 deletions

View File

@ -6,6 +6,7 @@
- Ditto with AccountSubsystem and ba.app.accounts - Ditto with AccountSubsystem and ba.app.accounts
- Ditto with MetadataSubsystem and ba.app.meta - Ditto with MetadataSubsystem and ba.app.meta
- Ditto with AdsSubsystem and ba.app.ads - Ditto with AdsSubsystem and ba.app.ads
- Revamped tab-button functionality into a cleaner type-safe class (bastd.ui.tabs.TabRow)
### 1.5.26 (20217) ### 1.5.26 (20217)
- Simplified licensing header on python scripts. - Simplified licensing header on python scripts.

View File

@ -7,6 +7,7 @@ from __future__ import annotations
import threading import threading
import time import time
from enum import Enum
from typing import TYPE_CHECKING, cast from typing import TYPE_CHECKING, cast
import _ba import _ba
@ -19,6 +20,16 @@ if TYPE_CHECKING:
class GatherWindow(ba.Window): class GatherWindow(ba.Window):
"""Window for joining/inviting friends.""" """Window for joining/inviting friends."""
class TabID(Enum):
"""Our available tab types."""
ABOUT = 'about'
INTERNET = 'internet'
GOOGLE_PLAY = 'google_play'
LOCAL_NETWORK = 'local_network'
BLUETOOTH = 'bluetooth'
WIFI_DIRECT = 'wifi_direct'
MANUAL = 'manual'
def __del__(self) -> None: def __del__(self) -> None:
_ba.set_party_icon_always_visible(False) _ba.set_party_icon_always_visible(False)
@ -27,7 +38,7 @@ class GatherWindow(ba.Window):
origin_widget: ba.Widget = None): origin_widget: ba.Widget = None):
# pylint: disable=too-many-statements # pylint: disable=too-many-statements
# pylint: disable=too-many-locals # pylint: disable=too-many-locals
from bastd.ui import tabs from bastd.ui.tabs import TabRow
ba.set_analytics_screen('Gather Window') ba.set_analytics_screen('Gather Window')
scale_origin: Optional[Tuple[float, float]] scale_origin: Optional[Tuple[float, float]]
if origin_widget is not None: if origin_widget is not None:
@ -45,7 +56,7 @@ class GatherWindow(ba.Window):
x_offs = 100 if uiscale is ba.UIScale.SMALL else 0 x_offs = 100 if uiscale is ba.UIScale.SMALL else 0
self._height = (582 if uiscale is ba.UIScale.SMALL else self._height = (582 if uiscale is ba.UIScale.SMALL else
680 if uiscale is ba.UIScale.MEDIUM else 800) 680 if uiscale is ba.UIScale.MEDIUM else 800)
self._current_tab: Optional[str] = None self._current_tab: Optional[GatherWindow.TabID] = None
extra_top = 20 if uiscale is ba.UIScale.SMALL else 0 extra_top = 20 if uiscale is ba.UIScale.SMALL else 0
self._r = 'gatherWindow' self._r = 'gatherWindow'
self._tab_data: Any = None self._tab_data: Any = None
@ -130,35 +141,35 @@ class GatherWindow(ba.Window):
platform = ba.app.platform platform = ba.app.platform
subplatform = ba.app.subplatform subplatform = ba.app.subplatform
tabs_def: List[Tuple[str, ba.Lstr]] = [ tabs_def: List[Tuple[GatherWindow.TabID, ba.Lstr]] = [
('about', ba.Lstr(resource=self._r + '.aboutText')) (self.TabID.ABOUT, ba.Lstr(resource=self._r + '.aboutText'))
] ]
if _ba.get_account_misc_read_val('enablePublicParties', True): if _ba.get_account_misc_read_val('enablePublicParties', True):
tabs_def.append( tabs_def.append((self.TabID.INTERNET,
('internet', ba.Lstr(resource=self._r + '.internetText'))) ba.Lstr(resource=self._r + '.internetText')))
if platform == 'android' and subplatform == 'google': if platform == 'android' and subplatform == 'google':
tabs_def.append( tabs_def.append((self.TabID.GOOGLE_PLAY,
('google_play', ba.Lstr(resource=self._r + '.googlePlayText'))) ba.Lstr(resource=self._r + '.googlePlayText')))
tabs_def.append( tabs_def.append((self.TabID.LOCAL_NETWORK,
('local_network', ba.Lstr(resource=self._r + '.localNetworkText'))) ba.Lstr(resource=self._r + '.localNetworkText')))
tabs_def.append(('manual', ba.Lstr(resource=self._r + '.manualText'))) tabs_def.append(
(self.TabID.MANUAL, ba.Lstr(resource=self._r + '.manualText')))
scroll_buffer_h = 130 + 2 * x_offs scroll_buffer_h = 130 + 2 * x_offs
tab_buffer_h = 250 + 2 * x_offs tab_buffer_h = 250 + 2 * x_offs
self._tab_buttons = tabs.create_tab_buttons( self._tab_row = TabRow(self._root_widget,
self._root_widget, tabs_def,
tabs_def, pos=(tab_buffer_h * 0.5, self._height - 130),
pos=(tab_buffer_h * 0.5, self._height - 130), size=(self._width - tab_buffer_h, 50),
size=(self._width - tab_buffer_h, 50), on_select_call=self._set_tab)
on_select_call=self._set_tab)
if ba.app.ui.use_toolbars: if ba.app.ui.use_toolbars:
ba.widget(edit=self._tab_buttons[tabs_def[-1][0]], ba.widget(edit=self._tab_row.tabs[tabs_def[-1][0]].button,
right_widget=_ba.get_special_widget('party_button')) right_widget=_ba.get_special_widget('party_button'))
if uiscale is ba.UIScale.SMALL: if uiscale is ba.UIScale.SMALL:
ba.widget(edit=self._tab_buttons[tabs_def[0][0]], ba.widget(edit=self._tab_row.tabs[tabs_def[0][0]].button,
left_widget=_ba.get_special_widget('back_button')) left_widget=_ba.get_special_widget('back_button'))
self._scroll_width = self._width - scroll_buffer_h self._scroll_width = self._width - scroll_buffer_h
@ -222,21 +233,20 @@ class GatherWindow(ba.Window):
return return
appinvite.handle_app_invites_press() appinvite.handle_app_invites_press()
def _set_tab(self, tab: str) -> None: def _set_tab(self, tab_id: TabID) -> None:
# pylint: disable=too-many-statements # pylint: disable=too-many-statements
# pylint: disable=too-many-locals # pylint: disable=too-many-locals
from bastd.ui import tabs if self._current_tab is tab_id:
if self._current_tab == tab:
return return
self._current_tab = tab self._current_tab = tab_id
# We wanna preserve our current tab between runs. # We wanna preserve our current tab between runs.
cfg = ba.app.config cfg = ba.app.config
cfg['Gather Tab'] = tab cfg['Gather Tab'] = tab_id.value
cfg.commit() cfg.commit()
# Update tab colors based on which is selected. # Update tab colors based on which is selected.
tabs.update_tab_button_colors(self._tab_buttons, tab) self._tab_row.update_appearance(tab_id)
# (Re)create scroll widget. # (Re)create scroll widget.
if self._tab_container: if self._tab_container:
@ -249,7 +259,7 @@ class GatherWindow(ba.Window):
self._tab_data = {} self._tab_data = {}
# So we can still select root level widgets with direction buttons. # So we can still select root level widgets with direction buttons.
def _simple_message(tab2: str, def _simple_message(tab2: GatherWindow.TabID,
message: ba.Lstr, message: ba.Lstr,
string_height: float, string_height: float,
include_invite: bool = False) -> None: include_invite: bool = False) -> None:
@ -268,7 +278,7 @@ class GatherWindow(ba.Window):
size=(c_width_2, c_height_2), size=(c_width_2, c_height_2),
background=False, background=False,
selectable=include_invite) selectable=include_invite)
ba.widget(edit=cnt2, up_widget=self._tab_buttons[tab2]) ba.widget(edit=cnt2, up_widget=self._tab_row.tabs[tab2].button)
ba.textwidget( ba.textwidget(
parent=cnt2, parent=cnt2,
@ -306,9 +316,9 @@ class GatherWindow(ba.Window):
'gatherWindow.getFriendInviteCodeText')), 'gatherWindow.getFriendInviteCodeText')),
autoselect=True, autoselect=True,
on_activate_call=ba.WeakCall(self._invite_to_try_press), on_activate_call=ba.WeakCall(self._invite_to_try_press),
up_widget=self._tab_buttons[tab2]) up_widget=self._tab_row.tabs[tab2].button)
if tab == 'about': if tab_id is self.TabID.ABOUT:
msg = ba.Lstr(resource=self._r + '.aboutDescriptionText', msg = ba.Lstr(resource=self._r + '.aboutDescriptionText',
subs=[('${PARTY}', subs=[('${PARTY}',
ba.charstr(ba.SpecialChar.PARTY_ICON)), ba.charstr(ba.SpecialChar.PARTY_ICON)),
@ -327,9 +337,9 @@ class GatherWindow(ba.Window):
'.aboutDescriptionLocalMultiplayerExtraText')) '.aboutDescriptionLocalMultiplayerExtraText'))
]) ])
_simple_message(tab, msg, 400, include_invite=True) _simple_message(tab_id, msg, 400, include_invite=True)
elif tab == 'google_play': elif tab_id is self.TabID.GOOGLE_PLAY:
c_width = self._scroll_width c_width = self._scroll_width
c_height = 380.0 c_height = 380.0
self._tab_container = cnt = ba.containerwidget( self._tab_container = cnt = ba.containerwidget(
@ -351,7 +361,7 @@ class GatherWindow(ba.Window):
v_align='center', v_align='center',
text=ba.Lstr(resource='googleMultiplayerDiscontinuedText')) text=ba.Lstr(resource='googleMultiplayerDiscontinuedText'))
elif tab == 'internet': elif tab_id is self.TabID.INTERNET:
c_width = self._scroll_width c_width = self._scroll_width
c_height = self._scroll_height - 20 c_height = self._scroll_height - 20
self._tab_container = cnt = ba.containerwidget( self._tab_container = cnt = ba.containerwidget(
@ -378,7 +388,7 @@ class GatherWindow(ba.Window):
'join', playsound=True), 'join', playsound=True),
text=ba.Lstr(resource=self._r + text=ba.Lstr(resource=self._r +
'.joinPublicPartyDescriptionText')) '.joinPublicPartyDescriptionText'))
ba.widget(edit=txt, up_widget=self._tab_buttons[tab]) ba.widget(edit=txt, up_widget=self._tab_row.tabs[tab_id].button)
self._internet_host_text = txt = ba.textwidget( self._internet_host_text = txt = ba.textwidget(
parent=cnt, parent=cnt,
position=(c_width * 0.5 + 45, v - 13), position=(c_width * 0.5 + 45, v - 13),
@ -397,7 +407,7 @@ class GatherWindow(ba.Window):
'.hostPublicPartyDescriptionText')) '.hostPublicPartyDescriptionText'))
ba.widget(edit=txt, ba.widget(edit=txt,
left_widget=self._internet_join_text, left_widget=self._internet_join_text,
up_widget=self._tab_buttons[tab]) up_widget=self._tab_row.tabs[tab_id].button)
ba.widget(edit=self._internet_join_text, right_widget=txt) ba.widget(edit=self._internet_join_text, right_widget=txt)
# Attempt to fetch our local address so we have it for # Attempt to fetch our local address so we have it for
@ -447,7 +457,7 @@ class GatherWindow(ba.Window):
# initial query. # initial query.
self._update_internet_tab() self._update_internet_tab()
elif tab == 'local_network': elif tab_id is self.TabID.LOCAL_NETWORK:
c_width = self._scroll_width c_width = self._scroll_width
c_height = self._scroll_height - 20 c_height = self._scroll_height - 20
sub_scroll_height = c_height - 85 sub_scroll_height = c_height - 85
@ -545,14 +555,14 @@ class GatherWindow(ba.Window):
size=(sub_scroll_width, sub_scroll_height)) size=(sub_scroll_width, sub_scroll_height))
self._tab_data = NetScanner(scrollw, self._tab_data = NetScanner(scrollw,
self._tab_buttons[tab], self._tab_row.tabs[tab_id].button,
width=sub_scroll_width) width=sub_scroll_width)
ba.widget(edit=scrollw, ba.widget(edit=scrollw,
autoselect=True, autoselect=True,
up_widget=self._tab_buttons[tab]) up_widget=self._tab_row.tabs[tab_id].button)
elif tab == 'bluetooth': elif tab_id is self.TabID.BLUETOOTH:
c_width = self._scroll_width c_width = self._scroll_width
c_height = 380 c_height = 380
sub_scroll_width = 650 sub_scroll_width = 650
@ -594,7 +604,7 @@ class GatherWindow(ba.Window):
size=(300, 70), size=(300, 70),
autoselect=True, autoselect=True,
label=ba.Lstr(resource=self._r + '.bluetoothHostText')) label=ba.Lstr(resource=self._r + '.bluetoothHostText'))
ba.widget(edit=btn, up_widget=self._tab_buttons[tab]) ba.widget(edit=btn, up_widget=self._tab_row.tabs[tab_id].button)
btn = ba.buttonwidget( btn = ba.buttonwidget(
parent=cnt, parent=cnt,
position=(c_width * 0.5 - sub_scroll_width * 0.5 + 330, position=(c_width * 0.5 - sub_scroll_width * 0.5 + 330,
@ -604,10 +614,10 @@ class GatherWindow(ba.Window):
on_activate_call=ba.Call(ba.screenmessage, on_activate_call=ba.Call(ba.screenmessage,
'FIXME: Not wired up yet.'), 'FIXME: Not wired up yet.'),
label=ba.Lstr(resource=self._r + '.bluetoothJoinText')) label=ba.Lstr(resource=self._r + '.bluetoothJoinText'))
ba.widget(edit=btn, up_widget=self._tab_buttons[tab]) ba.widget(edit=btn, up_widget=self._tab_row.tabs[tab_id].button)
ba.widget(edit=self._tab_buttons[tab], down_widget=btn) ba.widget(edit=self._tab_row.tabs[tab_id].button, down_widget=btn)
elif tab == 'wifi_direct': elif tab_id is self.TabID.WIFI_DIRECT:
c_width = self._scroll_width c_width = self._scroll_width
c_height = self._scroll_height - 20 c_height = self._scroll_height - 20
self._tab_container = cnt = ba.containerwidget( self._tab_container = cnt = ba.containerwidget(
@ -641,7 +651,7 @@ class GatherWindow(ba.Window):
on_activate_call=_ba.android_show_wifi_settings) on_activate_call=_ba.android_show_wifi_settings)
v -= 82 v -= 82
ba.widget(edit=btn, up_widget=self._tab_buttons[tab]) ba.widget(edit=btn, up_widget=self._tab_row.tabs[tab_id].button)
ba.textwidget(parent=cnt, ba.textwidget(parent=cnt,
position=(c_width * 0.5, v), position=(c_width * 0.5, v),
@ -657,7 +667,7 @@ class GatherWindow(ba.Window):
subs=[('${APP_NAME}', subs=[('${APP_NAME}',
ba.Lstr(resource='titleText'))])) ba.Lstr(resource='titleText'))]))
elif tab == 'manual': elif tab_id is self.TabID.MANUAL:
c_width = self._scroll_width c_width = self._scroll_width
c_height = 380 c_height = 380
last_addr = ba.app.config.get('Last Manual Party Connect Address', last_addr = ba.app.config.get('Last Manual Party Connect Address',
@ -787,7 +797,7 @@ class GatherWindow(ba.Window):
position=(c_width * 0.5 - 150, v), position=(c_width * 0.5 - 150, v),
autoselect=True, autoselect=True,
on_activate_call=ba.Call(_connect, txt, txt2)) on_activate_call=ba.Call(_connect, txt, txt2))
ba.widget(edit=txt, up_widget=self._tab_buttons[tab]) ba.widget(edit=txt, up_widget=self._tab_row.tabs[tab_id].button)
ba.textwidget(edit=txt, on_return_press_call=btn.activate) ba.textwidget(edit=txt, on_return_press_call=btn.activate)
ba.textwidget(edit=txt2, on_return_press_call=btn.activate) ba.textwidget(edit=txt2, on_return_press_call=btn.activate)
v -= 45 v -= 45
@ -1897,18 +1907,21 @@ class GatherWindow(ba.Window):
def _save_state(self) -> None: def _save_state(self) -> None:
try: try:
sel = self._root_widget.get_selected_child() sel = self._root_widget.get_selected_child()
selected_tab_ids = [
tab_id for tab_id, tab in self._tab_row.tabs.items()
if sel == tab.button
]
if sel == self._back_button: if sel == self._back_button:
sel_name = 'Back' sel_name = 'Back'
elif sel in list(self._tab_buttons.values()): elif selected_tab_ids:
sel_name = 'Tab:' + list(self._tab_buttons.keys())[list( assert len(selected_tab_ids) == 1
self._tab_buttons.values()).index(sel)] sel_name = f'Tab:{selected_tab_ids[0].value}'
elif sel == self._tab_container: elif sel == self._tab_container:
sel_name = 'TabContainer' sel_name = 'TabContainer'
else: else:
raise ValueError(f'unrecognized selection: \'{sel}\'') raise ValueError(f'unrecognized selection: \'{sel}\'')
ba.app.ui.window_states[self.__class__.__name__] = { ba.app.ui.window_states[self.__class__.__name__] = {
'sel_name': sel_name, 'sel_name': sel_name,
'tab': self._current_tab,
'internet_tab': self._internet_tab 'internet_tab': self._internet_tab
} }
except Exception: except Exception:
@ -1916,21 +1929,32 @@ class GatherWindow(ba.Window):
def _restore_state(self) -> None: def _restore_state(self) -> None:
try: try:
sel: Optional[ba.Widget]
winstate = ba.app.ui.window_states.get(self.__class__.__name__, {}) winstate = ba.app.ui.window_states.get(self.__class__.__name__, {})
sel_name = winstate.get('sel_name', None) sel_name = winstate.get('sel_name', None)
assert isinstance(sel_name, (str, type(None)))
self._internet_tab = winstate.get('internet_tab', 'join') self._internet_tab = winstate.get('internet_tab', 'join')
current_tab = ba.app.config.get('Gather Tab', None) # current_tab = ba.app.config.get('Gather Tab', None)
if current_tab is None or current_tab not in self._tab_buttons: current_tab = self.TabID.ABOUT
current_tab = 'about' try:
stored_tab = self.TabID(ba.app.config.get('Gather Tab'))
if stored_tab in self._tab_row.tabs:
current_tab = stored_tab
except ValueError:
pass
self._set_tab(current_tab) self._set_tab(current_tab)
if sel_name == 'Back': if sel_name == 'Back':
sel = self._back_button sel = self._back_button
elif sel_name == 'TabContainer': elif sel_name == 'TabContainer':
sel = self._tab_container sel = self._tab_container
elif isinstance(sel_name, str) and sel_name.startswith('Tab:'): elif isinstance(sel_name, str) and sel_name.startswith('Tab:'):
sel = self._tab_buttons[sel_name.split(':')[-1]] try:
sel_tab_id = self.TabID(sel_name.split(':')[-1])
except ValueError:
sel_tab_id = self.TabID.ABOUT
sel = self._tab_row.tabs[sel_tab_id].button
else: else:
sel = self._tab_buttons[current_tab] sel = self._tab_row.tabs[current_tab].button
ba.containerwidget(edit=self._root_widget, selected_child=sel) ba.containerwidget(edit=self._root_widget, selected_child=sel)
except Exception: except Exception:
ba.print_exception(f'Error restoring state for {self}.') ba.print_exception(f'Error restoring state for {self}.')

View File

@ -75,69 +75,3 @@ class TabRow(Generic[T]):
ba.playsound(ba.getsound('click01')) ba.playsound(ba.getsound('click01'))
if call is not None: if call is not None:
call(arg) call(arg)
def create_tab_buttons(parent_widget: ba.Widget,
tabs: List[Tuple[str, ba.Lstr]],
pos: Sequence[float],
size: Sequence[float],
on_select_call: Callable[[Any], Any] = None,
return_extra_info: bool = False) -> Dict[str, Any]:
"""(internal)"""
# pylint: disable=too-many-locals
tab_pos_v = pos[1]
tab_buttons = {}
tab_buttons_indexed = []
tab_button_width = float(size[0]) / len(tabs)
# Add a bit more visual spacing as our buttons get narrower.
tab_spacing = (250.0 - tab_button_width) * 0.06
positions = []
sizes = []
h = pos[0]
for _i, tab in enumerate(tabs):
def _tick_and_call(call: Optional[Callable[[Any], Any]],
arg: Any) -> None:
ba.playsound(ba.getsound('click01'))
if call is not None:
call(arg)
pos = (h + tab_spacing * 0.5, tab_pos_v)
size = (tab_button_width - tab_spacing, 50.0)
positions.append(pos)
sizes.append(size)
btn = ba.buttonwidget(parent=parent_widget,
position=pos,
autoselect=True,
button_type='tab',
size=size,
label=tab[1],
enable_sound=False,
on_activate_call=ba.Call(_tick_and_call,
on_select_call, tab[0]))
h += tab_button_width
tab_buttons[tab[0]] = btn
tab_buttons_indexed.append(btn)
if return_extra_info:
return {
'buttons': tab_buttons,
'buttons_indexed': tab_buttons_indexed,
'positions': positions,
'sizes': sizes
}
return tab_buttons
def update_tab_button_colors(tabs: Dict[str, ba.Widget],
selected_tab: str) -> None:
"""(internal)"""
for t_id, tbutton in list(tabs.items()):
if t_id == selected_tab:
ba.buttonwidget(edit=tbutton,
color=(0.5, 0.4, 0.93),
textcolor=(0.85, 0.75, 0.95)) # lit
else:
ba.buttonwidget(edit=tbutton,
color=(0.52, 0.48, 0.63),
textcolor=(0.65, 0.6, 0.7)) # unlit

View File

@ -504,6 +504,7 @@ class WatchWindow(ba.Window):
def _restore_state(self) -> None: def _restore_state(self) -> None:
try: try:
sel: Optional[ba.Widget]
sel_name = ba.app.ui.window_states.get(self.__class__.__name__, sel_name = ba.app.ui.window_states.get(self.__class__.__name__,
{}).get('sel_name') {}).get('sel_name')
assert isinstance(sel_name, (str, type(None))) assert isinstance(sel_name, (str, type(None)))