From e4b833635b39a84fd797825a1bcbd0115fa0f490 Mon Sep 17 00:00:00 2001 From: Ayush Saini Date: Tue, 19 Jan 2021 01:04:26 +0530 Subject: [PATCH 1/4] 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 From 32e5e3c73d02bef74d81d8b3b0f4453c660ad1b8 Mon Sep 17 00:00:00 2001 From: Ayush Saini Date: Tue, 19 Jan 2021 11:44:01 +0530 Subject: [PATCH 2/4] trying tyding --- .../python/bastd/ui/gather/manualtab.py | 360 +++++++++--------- 1 file changed, 177 insertions(+), 183 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 7999e35a..91ef1393 100644 --- a/assets/src/ba_data/python/bastd/ui/gather/manualtab.py +++ b/assets/src/ba_data/python/bastd/ui/gather/manualtab.py @@ -6,23 +6,19 @@ from __future__ import annotations import threading from typing import TYPE_CHECKING, cast -import copy -import time -import os -import _ba -import ba - from enum import Enum -from bastd.ui.gather.bases import GatherTab from dataclasses import dataclass +from bastd.ui.gather.bases import GatherTab + +import _ba +import ba if TYPE_CHECKING: - from typing import Any, Optional, Dict, List, Tuple,Type + from typing import Any, Optional, Dict, List, Tuple, Type, Union, Callable from bastd.ui.gather import GatherWindow from bastd.ui.confirm import ConfirmWindow - def _safe_set_text(txt: Optional[ba.Widget], val: Union[str, ba.Lstr], success: bool = True) -> None: @@ -52,20 +48,17 @@ 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 + sub_tab: SubTabType = SubTabType.SAVED class ManualGatherTab(GatherTab): @@ -83,6 +76,19 @@ class ManualGatherTab(GatherTab): self._access_check_timer: Optional[ba.Timer] = None self._checking_state_text: Optional[ba.Widget] = None self._container: Optional[ba.Widget] = None + self._join_new_party_text: Optional[ba.Widget] = None + self._join_saved_party_text: Optional[ba.Widget] = None + self._width: Optional[int] = None + self._height: Optional[int] = None + self._scroll_width: Optional[int] = None + self._scroll_height: Optional[int] = None + self._my_parties_scroll_width: Optional[int] = None + self._my_saved_party_connect_button: Optional[ba.Widget] = None + self._scrollwidget: Optional[ba.Widget] = None + self._columnwidget: Optional[ba.Widget] = None + self._my_saved_party_selected: Optional[str] = None + self._my_saved_party_rename_window: Optional[ba.Widget] = None + self._my_party_rename_text: Optional[ba.Widget] = None def on_activate( self, @@ -96,7 +102,6 @@ class ManualGatherTab(GatherTab): c_width = region_width c_height = region_height - 20 - last_addr = ba.app.config.get('Last Manual Party Connect Address', '') self._container = ba.containerwidget( parent=parent_widget, @@ -124,7 +129,7 @@ class ManualGatherTab(GatherTab): region_height, playsound=True, ), - text="Join By Address") + text='Join By Address') self._join_saved_party_text = ba.textwidget( parent=self._container, position=(c_width * 0.5 + 45, v - 13), @@ -143,26 +148,28 @@ class ManualGatherTab(GatherTab): region_height, playsound=True, ), - text="Join Saved Party") + 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) + 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 - ) + 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, @@ -172,41 +179,34 @@ class ManualGatherTab(GatherTab): 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) + 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}: + 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) + self._build_saved_party_tab(region_height) + # The old manual tab def _build_new_party_tab(self, region_width: float, - region_height: float) -> None: + region_height: float) -> None: c_width = region_width - c_height = region_height -20 + 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, @@ -269,17 +269,16 @@ class ManualGatherTab(GatherTab): size=(300, 70), label=ba.Lstr(resource='gatherWindow.' 'manualConnectText'), - position=(c_width * 0.5 - 300 , v), + position=(c_width * 0.5 - 300, v), autoselect=True, on_activate_call=ba.Call(self._connect, txt, txt2)) - 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.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) @@ -300,122 +299,113 @@ class ManualGatherTab(GatherTab): selectable=True, on_activate_call=ba.Call(self._on_show_my_address_button_press, v, self._container, c_width)) - + # Tab containing saved parties - def _build_saved_party_tab(self, region_width: float, - region_height: float) -> None: - c_width = region_width + def _build_saved_party_tab(self, region_height: float) -> None: + c_height = region_height - 20 - v = c_height - 35 - v -= 25 - is_public_enabled = _ba.get_public_party_enabled() - v -= 30 + v = c_height - 35 - 25 - 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_width = self._width - 130 + 2 * x_inset 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) - + 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) + v = c_height - 30 - 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) + 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) - 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) + btnv = (c_height - (48 if uiscale is ba.UIScale.SMALL else + 45 if uiscale is ba.UIScale.MEDIUM else 40) - + b_height) - - self._my_saved_party_selected = None - self._refresh_my_saved_parties() + self._my_saved_party_connect_button = btn1 = ba.buttonwidget( + parent=self._container, + size=(b_width, b_height), + position=(40 if uiscale is ba.UIScale.SMALL else 40, btnv), + button_type='square', + color=(0.6, 0.53, 0.63), + textcolor=(0.75, 0.7, 0.8), + on_activate_call=self._on_my_saved_party_press, + text_scale=1.0 if uiscale is ba.UIScale.SMALL else 1.2, + 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=(40 if uiscale is ba.UIScale.SMALL else 40, + btnv), + button_type='square', + color=(0.6, 0.53, 0.63), + textcolor=(0.75, 0.7, 0.8), + on_activate_call=self._on_my_saved_party_rename_press, + text_scale=1.0 if uiscale is ba.UIScale.SMALL else 1.2, + label='Rename', + autoselect=True) + btnv -= b_height + b_space_extra + ba.buttonwidget(parent=self._container, + size=(b_width, b_height), + position=(40 if uiscale is ba.UIScale.SMALL else 40, + btnv), + button_type='square', + color=(0.6, 0.53, 0.63), + textcolor=(0.75, 0.7, 0.8), + on_activate_call=self._on_my_saved_party_delete_press, + text_scale=1.0 if uiscale is ba.UIScale.SMALL else 1.2, + label='Delete', + autoselect=True) + + v -= sub_scroll_height + 23 + self._scrollwidget = scrlw = ba.scrollwidget( + parent=self._container, + position=(190 if uiscale is ba.UIScale.SMALL else 225, 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"), + 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']) + else: + 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 @@ -424,12 +414,12 @@ class ManualGatherTab(GatherTab): 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", + 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( @@ -437,9 +427,10 @@ class ManualGatherTab(GatherTab): size=(c_width * 0.8, 40), h_align='left', v_align='center', - text=ba.app.config['Saved Servers'][self._my_saved_party_selected]['name'], + text=ba.app.config['Saved Servers'][ + self._my_saved_party_selected]['name'], editable=True, - description="Server name text", + description='Server name text', position=(c_width * 0.1, c_height - 140), autoselect=True, maxwidth=c_width * 0.7, @@ -454,7 +445,7 @@ class ManualGatherTab(GatherTab): position=(30, 30), autoselect=True) okb = ba.buttonwidget(parent=cnt, - label="Rename", + label='Rename', size=(180, 60), position=(c_width - 230, 30), on_activate_call=ba.Call( @@ -467,18 +458,15 @@ class ManualGatherTab(GatherTab): 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 + 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)) + 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') @@ -489,15 +477,15 @@ class ManualGatherTab(GatherTab): self._no_saved_party_selected_error() return confirm.ConfirmWindow( - "Confirm Delete ?", - ba.Call(self._delete_saved_party, self._my_saved_party_selected), 450, 150) - - + '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'] + 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 @@ -506,34 +494,33 @@ class ManualGatherTab(GatherTab): for child in self._columnwidget.get_children(): child.delete() t_scale = 1.6 - - - config=ba.app.config + + config = ba.app.config if 'Saved Servers' in config: - servers=config['Saved Servers'] - - else: - servers = [] + 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( + ba.textwidget( parent=self._columnwidget, size=(self._my_parties_scroll_width / t_scale, 30), selectable=True, - color=(1.0, 1, 0.4) , + 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']), + 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 @@ -562,7 +549,7 @@ class ManualGatherTab(GatherTab): call=ba.WeakCall(self._host_lookup_result)).start() def _save_server(self, textwidget: ba.Widget, - port_textwidget: ba.Widget) -> None: + port_textwidget: ba.Widget) -> None: addr = cast(str, ba.textwidget(query=textwidget)) if addr == '': ba.screenmessage( @@ -583,22 +570,29 @@ class ManualGatherTab(GatherTab): import socket addr = socket.gethostbyname(addr) except Exception: - addr = None - config=ba.app.config + addr = '' + 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']={} + if addr != '': + 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)) + 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)) + 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: @@ -827,4 +821,4 @@ class ManualGatherTab(GatherTab): subs=[('${PORT}', str(_ba.get_game_port()))]), color=color_bad, - ) \ No newline at end of file + ) From 0e9b3036a7170a67e87f904d2d3a88209a499396 Mon Sep 17 00:00:00 2001 From: Eric Froemling Date: Tue, 19 Jan 2021 00:54:36 -0800 Subject: [PATCH 3/4] Cleanup for Saved Manual Address tab --- .idea/dictionaries/ericf.xml | 1 + .../python/bastd/ui/gather/manualtab.py | 131 ++++++++++-------- .../.idea/dictionaries/ericf.xml | 1 + 3 files changed, 73 insertions(+), 60 deletions(-) diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml index ff67abad..7ed9fbec 100644 --- a/.idea/dictionaries/ericf.xml +++ b/.idea/dictionaries/ericf.xml @@ -1801,6 +1801,7 @@ samsung sandboxing sandyrb + savebutton saxutils sbblk sbblu 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 91ef1393..169f997e 100644 --- a/assets/src/ba_data/python/bastd/ui/gather/manualtab.py +++ b/assets/src/ba_data/python/bastd/ui/gather/manualtab.py @@ -58,7 +58,7 @@ class SubTabType(Enum): @dataclass class State: """State saved/restored only while the app is running.""" - sub_tab: SubTabType = SubTabType.SAVED + sub_tab: SubTabType = SubTabType.NEW class ManualGatherTab(GatherTab): @@ -69,7 +69,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._sub_tab: SubTabType = SubTabType.NEW self._t_addr: Optional[ba.Widget] = None self._t_accessible: Optional[ba.Widget] = None self._t_accessible_extra: Optional[ba.Widget] = None @@ -153,6 +153,7 @@ class ManualGatherTab(GatherTab): ba.widget(edit=self._join_saved_party_text, left_widget=self._join_new_party_text, up_widget=tab_button) + ba.widget(edit=tab_button, down_widget=self._join_saved_party_text) 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) @@ -208,18 +209,7 @@ class ManualGatherTab(GatherTab): 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), - 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 = c_height - 70 v -= 70 ba.textwidget(parent=self._container, position=(c_width * 0.5 - 260 - 50, v), @@ -241,6 +231,8 @@ class ManualGatherTab(GatherTab): v_align='center', scale=1.0, size=(420, 60)) + ba.widget(edit=self._join_new_party_text, down_widget=txt) + ba.widget(edit=self._join_saved_party_text, down_widget=txt) ba.textwidget(parent=self._container, position=(c_width * 0.5 - 260 + 490, v), color=(0.6, 1.0, 0.6), @@ -273,13 +265,15 @@ class ManualGatherTab(GatherTab): autoselect=True, on_activate_call=ba.Call(self._connect, txt, txt2)) - 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) + savebutton = 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=btn, right_widget=savebutton) + ba.widget(edit=savebutton, left_widget=btn, up_widget=txt2) ba.textwidget(edit=txt, on_return_press_call=btn.activate) ba.textwidget(edit=txt2, on_return_press_call=btn.activate) v -= 45 @@ -299,6 +293,7 @@ class ManualGatherTab(GatherTab): selectable=True, on_activate_call=ba.Call(self._on_show_my_address_button_press, v, self._container, c_width)) + ba.widget(edit=self._check_button, up_widget=btn) # Tab containing saved parties def _build_saved_party_tab(self, region_height: float) -> None: @@ -344,7 +339,6 @@ class ManualGatherTab(GatherTab): text_scale=1.0 if uiscale is ba.UIScale.SMALL else 1.2, 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')) @@ -377,18 +371,22 @@ class ManualGatherTab(GatherTab): self._scrollwidget = scrlw = ba.scrollwidget( parent=self._container, position=(190 if uiscale is ba.UIScale.SMALL else 225, v), - size=(sub_scroll_width, sub_scroll_height)) + size=(sub_scroll_width, sub_scroll_height), + claims_left_right=True) + ba.widget(edit=self._my_saved_party_connect_button, + right_widget=self._scrollwidget) ba.containerwidget(edit=self._container, selected_child=scrlw) self._columnwidget = ba.columnwidget(parent=scrlw, left_border=10, border=2, - margin=0) + margin=0, + claims_left_right=True) 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'), + ba.screenmessage(ba.Lstr(resource='nothingIsSelectedErrorText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) @@ -399,7 +397,10 @@ class ManualGatherTab(GatherTab): else: config = ba.app.config['Saved Servers'][ self._my_saved_party_selected] - _ba.connect_to_party(config['addr'], config['port']) + _HostLookupThread(name=config['addr'], + port=config['port'], + call=ba.WeakCall( + self._host_lookup_result)).start() def _on_my_saved_party_rename_press(self) -> None: if self._my_saved_party_selected is None: @@ -449,23 +450,27 @@ class ManualGatherTab(GatherTab): size=(180, 60), position=(c_width - 230, 30), on_activate_call=ba.Call( - self._rename_saved_party, - self._my_saved_party_selected), + self._rename_saved_party), 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: + def _rename_saved_party(self) -> None: + server = self._my_saved_party_selected + if self._my_saved_party_selected is None: + self._no_saved_party_selected_error() + return 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)) + ba.screenmessage('Renamed Successfully', color=(0, 1, 0)) + ba.playsound(ba.getsound('gunCocking')) self._refresh_my_saved_parties() ba.containerwidget(edit=self._my_saved_party_rename_window, @@ -477,13 +482,20 @@ class ManualGatherTab(GatherTab): self._no_saved_party_selected_error() return confirm.ConfirmWindow( - 'Confirm Delete ?', - ba.Call(self._delete_saved_party, self._my_saved_party_selected), - 450, 150) + ba.Lstr(resource='gameListWindow.deleteConfirmText', + subs=[('${LIST}', ba.app.config['Saved Servers'][ + self._my_saved_party_selected]['name'])]), + self._delete_saved_party, 450, 150) - def _delete_saved_party(self, server: str) -> None: + def _delete_saved_party(self) -> None: + if self._my_saved_party_selected is None: + self._no_saved_party_selected_error() + return config = ba.app.config['Saved Servers'] - del config[server] + del config[self._my_saved_party_selected] + self._my_saved_party_selected = None + ba.app.config.commit() + ba.playsound(ba.getsound('shieldDown')) self._refresh_my_saved_parties() def _on_my_saved_party_select(self, server: str) -> None: @@ -504,8 +516,8 @@ class ManualGatherTab(GatherTab): assert self._my_parties_scroll_width is not None assert self._my_saved_party_connect_button is not None - for server in servers: - ba.textwidget( + for i, server in enumerate(servers): + txt = ba.textwidget( parent=self._columnwidget, size=(self._my_parties_scroll_width / t_scale, 30), selectable=True, @@ -513,14 +525,23 @@ class ManualGatherTab(GatherTab): 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']), + 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) + if i == 0: + ba.widget(edit=txt, up_widget=self._join_saved_party_text) + ba.widget(edit=txt, + left_widget=self._my_saved_party_connect_button, + right_widget=txt) + + # If there's no servers, allow selecting out of the scroll area + ba.containerwidget(edit=self._scrollwidget, + claims_left_right=bool(servers)) def on_deactivate(self) -> None: self._access_check_timer = None @@ -566,32 +587,22 @@ class ManualGatherTab(GatherTab): color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return - try: - import socket - addr = socket.gethostbyname(addr) - except Exception: - addr = '' config = ba.app.config - if addr != '': - if 'Saved Servers' in config: - config['Saved Servers'][addr + str(port)] = { - 'addr': addr, - 'port': port, - 'name': '' - } - else: + if addr: + if not isinstance(config.get('Saved Servers'), dict): config['Saved Servers'] = {} - - config['Saved Servers'][addr + str(port)] = { - 'addr': addr, - 'port': port, - 'name': '' - } + config['Saved Servers'][f'{addr}@{port}'] = { + 'addr': addr, + 'port': port, + 'name': addr + } config.commit() ba.screenmessage('Saved Successfully', color=(0, 1, 0)) + ba.playsound(ba.getsound('gunCocking')) else: ba.screenmessage('Invalid Address', color=(1, 0, 0)) + ba.playsound(ba.getsound('error')) def _host_lookup_result(self, resolved_address: Optional[str], port: int) -> None: diff --git a/ballisticacore-cmake/.idea/dictionaries/ericf.xml b/ballisticacore-cmake/.idea/dictionaries/ericf.xml index afc6fd4b..2a22a0bf 100644 --- a/ballisticacore-cmake/.idea/dictionaries/ericf.xml +++ b/ballisticacore-cmake/.idea/dictionaries/ericf.xml @@ -758,6 +758,7 @@ safecolor samsung sapspace + savebutton scancode scenetime screenmessage From a49d4599c0b0e751c9857743a4ef9e1668e332ac Mon Sep 17 00:00:00 2001 From: Ayush Saini Date: Tue, 19 Jan 2021 15:00:43 +0530 Subject: [PATCH 4/4] added myself --- CHANGELOG.md | 1 + CONTRIBUTORS.md | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76c34f5c..a58e42bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ### 1.5.30 (20263) - The meta subsystem now enables new plugins by default in headless builds. +- Added option to save party in Manual tab ### 1.5.29 (20246) - Exposed ba method/class initing in public C++ layer. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 226a1795..5ab7328e 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -17,3 +17,6 @@ ### Ali Borhani - Bug fixes + +### Mr.Smoothy +- Modder \ No newline at end of file