mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-29 18:53:22 +08:00
Split Gather UI code out into more manageable chunks
This commit is contained in:
parent
6bcd827ced
commit
b248d743f0
7
.idea/dictionaries/ericf.xml
generated
7
.idea/dictionaries/ericf.xml
generated
@ -17,6 +17,7 @@
|
||||
<w>abeb</w>
|
||||
<w>abishort</w>
|
||||
<w>abot</w>
|
||||
<w>abouttab</w>
|
||||
<w>abtn</w>
|
||||
<w>accesstime</w>
|
||||
<w>accountname</w>
|
||||
@ -877,6 +878,7 @@
|
||||
<w>gnode</w>
|
||||
<w>goles</w>
|
||||
<w>goodlist</w>
|
||||
<w>googleplaytab</w>
|
||||
<w>googlevr</w>
|
||||
<w>goosey</w>
|
||||
<w>gotresponse</w>
|
||||
@ -1209,6 +1211,7 @@
|
||||
<w>malformatted</w>
|
||||
<w>mallimportedby</w>
|
||||
<w>mandir</w>
|
||||
<w>manualtab</w>
|
||||
<w>mapdata</w>
|
||||
<w>mapdef</w>
|
||||
<w>mapdefs</w>
|
||||
@ -1340,6 +1343,7 @@
|
||||
<w>ncpu</w>
|
||||
<w>ndbm</w>
|
||||
<w>ndkpath</w>
|
||||
<w>nearbytab</w>
|
||||
<w>neededsettings</w>
|
||||
<w>ness</w>
|
||||
<w>netlink</w>
|
||||
@ -1607,6 +1611,7 @@
|
||||
<w>pthreads</w>
|
||||
<w>ptrans</w>
|
||||
<w>ptype</w>
|
||||
<w>publictab</w>
|
||||
<w>pubsync</w>
|
||||
<w>pucknode</w>
|
||||
<w>pulllist</w>
|
||||
@ -2010,6 +2015,8 @@
|
||||
<w>sysctl</w>
|
||||
<w>syslogmodule</w>
|
||||
<w>tabdefs</w>
|
||||
<w>tabtype</w>
|
||||
<w>tabtypes</w>
|
||||
<w>tabval</w>
|
||||
<w>tagargs</w>
|
||||
<w>tagversion</w>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
### 1.5.27 (20218)
|
||||
### 1.5.27 (20224)
|
||||
- Language functionality has been consolidated into a LanguageSubsystem object at ba.app.lang
|
||||
- ba.get_valid_languages() is now an attr: ba.app.lang.available_languages
|
||||
- Achievement functionality has been consolidated into an AchievementSubsystem object at ba.app.ach
|
||||
@ -7,6 +7,7 @@
|
||||
- Ditto with MetadataSubsystem and ba.app.meta
|
||||
- Ditto with AdsSubsystem and ba.app.ads
|
||||
- Revamped tab-button functionality into a cleaner type-safe class (bastd.ui.tabs.TabRow)
|
||||
- Split Gather-Window tabs out into individual classes for future improvements (bastd.ui.gather.*)
|
||||
|
||||
### 1.5.26 (20217)
|
||||
- Simplified licensing header on python scripts.
|
||||
|
||||
@ -303,7 +303,6 @@
|
||||
"ba_data/python/bastd/ui/__pycache__/debug.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/bastd/ui/__pycache__/feedback.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/bastd/ui/__pycache__/fileselector.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/bastd/ui/__pycache__/gather.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/bastd/ui/__pycache__/getcurrency.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/bastd/ui/__pycache__/getremote.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/bastd/ui/__pycache__/helpui.cpython-38.opt-1.pyc",
|
||||
@ -362,7 +361,20 @@
|
||||
"ba_data/python/bastd/ui/debug.py",
|
||||
"ba_data/python/bastd/ui/feedback.py",
|
||||
"ba_data/python/bastd/ui/fileselector.py",
|
||||
"ba_data/python/bastd/ui/gather.py",
|
||||
"ba_data/python/bastd/ui/gather/__init__.py",
|
||||
"ba_data/python/bastd/ui/gather/__pycache__/__init__.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/bastd/ui/gather/__pycache__/abouttab.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/bastd/ui/gather/__pycache__/bases.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/bastd/ui/gather/__pycache__/googleplaytab.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/bastd/ui/gather/__pycache__/manualtab.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/bastd/ui/gather/__pycache__/nearbytab.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/bastd/ui/gather/__pycache__/publictab.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/bastd/ui/gather/abouttab.py",
|
||||
"ba_data/python/bastd/ui/gather/bases.py",
|
||||
"ba_data/python/bastd/ui/gather/googleplaytab.py",
|
||||
"ba_data/python/bastd/ui/gather/manualtab.py",
|
||||
"ba_data/python/bastd/ui/gather/nearbytab.py",
|
||||
"ba_data/python/bastd/ui/gather/publictab.py",
|
||||
"ba_data/python/bastd/ui/getcurrency.py",
|
||||
"ba_data/python/bastd/ui/getremote.py",
|
||||
"ba_data/python/bastd/ui/helpui.py",
|
||||
|
||||
@ -294,7 +294,13 @@ SCRIPT_TARGETS_PY_PUBLIC = \
|
||||
build/ba_data/python/bastd/ui/debug.py \
|
||||
build/ba_data/python/bastd/ui/feedback.py \
|
||||
build/ba_data/python/bastd/ui/fileselector.py \
|
||||
build/ba_data/python/bastd/ui/gather.py \
|
||||
build/ba_data/python/bastd/ui/gather/__init__.py \
|
||||
build/ba_data/python/bastd/ui/gather/abouttab.py \
|
||||
build/ba_data/python/bastd/ui/gather/bases.py \
|
||||
build/ba_data/python/bastd/ui/gather/googleplaytab.py \
|
||||
build/ba_data/python/bastd/ui/gather/manualtab.py \
|
||||
build/ba_data/python/bastd/ui/gather/nearbytab.py \
|
||||
build/ba_data/python/bastd/ui/gather/publictab.py \
|
||||
build/ba_data/python/bastd/ui/getcurrency.py \
|
||||
build/ba_data/python/bastd/ui/getremote.py \
|
||||
build/ba_data/python/bastd/ui/helpui.py \
|
||||
@ -533,7 +539,13 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
|
||||
build/ba_data/python/bastd/ui/__pycache__/debug.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/bastd/ui/__pycache__/feedback.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/bastd/ui/__pycache__/fileselector.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/bastd/ui/__pycache__/gather.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/bastd/ui/gather/__pycache__/__init__.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/bastd/ui/gather/__pycache__/abouttab.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/bastd/ui/gather/__pycache__/bases.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/bastd/ui/gather/__pycache__/googleplaytab.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/bastd/ui/gather/__pycache__/manualtab.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/bastd/ui/gather/__pycache__/nearbytab.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/bastd/ui/gather/__pycache__/publictab.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/bastd/ui/__pycache__/getcurrency.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/bastd/ui/__pycache__/getremote.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/bastd/ui/__pycache__/helpui.cpython-38.opt-1.pyc \
|
||||
|
||||
@ -11,9 +11,9 @@ import inspect
|
||||
from typing import TYPE_CHECKING, TypeVar, Protocol
|
||||
|
||||
from efro.terminal import Clr
|
||||
import _ba
|
||||
from ba._error import print_error, print_exception
|
||||
from ba._enums import TimeType
|
||||
import _ba
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Type, Optional
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
271
assets/src/ba_data/python/bastd/ui/gather/__init__.py
Normal file
271
assets/src/ba_data/python/bastd/ui/gather/__init__.py
Normal file
@ -0,0 +1,271 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Provides UI for inviting/joining friends."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import _ba
|
||||
import ba
|
||||
from bastd.ui.gather.abouttab import AboutGatherTab
|
||||
from bastd.ui.gather.manualtab import ManualGatherTab
|
||||
from bastd.ui.gather.googleplaytab import GooglePlayGatherTab
|
||||
from bastd.ui.gather.publictab import PublicGatherTab
|
||||
from bastd.ui.gather.nearbytab import NearbyGatherTab
|
||||
from bastd.ui.tabs import TabRow
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import (Any, Optional, Tuple, Dict, List, Union, Callable,
|
||||
Type)
|
||||
from bastd.ui.gather.bases import GatherTab
|
||||
|
||||
|
||||
class GatherWindow(ba.Window):
|
||||
"""Window for joining/inviting friends."""
|
||||
|
||||
class TabID(Enum):
|
||||
"""Our available tab types."""
|
||||
ABOUT = 'about'
|
||||
INTERNET = 'internet'
|
||||
GOOGLE_PLAY = 'google_play'
|
||||
LOCAL_NETWORK = 'local_network'
|
||||
MANUAL = 'manual'
|
||||
|
||||
def __init__(self,
|
||||
transition: Optional[str] = 'in_right',
|
||||
origin_widget: ba.Widget = None):
|
||||
# pylint: disable=too-many-statements
|
||||
# pylint: disable=too-many-locals
|
||||
ba.set_analytics_screen('Gather 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('Gather')
|
||||
_ba.set_party_icon_always_visible(True)
|
||||
uiscale = ba.app.ui.uiscale
|
||||
self._width = 1240 if uiscale is ba.UIScale.SMALL else 1040
|
||||
x_offs = 100 if uiscale is ba.UIScale.SMALL else 0
|
||||
self._height = (582 if uiscale is ba.UIScale.SMALL else
|
||||
680 if uiscale is ba.UIScale.MEDIUM else 800)
|
||||
self._current_tab: Optional[GatherWindow.TabID] = None
|
||||
extra_top = 20 if uiscale is ba.UIScale.SMALL else 0
|
||||
self._r = 'gatherWindow'
|
||||
|
||||
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, -11) if uiscale is ba.UIScale.SMALL else (
|
||||
0, 0) 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,
|
||||
position=(70 + x_offs, self._height - 74),
|
||||
size=(140, 60),
|
||||
scale=1.1,
|
||||
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.buttonwidget(edit=btn,
|
||||
button_type='backSmall',
|
||||
position=(70 + x_offs, self._height - 78),
|
||||
size=(60, 60),
|
||||
label=ba.charstr(ba.SpecialChar.BACK))
|
||||
|
||||
ba.textwidget(parent=self._root_widget,
|
||||
position=(self._width * 0.5, self._height - 42),
|
||||
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=550)
|
||||
|
||||
platform = ba.app.platform
|
||||
subplatform = ba.app.subplatform
|
||||
|
||||
scroll_buffer_h = 130 + 2 * x_offs
|
||||
tab_buffer_h = 250 + 2 * x_offs
|
||||
|
||||
# Build up the set of tabs we want.
|
||||
tabdefs: List[Tuple[GatherWindow.TabID, ba.Lstr]] = [
|
||||
(self.TabID.ABOUT, ba.Lstr(resource=self._r + '.aboutText'))
|
||||
]
|
||||
if _ba.get_account_misc_read_val('enablePublicParties', True):
|
||||
tabdefs.append((self.TabID.INTERNET,
|
||||
ba.Lstr(resource=self._r + '.internetText')))
|
||||
if platform == 'android' and subplatform == 'google':
|
||||
tabdefs.append((self.TabID.GOOGLE_PLAY,
|
||||
ba.Lstr(resource=self._r + '.googlePlayText')))
|
||||
tabdefs.append((self.TabID.LOCAL_NETWORK,
|
||||
ba.Lstr(resource=self._r + '.localNetworkText')))
|
||||
tabdefs.append(
|
||||
(self.TabID.MANUAL, ba.Lstr(resource=self._r + '.manualText')))
|
||||
|
||||
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)
|
||||
|
||||
# Now instantiate handlers for these tabs.
|
||||
tabtypes: Dict[GatherWindow.TabID, Type[GatherTab]] = {
|
||||
self.TabID.ABOUT: AboutGatherTab,
|
||||
self.TabID.MANUAL: ManualGatherTab,
|
||||
self.TabID.GOOGLE_PLAY: GooglePlayGatherTab,
|
||||
self.TabID.INTERNET: PublicGatherTab,
|
||||
self.TabID.LOCAL_NETWORK: NearbyGatherTab
|
||||
}
|
||||
self._tabs: Dict[GatherWindow.TabID, GatherTab] = {}
|
||||
for tab_id in self._tab_row.tabs:
|
||||
tabtype = tabtypes.get(tab_id)
|
||||
if tabtype is not None:
|
||||
self._tabs[tab_id] = tabtype(self)
|
||||
|
||||
if ba.app.ui.use_toolbars:
|
||||
ba.widget(edit=self._tab_row.tabs[tabdefs[-1][0]].button,
|
||||
right_widget=_ba.get_special_widget('party_button'))
|
||||
if uiscale is ba.UIScale.SMALL:
|
||||
ba.widget(edit=self._tab_row.tabs[tabdefs[0][0]].button,
|
||||
left_widget=_ba.get_special_widget('back_button'))
|
||||
|
||||
self._scroll_width = self._width - scroll_buffer_h
|
||||
self._scroll_height = self._height - 180.0
|
||||
|
||||
self._scroll_left = (self._width - self._scroll_width) * 0.5
|
||||
self._scroll_bottom = self._height - self._scroll_height - 79 - 48
|
||||
buffer_h = 10
|
||||
buffer_v = 4
|
||||
|
||||
# Not actually using a scroll widget anymore; just an image.
|
||||
ba.imagewidget(parent=self._root_widget,
|
||||
position=(self._scroll_left - buffer_h,
|
||||
self._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 __del__(self) -> None:
|
||||
_ba.set_party_icon_always_visible(False)
|
||||
|
||||
def _set_tab(self, tab_id: TabID) -> None:
|
||||
if self._current_tab is tab_id:
|
||||
return
|
||||
prev_tab_id = self._current_tab
|
||||
self._current_tab = tab_id
|
||||
|
||||
# We wanna preserve our current tab between runs.
|
||||
cfg = ba.app.config
|
||||
cfg['Gather Tab'] = tab_id.value
|
||||
cfg.commit()
|
||||
|
||||
# Update tab colors based on which is selected.
|
||||
self._tab_row.update_appearance(tab_id)
|
||||
|
||||
if prev_tab_id is not None:
|
||||
prev_tab = self._tabs.get(prev_tab_id)
|
||||
if prev_tab is not None:
|
||||
prev_tab.on_deactivate()
|
||||
|
||||
# Clear up prev container if it hasn't been done.
|
||||
if self._tab_container:
|
||||
self._tab_container.delete()
|
||||
|
||||
tab = self._tabs.get(tab_id)
|
||||
if tab is not None:
|
||||
self._tab_container = tab.on_activate(
|
||||
self._root_widget,
|
||||
self._tab_row.tabs[tab_id].button,
|
||||
self._scroll_width,
|
||||
self._scroll_height,
|
||||
self._scroll_left,
|
||||
self._scroll_bottom,
|
||||
)
|
||||
return
|
||||
|
||||
def _save_state(self) -> None:
|
||||
try:
|
||||
for tab in self._tabs.values():
|
||||
tab.save_state()
|
||||
|
||||
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:
|
||||
sel_name = 'Back'
|
||||
elif selected_tab_ids:
|
||||
assert len(selected_tab_ids) == 1
|
||||
sel_name = f'Tab:{selected_tab_ids[0].value}'
|
||||
elif sel == self._tab_container:
|
||||
sel_name = 'TabContainer'
|
||||
else:
|
||||
raise ValueError(f'unrecognized selection: \'{sel}\'')
|
||||
ba.app.ui.window_states[self.__class__.__name__] = {
|
||||
'sel_name': sel_name,
|
||||
}
|
||||
except Exception:
|
||||
ba.print_exception(f'Error saving state for {self}.')
|
||||
|
||||
def _restore_state(self) -> None:
|
||||
try:
|
||||
for tab in self._tabs.values():
|
||||
tab.restore_state()
|
||||
|
||||
sel: Optional[ba.Widget]
|
||||
winstate = ba.app.ui.window_states.get(self.__class__.__name__, {})
|
||||
sel_name = winstate.get('sel_name', None)
|
||||
assert isinstance(sel_name, (str, type(None)))
|
||||
current_tab = self.TabID.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)
|
||||
if sel_name == 'Back':
|
||||
sel = self._back_button
|
||||
elif sel_name == 'TabContainer':
|
||||
sel = self._tab_container
|
||||
elif isinstance(sel_name, str) and sel_name.startswith('Tab:'):
|
||||
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:
|
||||
sel = self._tab_row.tabs[current_tab].button
|
||||
ba.containerwidget(edit=self._root_widget, selected_child=sel)
|
||||
except Exception:
|
||||
ba.print_exception(f'Error restoring state for {self}.')
|
||||
|
||||
def _back(self) -> None:
|
||||
from bastd.ui.mainmenu import MainMenuWindow
|
||||
self._save_state()
|
||||
ba.containerwidget(edit=self._root_widget,
|
||||
transition=self._transition_out)
|
||||
ba.app.ui.set_main_menu_window(
|
||||
MainMenuWindow(transition='in_left').get_root_widget())
|
||||
110
assets/src/ba_data/python/bastd/ui/gather/abouttab.py
Normal file
110
assets/src/ba_data/python/bastd/ui/gather/abouttab.py
Normal file
@ -0,0 +1,110 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Defines the about tab in the gather UI."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
import _ba
|
||||
from bastd.ui.gather.bases import GatherTab
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Optional
|
||||
from bastd.ui.gather import GatherWindow
|
||||
|
||||
|
||||
class AboutGatherTab(GatherTab):
|
||||
"""The about tab in the gather UI"""
|
||||
|
||||
def __init__(self, window: GatherWindow) -> None:
|
||||
super().__init__(window)
|
||||
self._container: Optional[ba.Widget] = None
|
||||
|
||||
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:
|
||||
message = ba.Lstr(resource='gatherWindow.aboutDescriptionText',
|
||||
subs=[('${PARTY}',
|
||||
ba.charstr(ba.SpecialChar.PARTY_ICON)),
|
||||
('${BUTTON}',
|
||||
ba.charstr(ba.SpecialChar.TOP_BUTTON))])
|
||||
|
||||
# Let's not talk about sharing in vr-mode; its tricky to fit more
|
||||
# than one head in a VR-headset ;-)
|
||||
if not ba.app.vr_mode:
|
||||
message = ba.Lstr(
|
||||
value='${A}\n\n${B}',
|
||||
subs=[('${A}', message),
|
||||
('${B}',
|
||||
ba.Lstr(resource='gatherWindow.'
|
||||
'aboutDescriptionLocalMultiplayerExtraText'))])
|
||||
string_height = 400
|
||||
include_invite = True
|
||||
msc_scale = 1.1
|
||||
c_height_2 = min(region_height, string_height * msc_scale + 100)
|
||||
try_tickets = _ba.get_account_misc_read_val('friendTryTickets', None)
|
||||
if try_tickets is None:
|
||||
include_invite = False
|
||||
self._container = ba.containerwidget(
|
||||
parent=parent_widget,
|
||||
position=(region_left,
|
||||
region_bottom + (region_height - c_height_2) * 0.5),
|
||||
size=(region_width, c_height_2),
|
||||
background=False,
|
||||
selectable=include_invite)
|
||||
ba.widget(edit=self._container, up_widget=tab_button)
|
||||
|
||||
ba.textwidget(parent=self._container,
|
||||
position=(region_width * 0.5, c_height_2 *
|
||||
(0.58 if include_invite else 0.5)),
|
||||
color=(0.6, 1.0, 0.6),
|
||||
scale=msc_scale,
|
||||
size=(0, 0),
|
||||
maxwidth=region_width * 0.9,
|
||||
max_height=c_height_2 * (0.7 if include_invite else 0.9),
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
text=message)
|
||||
|
||||
if include_invite:
|
||||
ba.textwidget(parent=self._container,
|
||||
position=(region_width * 0.57, 35),
|
||||
color=(0, 1, 0),
|
||||
scale=0.6,
|
||||
size=(0, 0),
|
||||
maxwidth=region_width * 0.5,
|
||||
h_align='right',
|
||||
v_align='center',
|
||||
flatness=1.0,
|
||||
text=ba.Lstr(
|
||||
resource='gatherWindow.inviteAFriendText',
|
||||
subs=[('${COUNT}', str(try_tickets))]))
|
||||
ba.buttonwidget(
|
||||
parent=self._container,
|
||||
position=(region_width * 0.59, 10),
|
||||
size=(230, 50),
|
||||
color=(0.54, 0.42, 0.56),
|
||||
textcolor=(0, 1, 0),
|
||||
label=ba.Lstr(resource='gatherWindow.inviteFriendsText',
|
||||
fallback_resource=(
|
||||
'gatherWindow.getFriendInviteCodeText')),
|
||||
autoselect=True,
|
||||
on_activate_call=ba.WeakCall(self._invite_to_try_press),
|
||||
up_widget=tab_button)
|
||||
return self._container
|
||||
|
||||
def _invite_to_try_press(self) -> None:
|
||||
from bastd.ui.account import show_sign_in_prompt
|
||||
from bastd.ui.appinvite import handle_app_invites_press
|
||||
if _ba.get_account_state() != 'signed_in':
|
||||
show_sign_in_prompt()
|
||||
return
|
||||
handle_app_invites_press()
|
||||
52
assets/src/ba_data/python/bastd/ui/gather/bases.py
Normal file
52
assets/src/ba_data/python/bastd/ui/gather/bases.py
Normal file
@ -0,0 +1,52 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Provides UI for inviting/joining friends."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import weakref
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bastd.ui.gather import GatherWindow
|
||||
|
||||
|
||||
class GatherTab:
|
||||
"""Defines a tab for use in the gather UI."""
|
||||
|
||||
def __init__(self, window: GatherWindow) -> None:
|
||||
self._window = weakref.ref(window)
|
||||
|
||||
@property
|
||||
def window(self) -> GatherWindow:
|
||||
"""The GatherWindow that this tab belongs to."""
|
||||
window = self._window()
|
||||
if window is None:
|
||||
raise ba.NotFoundError("GatherTab's window no longer exists.")
|
||||
return window
|
||||
|
||||
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:
|
||||
"""Called when the tab becomes the active one.
|
||||
|
||||
The tab should create and return a container widget covering the
|
||||
specified region.
|
||||
"""
|
||||
|
||||
def on_deactivate(self) -> None:
|
||||
"""Called when the tab will no longer be the active one."""
|
||||
|
||||
def save_state(self) -> None:
|
||||
"""Called when the parent window is saving state."""
|
||||
|
||||
def restore_state(self) -> None:
|
||||
"""Called when the parent window is restoring state."""
|
||||
86
assets/src/ba_data/python/bastd/ui/gather/googleplaytab.py
Normal file
86
assets/src/ba_data/python/bastd/ui/gather/googleplaytab.py
Normal file
@ -0,0 +1,86 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Defines the Google Play tab in the gather UI."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import _ba
|
||||
import ba
|
||||
from bastd.ui.gather.bases import GatherTab
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Optional
|
||||
from bastd.ui.gather import GatherWindow
|
||||
|
||||
|
||||
class GooglePlayGatherTab(GatherTab):
|
||||
"""The public tab in the gather UI"""
|
||||
|
||||
def __init__(self, window: GatherWindow) -> None:
|
||||
super().__init__(window)
|
||||
self._container: Optional[ba.Widget] = None
|
||||
|
||||
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 = 380.0
|
||||
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.0
|
||||
ba.textwidget(
|
||||
parent=self._container,
|
||||
position=(c_width * 0.5, v - 140.0),
|
||||
color=(0.6, 1.0, 0.6),
|
||||
scale=1.3,
|
||||
size=(0.0, 0.0),
|
||||
maxwidth=c_width * 0.9,
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
text=ba.Lstr(resource='googleMultiplayerDiscontinuedText'))
|
||||
return self._container
|
||||
|
||||
def _on_google_play_show_invites_press(self) -> None:
|
||||
from bastd.ui import account
|
||||
if (_ba.get_account_state() != 'signed_in'
|
||||
or _ba.get_account_type() != 'Google Play'):
|
||||
account.show_sign_in_prompt('Google Play')
|
||||
else:
|
||||
_ba.show_invites_ui()
|
||||
|
||||
def _on_google_play_invite_press(self) -> None:
|
||||
from bastd.ui.confirm import ConfirmWindow
|
||||
from bastd.ui.account import show_sign_in_prompt
|
||||
if (_ba.get_account_state() != 'signed_in'
|
||||
or _ba.get_account_type() != 'Google Play'):
|
||||
show_sign_in_prompt('Google Play')
|
||||
else:
|
||||
# If there's google play people connected to us, inform the user
|
||||
# that they will get disconnected. Otherwise just go ahead.
|
||||
google_player_count = (_ba.get_google_play_party_client_count())
|
||||
if google_player_count > 0:
|
||||
ConfirmWindow(
|
||||
ba.Lstr(resource='gatherWindow.'
|
||||
'googlePlayReInviteText',
|
||||
subs=[('${COUNT}', str(google_player_count))]),
|
||||
lambda: ba.timer(
|
||||
0.2, _ba.invite_players, timetype=ba.TimeType.REAL),
|
||||
width=500,
|
||||
height=150,
|
||||
ok_text=ba.Lstr(resource='gatherWindow.'
|
||||
'googlePlayInviteText'))
|
||||
else:
|
||||
ba.timer(0.1, _ba.invite_players, timetype=ba.TimeType.REAL)
|
||||
426
assets/src/ba_data/python/bastd/ui/gather/manualtab.py
Normal file
426
assets/src/ba_data/python/bastd/ui/gather/manualtab.py
Normal file
@ -0,0 +1,426 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Defines the manual tab in the gather UI."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import threading
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
import _ba
|
||||
import ba
|
||||
from bastd.ui.gather.bases import GatherTab
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Callable, Optional, Any, Union, Dict
|
||||
from bastd.ui.gather import GatherWindow
|
||||
|
||||
|
||||
def _safe_set_text(txt: Optional[ba.Widget],
|
||||
val: Union[str, ba.Lstr],
|
||||
success: bool = True) -> None:
|
||||
if txt:
|
||||
ba.textwidget(edit=txt,
|
||||
text=val,
|
||||
color=(0, 1, 0) if success else (1, 1, 0))
|
||||
|
||||
|
||||
class _HostLookupThread(threading.Thread):
|
||||
"""Thread to fetch an addr."""
|
||||
|
||||
def __init__(self, name: str, port: int,
|
||||
call: Callable[[Optional[str], int], Any]):
|
||||
super().__init__()
|
||||
self._name = name
|
||||
self._port = port
|
||||
self._call = call
|
||||
|
||||
def run(self) -> None:
|
||||
result: Optional[str]
|
||||
try:
|
||||
import socket
|
||||
result = socket.gethostbyname(self._name)
|
||||
except Exception:
|
||||
result = None
|
||||
ba.pushcall(lambda: self._call(result, self._port),
|
||||
from_other_thread=True)
|
||||
|
||||
|
||||
class ManualGatherTab(GatherTab):
|
||||
"""The manual tab in the gather UI"""
|
||||
|
||||
def __init__(self, window: GatherWindow) -> None:
|
||||
super().__init__(window)
|
||||
self._check_button: Optional[ba.Widget] = None
|
||||
self._doing_access_check: Optional[bool] = None
|
||||
self._access_check_count: Optional[int] = None
|
||||
self._t_addr: Optional[ba.Widget] = None
|
||||
self._t_accessible: Optional[ba.Widget] = None
|
||||
self._t_accessible_extra: Optional[ba.Widget] = None
|
||||
self._access_check_timer: Optional[ba.Timer] = None
|
||||
self._checking_state_text: Optional[ba.Widget] = None
|
||||
self._container: Optional[ba.Widget] = None
|
||||
|
||||
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 = 380
|
||||
last_addr = ba.app.config.get('Last Manual Party Connect Address', '')
|
||||
|
||||
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
|
||||
ba.textwidget(parent=self._container,
|
||||
position=(c_width * 0.5, v),
|
||||
color=(0.6, 1.0, 0.6),
|
||||
scale=1.3,
|
||||
size=(0, 0),
|
||||
maxwidth=c_width * 0.9,
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'manualDescriptionText'))
|
||||
v -= 30
|
||||
v -= 70
|
||||
ba.textwidget(parent=self._container,
|
||||
position=(c_width * 0.5 - 260 - 50, v),
|
||||
color=(0.6, 1.0, 0.6),
|
||||
scale=1.0,
|
||||
size=(0, 0),
|
||||
maxwidth=130,
|
||||
h_align='right',
|
||||
v_align='center',
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'manualAddressText'))
|
||||
txt = ba.textwidget(parent=self._container,
|
||||
editable=True,
|
||||
description=ba.Lstr(resource='gatherWindow.'
|
||||
'manualAddressText'),
|
||||
position=(c_width * 0.5 - 240 - 50, v - 30),
|
||||
text=last_addr,
|
||||
autoselect=True,
|
||||
v_align='center',
|
||||
scale=1.0,
|
||||
size=(420, 60))
|
||||
ba.textwidget(parent=self._container,
|
||||
position=(c_width * 0.5 - 260 + 490, v),
|
||||
color=(0.6, 1.0, 0.6),
|
||||
scale=1.0,
|
||||
size=(0, 0),
|
||||
maxwidth=80,
|
||||
h_align='right',
|
||||
v_align='center',
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'portText'))
|
||||
txt2 = ba.textwidget(parent=self._container,
|
||||
editable=True,
|
||||
description=ba.Lstr(resource='gatherWindow.'
|
||||
'portText'),
|
||||
text='43210',
|
||||
autoselect=True,
|
||||
max_chars=5,
|
||||
position=(c_width * 0.5 - 240 + 490, v - 30),
|
||||
v_align='center',
|
||||
scale=1.0,
|
||||
size=(170, 60))
|
||||
|
||||
v -= 110
|
||||
|
||||
btn = ba.buttonwidget(parent=self._container,
|
||||
size=(300, 70),
|
||||
label=ba.Lstr(resource='gatherWindow.'
|
||||
'manualConnectText'),
|
||||
position=(c_width * 0.5 - 150, v),
|
||||
autoselect=True,
|
||||
on_activate_call=ba.Call(self._connect, txt,
|
||||
txt2))
|
||||
ba.widget(edit=txt, up_widget=tab_button)
|
||||
ba.textwidget(edit=txt, on_return_press_call=btn.activate)
|
||||
ba.textwidget(edit=txt2, on_return_press_call=btn.activate)
|
||||
v -= 45
|
||||
|
||||
self._check_button = ba.textwidget(
|
||||
parent=self._container,
|
||||
size=(250, 60),
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'showMyAddressText'),
|
||||
v_align='center',
|
||||
h_align='center',
|
||||
click_activate=True,
|
||||
position=(c_width * 0.5 - 125, v - 30),
|
||||
autoselect=True,
|
||||
color=(0.5, 0.9, 0.5),
|
||||
scale=0.8,
|
||||
selectable=True,
|
||||
on_activate_call=ba.Call(self._on_show_my_address_button_press, v,
|
||||
self._container, c_width))
|
||||
return self._container
|
||||
|
||||
def on_deactivate(self) -> None:
|
||||
self._access_check_timer = None
|
||||
|
||||
def _connect(self, textwidget: ba.Widget,
|
||||
port_textwidget: ba.Widget) -> None:
|
||||
addr = cast(str, ba.textwidget(query=textwidget))
|
||||
if addr == '':
|
||||
ba.screenmessage(
|
||||
ba.Lstr(resource='internal.invalidAddressErrorText'),
|
||||
color=(1, 0, 0))
|
||||
ba.playsound(ba.getsound('error'))
|
||||
return
|
||||
try:
|
||||
port = int(cast(str, ba.textwidget(query=port_textwidget)))
|
||||
except ValueError:
|
||||
port = -1
|
||||
if port > 65535 or port < 0:
|
||||
ba.screenmessage(ba.Lstr(resource='internal.invalidPortErrorText'),
|
||||
color=(1, 0, 0))
|
||||
ba.playsound(ba.getsound('error'))
|
||||
return
|
||||
|
||||
_HostLookupThread(name=addr,
|
||||
port=port,
|
||||
call=ba.WeakCall(self._host_lookup_result)).start()
|
||||
|
||||
def _host_lookup_result(self, resolved_address: Optional[str],
|
||||
port: int) -> None:
|
||||
if resolved_address is None:
|
||||
ba.screenmessage(
|
||||
ba.Lstr(resource='internal.unableToResolveHostText'),
|
||||
color=(1, 0, 0))
|
||||
ba.playsound(ba.getsound('error'))
|
||||
else:
|
||||
# Store for later.
|
||||
config = ba.app.config
|
||||
config['Last Manual Party Connect Address'] = resolved_address
|
||||
config.commit()
|
||||
_ba.connect_to_party(resolved_address, port=port)
|
||||
|
||||
def _run_addr_fetch(self) -> None:
|
||||
try:
|
||||
# FIXME: Update this to work with IPv6.
|
||||
import socket
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.connect(('8.8.8.8', 80))
|
||||
val = sock.getsockname()[0]
|
||||
sock.close()
|
||||
ba.pushcall(
|
||||
ba.Call(
|
||||
_safe_set_text,
|
||||
self._checking_state_text,
|
||||
val,
|
||||
),
|
||||
from_other_thread=True,
|
||||
)
|
||||
except Exception as exc:
|
||||
err_str = str(exc)
|
||||
|
||||
# FIXME: Should look at exception types here,
|
||||
# not strings.
|
||||
if 'Network is unreachable' in err_str:
|
||||
ba.pushcall(ba.Call(
|
||||
_safe_set_text, self._checking_state_text,
|
||||
ba.Lstr(resource='gatherWindow.'
|
||||
'noConnectionText'), False),
|
||||
from_other_thread=True)
|
||||
else:
|
||||
ba.pushcall(ba.Call(
|
||||
_safe_set_text, self._checking_state_text,
|
||||
ba.Lstr(resource='gatherWindow.'
|
||||
'addressFetchErrorText'), False),
|
||||
from_other_thread=True)
|
||||
ba.pushcall(ba.Call(ba.print_error,
|
||||
'error in AddrFetchThread: ' + str(exc)),
|
||||
from_other_thread=True)
|
||||
|
||||
def _on_show_my_address_button_press(self, v2: float,
|
||||
container: Optional[ba.Widget],
|
||||
c_width: float) -> None:
|
||||
if not container:
|
||||
return
|
||||
|
||||
tscl = 0.85
|
||||
tspc = 25
|
||||
|
||||
ba.playsound(ba.getsound('swish'))
|
||||
ba.textwidget(parent=container,
|
||||
position=(c_width * 0.5 - 10, v2),
|
||||
color=(0.6, 1.0, 0.6),
|
||||
scale=tscl,
|
||||
size=(0, 0),
|
||||
maxwidth=c_width * 0.45,
|
||||
flatness=1.0,
|
||||
h_align='right',
|
||||
v_align='center',
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'manualYourLocalAddressText'))
|
||||
self._checking_state_text = ba.textwidget(
|
||||
parent=container,
|
||||
position=(c_width * 0.5, v2),
|
||||
color=(0.5, 0.5, 0.5),
|
||||
scale=tscl,
|
||||
size=(0, 0),
|
||||
maxwidth=c_width * 0.45,
|
||||
flatness=1.0,
|
||||
h_align='left',
|
||||
v_align='center',
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'checkingText'))
|
||||
|
||||
threading.Thread(target=self._run_addr_fetch).start()
|
||||
|
||||
v2 -= tspc
|
||||
ba.textwidget(parent=container,
|
||||
position=(c_width * 0.5 - 10, v2),
|
||||
color=(0.6, 1.0, 0.6),
|
||||
scale=tscl,
|
||||
size=(0, 0),
|
||||
maxwidth=c_width * 0.45,
|
||||
flatness=1.0,
|
||||
h_align='right',
|
||||
v_align='center',
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'manualYourAddressFromInternetText'))
|
||||
|
||||
t_addr = ba.textwidget(parent=container,
|
||||
position=(c_width * 0.5, v2),
|
||||
color=(0.5, 0.5, 0.5),
|
||||
scale=tscl,
|
||||
size=(0, 0),
|
||||
maxwidth=c_width * 0.45,
|
||||
h_align='left',
|
||||
v_align='center',
|
||||
flatness=1.0,
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'checkingText'))
|
||||
v2 -= tspc
|
||||
ba.textwidget(parent=container,
|
||||
position=(c_width * 0.5 - 10, v2),
|
||||
color=(0.6, 1.0, 0.6),
|
||||
scale=tscl,
|
||||
size=(0, 0),
|
||||
maxwidth=c_width * 0.45,
|
||||
flatness=1.0,
|
||||
h_align='right',
|
||||
v_align='center',
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'manualJoinableFromInternetText'))
|
||||
|
||||
t_accessible = ba.textwidget(parent=container,
|
||||
position=(c_width * 0.5, v2),
|
||||
color=(0.5, 0.5, 0.5),
|
||||
scale=tscl,
|
||||
size=(0, 0),
|
||||
maxwidth=c_width * 0.45,
|
||||
flatness=1.0,
|
||||
h_align='left',
|
||||
v_align='center',
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'checkingText'))
|
||||
v2 -= 28
|
||||
t_accessible_extra = ba.textwidget(parent=container,
|
||||
position=(c_width * 0.5, v2),
|
||||
color=(1, 0.5, 0.2),
|
||||
scale=0.7,
|
||||
size=(0, 0),
|
||||
maxwidth=c_width * 0.9,
|
||||
flatness=1.0,
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
text='')
|
||||
|
||||
self._doing_access_check = False
|
||||
self._access_check_count = 0 # Cap our refreshes eventually.
|
||||
self._access_check_timer = ba.Timer(
|
||||
10.0,
|
||||
ba.WeakCall(self._access_check_update, t_addr, t_accessible,
|
||||
t_accessible_extra),
|
||||
repeat=True,
|
||||
timetype=ba.TimeType.REAL)
|
||||
|
||||
# Kick initial off.
|
||||
self._access_check_update(t_addr, t_accessible, t_accessible_extra)
|
||||
if self._check_button:
|
||||
self._check_button.delete()
|
||||
|
||||
def _access_check_update(self, t_addr: ba.Widget, t_accessible: ba.Widget,
|
||||
t_accessible_extra: ba.Widget) -> None:
|
||||
from ba.internal import master_server_get
|
||||
|
||||
# If we don't have an outstanding query, start one..
|
||||
assert self._doing_access_check is not None
|
||||
assert self._access_check_count is not None
|
||||
if not self._doing_access_check and self._access_check_count < 100:
|
||||
self._doing_access_check = True
|
||||
self._access_check_count += 1
|
||||
self._t_addr = t_addr
|
||||
self._t_accessible = t_accessible
|
||||
self._t_accessible_extra = t_accessible_extra
|
||||
master_server_get('bsAccessCheck', {'b': ba.app.build_number},
|
||||
callback=ba.WeakCall(
|
||||
self._on_accessible_response))
|
||||
|
||||
def _on_accessible_response(self, data: Optional[Dict[str, Any]]) -> None:
|
||||
t_addr = self._t_addr
|
||||
t_accessible = self._t_accessible
|
||||
t_accessible_extra = self._t_accessible_extra
|
||||
self._doing_access_check = False
|
||||
color_bad = (1, 1, 0)
|
||||
color_good = (0, 1, 0)
|
||||
if data is None or 'address' not in data or 'accessible' not in data:
|
||||
if t_addr:
|
||||
ba.textwidget(edit=t_addr,
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'noConnectionText'),
|
||||
color=color_bad)
|
||||
if t_accessible:
|
||||
ba.textwidget(edit=t_accessible,
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'noConnectionText'),
|
||||
color=color_bad)
|
||||
if t_accessible_extra:
|
||||
ba.textwidget(edit=t_accessible_extra,
|
||||
text='',
|
||||
color=color_bad)
|
||||
return
|
||||
if t_addr:
|
||||
ba.textwidget(edit=t_addr, text=data['address'], color=color_good)
|
||||
if t_accessible:
|
||||
if data['accessible']:
|
||||
ba.textwidget(edit=t_accessible,
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'manualJoinableYesText'),
|
||||
color=color_good)
|
||||
if t_accessible_extra:
|
||||
ba.textwidget(edit=t_accessible_extra,
|
||||
text='',
|
||||
color=color_good)
|
||||
else:
|
||||
ba.textwidget(
|
||||
edit=t_accessible,
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'manualJoinableNoWithAsteriskText'),
|
||||
color=color_bad,
|
||||
)
|
||||
if t_accessible_extra:
|
||||
ba.textwidget(
|
||||
edit=t_accessible_extra,
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'manualRouterForwardingText',
|
||||
subs=[('${PORT}',
|
||||
str(_ba.get_game_port()))]),
|
||||
color=color_bad,
|
||||
)
|
||||
136
assets/src/ba_data/python/bastd/ui/gather/nearbytab.py
Normal file
136
assets/src/ba_data/python/bastd/ui/gather/nearbytab.py
Normal file
@ -0,0 +1,136 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Defines the nearby tab in the gather UI."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import ba
|
||||
import _ba
|
||||
from bastd.ui.gather.bases import GatherTab
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Optional, Dict, Any
|
||||
from bastd.ui.gather import GatherWindow
|
||||
|
||||
|
||||
class NetScanner:
|
||||
"""Class for scanning for games on the lan."""
|
||||
|
||||
def __init__(self, scrollwidget: ba.Widget, tab_button: ba.Widget,
|
||||
width: float):
|
||||
self._scrollwidget = scrollwidget
|
||||
self._tab_button = tab_button
|
||||
self._columnwidget = ba.columnwidget(parent=self._scrollwidget,
|
||||
border=2,
|
||||
margin=0,
|
||||
left_border=10)
|
||||
ba.widget(edit=self._columnwidget, up_widget=tab_button)
|
||||
self._width = width
|
||||
self._last_selected_host: Optional[Dict[str, Any]] = None
|
||||
|
||||
self._update_timer = ba.Timer(1.0,
|
||||
ba.WeakCall(self.update),
|
||||
timetype=ba.TimeType.REAL,
|
||||
repeat=True)
|
||||
# Go ahead and run a few *almost* immediately so we don't
|
||||
# have to wait a second.
|
||||
self.update()
|
||||
ba.timer(0.25, ba.WeakCall(self.update), timetype=ba.TimeType.REAL)
|
||||
|
||||
def __del__(self) -> None:
|
||||
_ba.end_host_scanning()
|
||||
|
||||
def _on_select(self, host: Dict[str, Any]) -> None:
|
||||
self._last_selected_host = host
|
||||
|
||||
def _on_activate(self, host: Dict[str, Any]) -> None:
|
||||
_ba.connect_to_party(host['address'])
|
||||
|
||||
def update(self) -> None:
|
||||
"""(internal)"""
|
||||
t_scale = 1.6
|
||||
for child in self._columnwidget.get_children():
|
||||
child.delete()
|
||||
|
||||
# Grab this now this since adding widgets will change it.
|
||||
last_selected_host = self._last_selected_host
|
||||
hosts = _ba.host_scan_cycle()
|
||||
for i, host in enumerate(hosts):
|
||||
txt3 = ba.textwidget(parent=self._columnwidget,
|
||||
size=(self._width / t_scale, 30),
|
||||
selectable=True,
|
||||
color=(1, 1, 1),
|
||||
on_select_call=ba.Call(self._on_select, host),
|
||||
on_activate_call=ba.Call(
|
||||
self._on_activate, host),
|
||||
click_activate=True,
|
||||
text=host['display_string'],
|
||||
h_align='left',
|
||||
v_align='center',
|
||||
corner_scale=t_scale,
|
||||
maxwidth=(self._width / t_scale) * 0.93)
|
||||
if host == last_selected_host:
|
||||
ba.containerwidget(edit=self._columnwidget,
|
||||
selected_child=txt3,
|
||||
visible_child=txt3)
|
||||
if i == 0:
|
||||
ba.widget(edit=txt3, up_widget=self._tab_button)
|
||||
|
||||
|
||||
class NearbyGatherTab(GatherTab):
|
||||
"""The nearby tab in the gather UI"""
|
||||
|
||||
def __init__(self, window: GatherWindow) -> None:
|
||||
super().__init__(window)
|
||||
self._net_scanner: Optional[NetScanner] = None
|
||||
self._container: Optional[ba.Widget] = None
|
||||
|
||||
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
|
||||
sub_scroll_height = c_height - 85
|
||||
sub_scroll_width = 650
|
||||
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
|
||||
ba.textwidget(parent=self._container,
|
||||
position=(c_width * 0.5, v - 3),
|
||||
color=(0.6, 1.0, 0.6),
|
||||
scale=1.3,
|
||||
size=(0, 0),
|
||||
maxwidth=c_width * 0.9,
|
||||
h_align='center',
|
||||
v_align='center',
|
||||
text=ba.Lstr(resource='gatherWindow.'
|
||||
'localNetworkDescriptionText'))
|
||||
v -= 15
|
||||
v -= sub_scroll_height + 23
|
||||
scrollw = ba.scrollwidget(parent=self._container,
|
||||
position=((region_width - sub_scroll_width) *
|
||||
0.5, v),
|
||||
size=(sub_scroll_width, sub_scroll_height))
|
||||
|
||||
self._net_scanner = NetScanner(scrollw,
|
||||
tab_button,
|
||||
width=sub_scroll_width)
|
||||
|
||||
ba.widget(edit=scrollw, autoselect=True, up_widget=tab_button)
|
||||
return self._container
|
||||
|
||||
def on_deactivate(self) -> None:
|
||||
self._net_scanner = None
|
||||
1040
assets/src/ba_data/python/bastd/ui/gather/publictab.py
Normal file
1040
assets/src/ba_data/python/bastd/ui/gather/publictab.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,7 @@
|
||||
<w>NOMINMAX</w>
|
||||
<w>aabb</w>
|
||||
<w>abcdefghijklmnopqrstuvwxyz</w>
|
||||
<w>abouttab</w>
|
||||
<w>absval</w>
|
||||
<w>accel</w>
|
||||
<w>accountid</w>
|
||||
@ -360,6 +361,7 @@
|
||||
<w>gettotalrefcount</w>
|
||||
<w>gles</w>
|
||||
<w>glext</w>
|
||||
<w>googleplaytab</w>
|
||||
<w>gpgs</w>
|
||||
<w>gqualstr</w>
|
||||
<w>grav</w>
|
||||
@ -491,6 +493,7 @@
|
||||
<w>magua</w>
|
||||
<w>mainmenu</w>
|
||||
<w>mallocs</w>
|
||||
<w>manualtab</w>
|
||||
<w>maskhigh</w>
|
||||
<w>maskuv</w>
|
||||
<w>maximus</w>
|
||||
@ -535,6 +538,7 @@
|
||||
<w>mystatspage</w>
|
||||
<w>mywidget</w>
|
||||
<w>ndebug</w>
|
||||
<w>nearbytab</w>
|
||||
<w>nearval</w>
|
||||
<w>needwindow</w>
|
||||
<w>negativex</w>
|
||||
@ -654,6 +658,7 @@
|
||||
<w>pton</w>
|
||||
<w>ptrs</w>
|
||||
<w>ptype</w>
|
||||
<w>publictab</w>
|
||||
<w>pulseaudio</w>
|
||||
<w>punchmomentumlinear</w>
|
||||
<w>punchthrough</w>
|
||||
@ -815,6 +820,8 @@
|
||||
<w>symbolification</w>
|
||||
<w>syscalls</w>
|
||||
<w>tabdefs</w>
|
||||
<w>tabtype</w>
|
||||
<w>tabtypes</w>
|
||||
<w>talloc</w>
|
||||
<w>tegra</w>
|
||||
<w>telefonaktiebolaget</w>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
|
||||
<h4><em>last updated on 2020-10-19 for Ballistica version 1.5.27 build 20224</em></h4>
|
||||
<h4><em>last updated on 2020-10-20 for Ballistica version 1.5.27 build 20224</em></h4>
|
||||
<p>This page documents the Python classes and functions in the 'ba' module,
|
||||
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
|
||||
<hr>
|
||||
|
||||
@ -49,7 +49,7 @@ class CallbackSet(Generic[CT]):
|
||||
|
||||
# Define Call() which can be used in type-checking call-wrappers that behave
|
||||
# similarly to functools.partial (in that they take a callable and some
|
||||
# positional arguments to be passed to it)
|
||||
# positional arguments to be passed to it).
|
||||
|
||||
# In type-checking land, We define several different _CallXArg classes
|
||||
# corresponding to different argument counts and define Call() as an
|
||||
@ -61,7 +61,7 @@ class CallbackSet(Generic[CT]):
|
||||
# class _MyCallWrapper:
|
||||
# <runtime class defined here>
|
||||
# if TYPE_CHECKING:
|
||||
# MyCallWrapper = bafoundation.executils.Call
|
||||
# MyCallWrapper = efro.call.Call
|
||||
# else:
|
||||
# MyCallWrapper = _MyCallWrapper
|
||||
|
||||
@ -196,7 +196,9 @@ if TYPE_CHECKING:
|
||||
# 2 arg call; no args bundled.
|
||||
# noinspection PyPep8Naming
|
||||
@overload
|
||||
def Call(call: Callable[[In1T, In2T], OutT]) -> _CallNoArgs[OutT]:
|
||||
def Call(
|
||||
call: Callable[[In1T, In2T],
|
||||
OutT]) -> _Call2Args[In1T, In2T, OutT]:
|
||||
...
|
||||
|
||||
# 3 arg call; 3 args bundled.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user