TUI: add double-escape quit and minor layout tweaks
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
// Centralized timing configuration for all animations and pauses
|
// Centralized timing configuration for all animations and pauses
|
||||||
// Edit these values to tune the feel of card animations and CPU gameplay
|
// Edit these values to tune the feel of card animations and CPU gameplay
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
from textual.app import ComposeResult
|
from textual.app import ComposeResult
|
||||||
from textual.containers import Container, Horizontal, Vertical
|
from textual.containers import Container, Horizontal, Vertical
|
||||||
from textual.screen import Screen
|
from textual.screen import Screen
|
||||||
@@ -23,6 +25,7 @@ class ConnectScreen(Screen):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._mode: str = "login" # "login" or "signup"
|
self._mode: str = "login" # "login" or "signup"
|
||||||
|
self._last_esc: float = 0.0
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
with Container(id="connect-container"):
|
with Container(id="connect-container"):
|
||||||
@@ -30,7 +33,7 @@ class ConnectScreen(Screen):
|
|||||||
|
|
||||||
# Login form
|
# Login form
|
||||||
with Vertical(id="login-form"):
|
with Vertical(id="login-form"):
|
||||||
yield Static("Log in to play")
|
yield Static("Log in to play\n")
|
||||||
yield Input(placeholder="Username", id="input-username")
|
yield Input(placeholder="Username", id="input-username")
|
||||||
yield Input(placeholder="Password", password=True, id="input-password")
|
yield Input(placeholder="Password", password=True, id="input-password")
|
||||||
with Horizontal(id="connect-buttons"):
|
with Horizontal(id="connect-buttons"):
|
||||||
@@ -64,7 +67,7 @@ class ConnectScreen(Screen):
|
|||||||
|
|
||||||
with Horizontal(classes="screen-footer"):
|
with Horizontal(classes="screen-footer"):
|
||||||
yield Static("", id="connect-footer-left", classes="screen-footer-left")
|
yield Static("", id="connect-footer-left", classes="screen-footer-left")
|
||||||
yield Static("\\[q] quit", id="connect-footer-right", classes="screen-footer-right")
|
yield Static("\\[q]uit or \\[esc]x2", id="connect-footer-right", classes="screen-footer-right")
|
||||||
|
|
||||||
def on_mount(self) -> None:
|
def on_mount(self) -> None:
|
||||||
self._update_form_visibility()
|
self._update_form_visibility()
|
||||||
@@ -103,11 +106,17 @@ class ConnectScreen(Screen):
|
|||||||
self._update_form_visibility()
|
self._update_form_visibility()
|
||||||
|
|
||||||
def handle_escape(self) -> None:
|
def handle_escape(self) -> None:
|
||||||
"""Escape goes back to login if on signup form."""
|
"""Escape goes back to login if on signup form. Double-escape quits."""
|
||||||
if self._mode == "signup":
|
if self._mode == "signup":
|
||||||
self._mode = "login"
|
self._mode = "login"
|
||||||
self._set_status("")
|
self._set_status("")
|
||||||
self._update_form_visibility()
|
self._update_form_visibility()
|
||||||
|
self._last_esc = 0.0
|
||||||
|
else:
|
||||||
|
now = time.monotonic()
|
||||||
|
if now - self._last_esc < 0.5:
|
||||||
|
self.app.exit()
|
||||||
|
self._last_esc = now
|
||||||
|
|
||||||
def on_input_submitted(self, event: Input.Submitted) -> None:
|
def on_input_submitted(self, event: Input.Submitted) -> None:
|
||||||
if event.input.id == "input-password":
|
if event.input.id == "input-password":
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class LobbyScreen(Screen):
|
|||||||
# In-room: player list + controls + settings
|
# In-room: player list + controls + settings
|
||||||
with Vertical(id="in-room"):
|
with Vertical(id="in-room"):
|
||||||
yield Static("", id="room-info")
|
yield Static("", id="room-info")
|
||||||
yield Static("[bold]Players[/bold]", id="player-list-label")
|
yield Static("[bold]Players[/bold]\n", id="player-list-label")
|
||||||
yield Static("", id="player-list")
|
yield Static("", id="player-list")
|
||||||
|
|
||||||
# CPU controls: compact [+] [-]
|
# CPU controls: compact [+] [-]
|
||||||
@@ -185,8 +185,8 @@ class LobbyScreen(Screen):
|
|||||||
yield Label("Wolfpack")
|
yield Label("Wolfpack")
|
||||||
yield Switch(id="sw-wolfpack")
|
yield Switch(id="sw-wolfpack")
|
||||||
|
|
||||||
with Collapsible(title="Deck Style", collapsed=True, id="coll-deck"):
|
|
||||||
with Horizontal(classes="setting-row"):
|
with Horizontal(classes="setting-row"):
|
||||||
|
yield Label("Deck Style")
|
||||||
yield Select(
|
yield Select(
|
||||||
[(name.replace("-", " ").title(), name) for name in DECK_PRESETS],
|
[(name.replace("-", " ").title(), name) for name in DECK_PRESETS],
|
||||||
value="classic",
|
value="classic",
|
||||||
@@ -398,15 +398,7 @@ class LobbyScreen(Screen):
|
|||||||
)
|
)
|
||||||
line3 = "".join(parts3)
|
line3 = "".join(parts3)
|
||||||
|
|
||||||
parts4: list[str] = []
|
return f"{line1}\n{line2}\n{line3}"
|
||||||
for color_name in seen:
|
|
||||||
hc = BACK_COLORS.get(color_name, BACK_COLORS["red"])
|
|
||||||
parts4.append(
|
|
||||||
f"[{bc}]└───┘[/{bc}] "
|
|
||||||
)
|
|
||||||
line4 = "".join(parts4)
|
|
||||||
|
|
||||||
return f"{line1}\n{line2}\n{line3}\n{line4}"
|
|
||||||
|
|
||||||
def _add_random_cpu(self) -> None:
|
def _add_random_cpu(self) -> None:
|
||||||
"""Add a random CPU (server picks the profile)."""
|
"""Add a random CPU (server picks the profile)."""
|
||||||
|
|||||||
@@ -205,9 +205,8 @@ LobbyScreen {
|
|||||||
|
|
||||||
#deck-preview {
|
#deck-preview {
|
||||||
width: auto;
|
width: auto;
|
||||||
height: auto;
|
height: 3;
|
||||||
padding: 1 1 0 1;
|
padding: 0 1;
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.rule-row {
|
.rule-row {
|
||||||
|
|||||||
Reference in New Issue
Block a user