Merge views into Match, expand history, and polish the UI

- Match page now holds players/match info, turn stats with a turn
  picker, and the deck lists (My deck · Preview · Opponent); the Deck
  and Turn tabs are gone. Section heights are draggable and remembered.
- History: sortable, resizable columns; deck and commander columns;
  right-click to show only or hide games with a value, with filter
  chips; stats tiles (win rate overall, last 10/100, last 7 days,
  play/draw, average length and turns) that follow the filters.
  Commanders are backfilled for games recorded before they were stored.
- Deck panel shows copies per zone against the deck total (e.g. 2/5).
- Preview can be cleared with ✕, a click on the image, or Esc.
- Customize menu stays open while toggling; Arrange covers deck
  sections and turn stats together.
- Fix squeezed nav buttons and the header pushing controls off-screen.
- Fix known opponent cards being double-counted when also revealed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aeth3randClaude Opus 5.5 committed 2026-10-16 12:34:02 -04:00
1 parent 5b3c8c8e63
commit b3325e8ecc
15 files changed
+760 -183

No files matched your search

+15 -16
View File
@@ -189,7 +189,7 @@ class PlayerCard(QFrame):
class MatchView(QWidget):
hovered = Signal(int)
def __init__(self, cards: CardDB, toggles: Toggles, parent=None):
def __init__(self, cards: CardDB, toggles: Toggles, turn_view: QWidget, deck_view: QWidget, parent=None):
super().__init__(parent)
self.cards = cards
self.toggles = toggles
@@ -200,7 +200,7 @@ class MatchView(QWidget):
self.info = QFrame()
self.info.setObjectName("card")
self.turn_label = _label(name="bigNumber")
self.turn_label = _label(name="statValue")
self.turn_caption = _label(name="muted")
self.phase = _label()
self.score = _label()
@@ -209,7 +209,7 @@ class MatchView(QWidget):
self.duration = _label(name="muted")
grid = QGridLayout(self.info)
grid.setContentsMargins(14, 10, 14, 12)
for row, w in enumerate([_label("Turn", "cardTitle"), self.turn_label, self.turn_caption,
for row, w in enumerate([_label("Match", "cardTitle"), self.turn_label, self.turn_caption,
self.phase, self.score, self.draws, self.duration]):
grid.addWidget(w, row, 0, Qt.AlignmentFlag.AlignHCenter)
grid.setRowStretch(7, 1)
@@ -221,19 +221,18 @@ class MatchView(QWidget):
top_lay.addWidget(self.info, 2)
top_lay.addWidget(self.opp, 3)
# The shared DeckView is re-parented into this slot when the match view is shown.
self.deck_slot = QWidget()
self.deck_slot_layout = QVBoxLayout(self.deck_slot)
self.deck_slot_layout.setContentsMargins(0, 0, 0, 0)
split = QSplitter(Qt.Orientation.Vertical)
split.addWidget(top)
split.addWidget(self.deck_slot)
split.setChildrenCollapsible(False)
split.setSizes([1, 1])
# Players + match info, then turn stats, then the deck lists; heights are draggable.
self.split = QSplitter(Qt.Orientation.Vertical)
self.split.addWidget(top)
self.split.addWidget(turn_view)
self.split.addWidget(deck_view)
self.split.setChildrenCollapsible(False)
self.split.setStretchFactor(0, 2)
self.split.setStretchFactor(1, 3)
self.split.setStretchFactor(2, 5)
lay = QVBoxLayout(self)
lay.setContentsMargins(0, 0, 0, 0)
lay.addWidget(split)
lay.addWidget(self.split)
def render(self, t: MatchTracker, now_ms: int) -> None:
g = t.game
@@ -246,8 +245,8 @@ class MatchView(QWidget):
on = self.toggles.on
whose = "your turn" if g.active_player == g.my_seat else "their turn"
self.turn_label.setText(str(g.turn or "–"))
self.turn_caption.setText(whose if g.turn else "pre-game")
self.turn_label.setText(f"Turn {g.turn}" if g.turn else "Pre-game")
self.turn_caption.setText(whose if g.turn else "")
phase = pretty_enum(g.phase) if g.phase else ""
step = pretty_enum(g.step) if g.step else ""
self.phase.setText(" · ".join(x for x in (phase, step) if x))