CommonClient: Some GUI polish (#1139)

* CommonClient: Focus text field when requesting input.

* CommonClient: Store and prefill last server address.

* CommonClient: Focus and select portion of server address upon start.

* CommonClient: Don't allow editing of address while connected.

* CommonClient: Don't make pressing Enter in the address bar disconnect you.

* CommonClient: Use TextInput.text_validate_unfocus over jank workaround.

* CommonClient: Fixed hang when closing after failed handshake.

* CommonClient: Made scrollbar wider and interactable.
This commit is contained in:
recklesscoder 2022-11-01 06:54:40 +01:00 committed by GitHub
parent 655f287d42
commit c1d7abd06e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 12 deletions

View File

@ -218,6 +218,12 @@ class CommonContext:
# execution # execution
self.keep_alive_task = asyncio.create_task(keep_alive(self), name="Bouncy") self.keep_alive_task = asyncio.create_task(keep_alive(self), name="Bouncy")
@property
def suggested_address(self) -> str:
if self.server_address:
return self.server_address
return Utils.persistent_load().get("client", {}).get("last_server_address", "")
@functools.cached_property @functools.cached_property
def raw_text_parser(self) -> RawJSONtoTextParser: def raw_text_parser(self) -> RawJSONtoTextParser:
return RawJSONtoTextParser(self) return RawJSONtoTextParser(self)
@ -229,9 +235,9 @@ class CommonContext:
return len(self.checked_locations | self.missing_locations) return len(self.checked_locations | self.missing_locations)
async def connection_closed(self): async def connection_closed(self):
self.reset_server_state()
if self.server and self.server.socket is not None: if self.server and self.server.socket is not None:
await self.server.socket.close() await self.server.socket.close()
self.reset_server_state()
def reset_server_state(self): def reset_server_state(self):
self.auth = None self.auth = None
@ -297,6 +303,8 @@ class CommonContext:
await self.send_msgs([payload]) await self.send_msgs([payload])
async def console_input(self) -> str: async def console_input(self) -> str:
if self.ui:
self.ui.focus_textinput()
self.input_requests += 1 self.input_requests += 1
return await self.input_queue.get() return await self.input_queue.get()
@ -665,6 +673,9 @@ async def process_server_cmd(ctx: CommonContext, args: dict):
ctx.checked_locations = set(args["checked_locations"]) ctx.checked_locations = set(args["checked_locations"])
ctx.server_locations = ctx.missing_locations | ctx. checked_locations ctx.server_locations = ctx.missing_locations | ctx. checked_locations
server_url = urllib.parse.urlparse(ctx.server_address)
Utils.persistent_store("client", "last_server_address", server_url.netloc)
elif cmd == 'ReceivedItems': elif cmd == 'ReceivedItems':
start_index = args["index"] start_index = args["index"]
@ -775,7 +786,6 @@ if __name__ == '__main__':
async def main(args): async def main(args):
ctx = TextContext(args.connect, args.password) ctx = TextContext(args.connect, args.password)
ctx.auth = args.name ctx.auth = args.name
ctx.server_address = args.connect
ctx.server_task = asyncio.create_task(server_loop(ctx), name="server loop") ctx.server_task = asyncio.create_task(server_loop(ctx), name="server loop")
if gui_enabled: if gui_enabled:

View File

@ -15,6 +15,8 @@
<UILog>: <UILog>:
viewclass: 'SelectableLabel' viewclass: 'SelectableLabel'
scroll_y: 0 scroll_y: 0
scroll_type: ["content", "bars"]
bar_width: dp(12)
effect_cls: "ScrollEffect" effect_cls: "ScrollEffect"
SelectableRecycleBoxLayout: SelectableRecycleBoxLayout:
default_size: None, dp(20) default_size: None, dp(20)

29
kvui.py
View File

@ -338,9 +338,12 @@ class GameManager(App):
# top part # top part
server_label = ServerLabel() server_label = ServerLabel()
self.connect_layout.add_widget(server_label) self.connect_layout.add_widget(server_label)
self.server_connect_bar = ConnectBarTextInput(text=self.ctx.server_address or "archipelago.gg", size_hint_y=None, self.server_connect_bar = ConnectBarTextInput(text=self.ctx.suggested_address or "archipelago.gg:", size_hint_y=None,
height=30, multiline=False, write_tab=False) height=30, multiline=False, write_tab=False)
self.server_connect_bar.bind(on_text_validate=self.connect_button_action) def connect_bar_validate(sender):
if not self.ctx.server:
self.connect_button_action(sender)
self.server_connect_bar.bind(on_text_validate=connect_bar_validate)
self.connect_layout.add_widget(self.server_connect_bar) self.connect_layout.add_widget(self.server_connect_bar)
self.server_connect_button = Button(text="Connect", size=(100, 30), size_hint_y=None, size_hint_x=None) self.server_connect_button = Button(text="Connect", size=(100, 30), size_hint_y=None, size_hint_x=None)
self.server_connect_button.bind(on_press=self.connect_button_action) self.server_connect_button.bind(on_press=self.connect_button_action)
@ -381,17 +384,19 @@ class GameManager(App):
bottom_layout.add_widget(info_button) bottom_layout.add_widget(info_button)
self.textinput = TextInput(size_hint_y=None, height=30, multiline=False, write_tab=False) self.textinput = TextInput(size_hint_y=None, height=30, multiline=False, write_tab=False)
self.textinput.bind(on_text_validate=self.on_message) self.textinput.bind(on_text_validate=self.on_message)
self.textinput.text_validate_unfocus = False
def text_focus(event):
"""Needs to be set via delay, as unfocusing happens after on_message"""
self.textinput.focus = True
self.textinput.text_focus = text_focus
bottom_layout.add_widget(self.textinput) bottom_layout.add_widget(self.textinput)
self.grid.add_widget(bottom_layout) self.grid.add_widget(bottom_layout)
self.commandprocessor("/help") self.commandprocessor("/help")
Clock.schedule_interval(self.update_texts, 1 / 30) Clock.schedule_interval(self.update_texts, 1 / 30)
self.container.add_widget(self.grid) self.container.add_widget(self.grid)
self.server_connect_bar.focus = True
self.server_connect_bar.select_text(
self.server_connect_bar.text.find(":") + 1,
len(self.server_connect_bar.text)
)
return self.container return self.container
def update_texts(self, dt): def update_texts(self, dt):
@ -402,10 +407,12 @@ class GameManager(App):
f" | Connected to: {self.ctx.server_address} " \ f" | Connected to: {self.ctx.server_address} " \
f"{'.'.join(str(e) for e in self.ctx.server_version)}" f"{'.'.join(str(e) for e in self.ctx.server_version)}"
self.server_connect_button.text = "Disconnect" self.server_connect_button.text = "Disconnect"
self.server_connect_bar.readonly = True
self.progressbar.max = len(self.ctx.checked_locations) + len(self.ctx.missing_locations) self.progressbar.max = len(self.ctx.checked_locations) + len(self.ctx.missing_locations)
self.progressbar.value = len(self.ctx.checked_locations) self.progressbar.value = len(self.ctx.checked_locations)
else: else:
self.server_connect_button.text = "Connect" self.server_connect_button.text = "Connect"
self.server_connect_bar.readonly = False
self.title = self.base_title + " " + Utils.__version__ self.title = self.base_title + " " + Utils.__version__
self.progressbar.value = 0 self.progressbar.value = 0
@ -443,8 +450,6 @@ class GameManager(App):
elif input_text: elif input_text:
self.commandprocessor(input_text) self.commandprocessor(input_text)
Clock.schedule_once(textinput.text_focus)
except Exception as e: except Exception as e:
logging.getLogger("Client").exception(e) logging.getLogger("Client").exception(e)
@ -453,6 +458,10 @@ class GameManager(App):
self.log_panels["Archipelago"].on_message_markup(text) self.log_panels["Archipelago"].on_message_markup(text)
self.log_panels["All"].on_message_markup(text) self.log_panels["All"].on_message_markup(text)
def focus_textinput(self):
if hasattr(self, "textinput"):
self.textinput.focus = True
def update_address_bar(self, text: str): def update_address_bar(self, text: str):
if hasattr(self, "server_connect_bar"): if hasattr(self, "server_connect_bar"):
self.server_connect_bar.text = text self.server_connect_bar.text = text