From e4b833635b39a84fd797825a1bcbd0115fa0f490 Mon Sep 17 00:00:00 2001 From: Ayush Saini Date: Tue, 19 Jan 2021 01:04:26 +0530 Subject: [PATCH] added feature to save party --- .../python/bastd/ui/gather/manualtab.py | 420 +++++++++++++++++- 1 file changed, 412 insertions(+), 8 deletions(-) diff --git a/assets/src/ba_data/python/bastd/ui/gather/manualtab.py b/assets/src/ba_data/python/bastd/ui/gather/manualtab.py index 64c0c2e3..7999e35a 100644 --- a/assets/src/ba_data/python/bastd/ui/gather/manualtab.py +++ b/assets/src/ba_data/python/bastd/ui/gather/manualtab.py @@ -6,14 +6,21 @@ from __future__ import annotations import threading from typing import TYPE_CHECKING, cast - +import copy +import time +import os import _ba import ba -from bastd.ui.gather.bases import GatherTab + +from enum import Enum +from bastd.ui.gather.bases import GatherTab +from dataclasses import dataclass if TYPE_CHECKING: - from typing import Callable, Optional, Any, Union, Dict + from typing import Any, Optional, Dict, List, Tuple,Type from bastd.ui.gather import GatherWindow + from bastd.ui.confirm import ConfirmWindow + def _safe_set_text(txt: Optional[ba.Widget], @@ -45,6 +52,21 @@ class _HostLookupThread(threading.Thread): ba.pushcall(lambda: self._call(result, self._port), from_other_thread=True) +class SubTabType(Enum): + """Available sub-tabs.""" + NEW = 'new' + SAVED = 'saved' + +@dataclass +class State: + """State saved/restored only while the app is running.""" + sub_tab: SubTabType = SubTabType.NEW + parties: Optional[List[Tuple[str, PartyEntry]]] = None + next_entry_index: int = 0 + filter_value: str = '' + have_server_list_response: bool = False + have_valid_server_list: bool = False + class ManualGatherTab(GatherTab): """The manual tab in the gather UI""" @@ -54,6 +76,7 @@ class ManualGatherTab(GatherTab): self._check_button: Optional[ba.Widget] = None self._doing_access_check: Optional[bool] = None self._access_check_count: Optional[int] = None + self._sub_tab: SubTabType = SubTabType.SAVED self._t_addr: Optional[ba.Widget] = None self._t_accessible: Optional[ba.Widget] = None self._t_accessible_extra: Optional[ba.Widget] = None @@ -72,7 +95,7 @@ class ManualGatherTab(GatherTab): ) -> ba.Widget: c_width = region_width - c_height = 380 + c_height = region_height - 20 last_addr = ba.app.config.get('Last Manual Party Connect Address', '') self._container = ba.containerwidget( @@ -83,6 +106,109 @@ class ManualGatherTab(GatherTab): background=False, selection_loops_to_parent=True) v = c_height - 30 + self._join_new_party_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.NEW, + region_width, + region_height, + playsound=True, + ), + text="Join By Address") + self._join_saved_party_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.SAVED, + region_width, + region_height, + playsound=True, + ), + text="Join Saved Party") + ba.widget(edit=self._join_new_party_text, up_widget=tab_button) + ba.widget(edit=self._join_saved_party_text, + left_widget=self._join_new_party_text, + up_widget=tab_button) + ba.widget(edit=self._join_new_party_text, right_widget=self._join_saved_party_text) + self._set_sub_tab(self._sub_tab, region_width, region_height) + + + return self._container + def save_state(self) -> None: + ba.app.ui.window_states[self.__class__.__name__] = State( + sub_tab=self._sub_tab + ) + def restore_state(self) -> None: + state = ba.app.ui.window_states.get(self.__class__.__name__) + if state is None: + state = State() + assert isinstance(state, State) + self._sub_tab = state.sub_tab + def _set_sub_tab(self, + value: SubTabType, + region_width: float, + region_height: float, + playsound: bool = False) -> None: + assert self._container + if playsound: + ba.playsound(ba.getsound('click01')) + + # Reset our selection. + # (prevents selecting something way down the list if we switched away + # and came back) + self._selection = None + self._have_user_selected_row = False + + # Reset refresh to the top and make sure everything refreshes. + self._refresh_ui_row = 0 + + + self._sub_tab = value + active_color = (0.6, 1.0, 0.6) + inactive_color = (0.5, 0.4, 0.5) + ba.textwidget( + edit=self._join_new_party_text, + color=active_color if value is SubTabType.NEW else inactive_color) + ba.textwidget( + edit=self._join_saved_party_text, + color=active_color if value is SubTabType.SAVED else inactive_color) + + # Clear anything existing in the old sub-tab. + for widget in self._container.get_children(): + if widget and widget not in {self._join_saved_party_text, self._join_new_party_text}: + widget.delete() + + if value is SubTabType.NEW: + self._build_new_party_tab(region_width, region_height) + + if value is SubTabType.SAVED: + self._build_saved_party_tab(region_width, region_height) + # The old manual tab + def _build_new_party_tab(self, region_width: float, + region_height: float) -> None: + c_width = region_width + c_height = region_height -20 + last_addr = ba.app.config.get('Last Manual Party Connect Address', '') + v = c_height - 100 ba.textwidget(parent=self._container, position=(c_width * 0.5, v), color=(0.6, 1.0, 0.6), @@ -143,11 +269,18 @@ class ManualGatherTab(GatherTab): size=(300, 70), label=ba.Lstr(resource='gatherWindow.' 'manualConnectText'), - position=(c_width * 0.5 - 150, v), + position=(c_width * 0.5 - 300 , v), autoselect=True, on_activate_call=ba.Call(self._connect, txt, txt2)) - ba.widget(edit=txt, up_widget=tab_button) + btn2 = ba.buttonwidget(parent=self._container, + size=(300, 70), + label="Save", + position=(c_width * 0.5 - 240 + 490 - 200 , v), + autoselect=True, + on_activate_call=ba.Call(self._save_server, 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 @@ -167,7 +300,240 @@ class ManualGatherTab(GatherTab): selectable=True, on_activate_call=ba.Call(self._on_show_my_address_button_press, v, self._container, c_width)) - return self._container + + # Tab containing saved parties + def _build_saved_party_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 + 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) + scroll_buffer_h = 130 + 2 * x_inset + tab_buffer_h = 750 + 2 * x_inset + self._scroll_width = self._width - scroll_buffer_h + self._scroll_height = self._height - 180 + x_inset = 100 if uiscale is ba.UIScale.SMALL else 0 + + if True: + c_width = self._scroll_width + c_height = self._scroll_height - 20 + sub_scroll_height = c_height - 63 + self._my_parties_scroll_width = sub_scroll_width = ( + 680 if uiscale is ba.UIScale.SMALL else 640) + + + + v = c_height - 30 + + 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_saved_party_connect_button = btn1 = ba.buttonwidget( + parent=self._container, + size=(b_width, b_height), + position=(btnh, btnv), + button_type='square', + color=b_color, + textcolor=b_textcolor, + on_activate_call=self._on_my_saved_party_press, + text_scale=tscl, + label="Connect", + 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=self._container, + size=(b_width, b_height), + position=(btnh, btnv), + button_type='square', + color=b_color, + textcolor=b_textcolor, + on_activate_call=self._on_my_saved_party_rename_press, + text_scale=tscl, + label="Rename", + autoselect=True) + btnv -= b_height + b_space_extra + ba.buttonwidget(parent=self._container, + size=(b_width, b_height), + position=(btnh, btnv), + button_type='square', + color=b_color, + textcolor=b_textcolor, + on_activate_call=self._on_my_saved_party_delete_press, + text_scale=tscl, + label="Delete", + autoselect=True) + + v -= sub_scroll_height + 23 + self._scrollwidget = scrlw = ba.scrollwidget( + parent=self._container, + position=(smlh, v), + size=(sub_scroll_width, sub_scroll_height)) + ba.containerwidget(edit=self._container, selected_child=scrlw) + self._columnwidget = ba.columnwidget(parent=scrlw, + left_border=10, + border=2, + margin=0) + + + self._my_saved_party_selected = None + self._refresh_my_saved_parties() + + def _no_saved_party_selected_error(self) -> None: + ba.screenmessage(ba.Lstr(resource="No Server Selected"), + color=(1, 0, 0)) + ba.playsound(ba.getsound('error')) + + def _on_my_saved_party_press(self) -> None: + if self._my_saved_party_selected is None: + self._no_saved_party_selected_error() + return + + config=ba.app.config['Saved Servers'][self._my_saved_party_selected] + _ba.connect_to_party(config['addr'],config['port']) + + def _on_my_saved_party_rename_press(self) -> None: + if self._my_saved_party_selected is None: + self._no_saved_party_selected_error() + return + c_width = 600 + c_height = 250 + uiscale = ba.app.ui.uiscale + self._my_saved_party_rename_window = cnt = ba.containerwidget( + scale=(1.8 if uiscale is ba.UIScale.SMALL else + 1.55 if uiscale is ba.UIScale.MEDIUM else 1.0), + size=(c_width, c_height), + transition='in_scale') + + ba.textwidget(parent=cnt, + size=(0, 0), + h_align='center', + v_align='center', + text="Enter Name of Party", + maxwidth=c_width * 0.8, + position=(c_width * 0.5, c_height - 60)) + self._my_party_rename_text = txt = ba.textwidget( + parent=cnt, + size=(c_width * 0.8, 40), + h_align='left', + v_align='center', + text=ba.app.config['Saved Servers'][self._my_saved_party_selected]['name'], + editable=True, + description="Server name text", + position=(c_width * 0.1, c_height - 140), + autoselect=True, + maxwidth=c_width * 0.7, + max_chars=200) + cbtn = ba.buttonwidget( + parent=cnt, + label=ba.Lstr(resource='cancelText'), + on_activate_call=ba.Call( + lambda c: ba.containerwidget(edit=c, transition='out_scale'), + cnt), + size=(180, 60), + position=(30, 30), + autoselect=True) + okb = ba.buttonwidget(parent=cnt, + label="Rename", + size=(180, 60), + position=(c_width - 230, 30), + on_activate_call=ba.Call( + self._rename_saved_party, + self._my_saved_party_selected), + autoselect=True) + ba.widget(edit=cbtn, right_widget=okb) + ba.widget(edit=okb, left_widget=cbtn) + ba.textwidget(edit=txt, on_return_press_call=okb.activate) + ba.containerwidget(edit=cnt, cancel_button=cbtn, start_button=okb) + + def _rename_saved_party(self, server: str) -> None: + new_name = None + + + if not self._my_party_rename_text: + return + new_name_raw = cast( + str, ba.textwidget(query=self._my_party_rename_text)) + ba.app.config['Saved Servers'][server]['name']=new_name_raw + ba.app.config.commit() + ba.screenmessage("Renamed Successfully",color=(0,0,1)) + self._refresh_my_saved_parties() + + + ba.containerwidget(edit=self._my_saved_party_rename_window, + transition='out_scale') + + def _on_my_saved_party_delete_press(self) -> None: + from bastd.ui import confirm + if self._my_saved_party_selected is None: + self._no_saved_party_selected_error() + return + confirm.ConfirmWindow( + "Confirm Delete ?", + ba.Call(self._delete_saved_party, self._my_saved_party_selected), 450, 150) + + + + def _delete_saved_party(self, server: str) -> None: + config=ba.app.config['Saved Servers'] + del config[server] + self._refresh_my_saved_parties() + def _on_my_saved_party_select(self, server: str) -> None: + self._my_saved_party_selected = server + + def _refresh_my_saved_parties(self) -> None: + assert self._columnwidget is not None + for child in self._columnwidget.get_children(): + child.delete() + t_scale = 1.6 + + + config=ba.app.config + if 'Saved Servers' in config: + servers=config['Saved Servers'] + + else: + servers = [] + + assert self._my_parties_scroll_width is not None + assert self._my_saved_party_connect_button is not None + for server in servers: + txt = ba.textwidget( + parent=self._columnwidget, + size=(self._my_parties_scroll_width / t_scale, 30), + selectable=True, + color=(1.0, 1, 0.4) , + always_highlight=True, + on_select_call=ba.Call(self._on_my_saved_party_select, server), + on_activate_call=self._my_saved_party_connect_button.activate, + text=config['Saved Servers'][server]['name'] if config['Saved Servers'][server]['name']!='' else config['Saved Servers'][server]['addr']+" "+str(config['Saved Servers'][server]['port']), + h_align='left', + v_align='center', + corner_scale=t_scale, + maxwidth=(self._my_parties_scroll_width / t_scale) * 0.93) + + + def on_deactivate(self) -> None: self._access_check_timer = None @@ -195,6 +561,44 @@ class ManualGatherTab(GatherTab): port=port, call=ba.WeakCall(self._host_lookup_result)).start() + def _save_server(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 + try: + import socket + addr = socket.gethostbyname(addr) + except Exception: + addr = None + config=ba.app.config + + if addr is not None: + if 'Saved Servers' in config: + config['Saved Servers'][addr+str(port)]={"addr":addr,"port":port,"name":''} + else: + config['Saved Servers']={} + + config['Saved Servers'][addr+str(port)]={"addr":addr,"port":port,"name":''} + config.commit() + ba.screenmessage("Saved Successfully", + color=(0, 1, 0)) + else: + ba.screenmessage("Invalid Address", + color=(1, 0, 0)) def _host_lookup_result(self, resolved_address: Optional[str], port: int) -> None: if resolved_address is None: @@ -423,4 +827,4 @@ class ManualGatherTab(GatherTab): subs=[('${PORT}', str(_ba.get_game_port()))]), color=color_bad, - ) + ) \ No newline at end of file