Add section toggles, simplify Customize, scope header to Match

- Customize menu: a "Sections" group to show/hide each Match panel
  (You, Match, Opponent, Turn stats, My deck, Preview, Opponent's
  cards); rows collapse when all their panels are hidden. Player panel
  details follow, then Arrange. The submenus duplicating Arrange are gone.
- The top bar's match summary only shows on the Match tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aeth3randClaude Opus 5.5 committed 2026-10-16 13:10:35 -04:00
1 parent e82f8d1858
commit 5f3c7c13d0
4 files changed
+69 -23

No files matched your search

+4 -3
View File
@@ -25,9 +25,10 @@ Two views, switched from the top bar (Ctrl+1 / Ctrl+2):
filters. Click a game to open it in the Match view ("Back to live" returns). Games from the
previous MTGA session (`Player-prev.log`) are imported on startup.
*⚙ Customize* shows or hides the match panels, turn stats, deck sections and the preview.
*Customize → Arrange…* reorders (drag, or ↑/↓) and shows/hides the My deck and Opponent sections
and the turn stats. Your choices are remembered.
*⚙ Customize* shows or hides each panel of the Match page (You, Match, Opponent, Turn stats,
My deck, Preview, Opponent's cards) and the details in the player panels. *Customize → Arrange…*
reorders (drag, or ↑/↓) and shows/hides the My deck and Opponent sections and the turn stats.
Your choices are remembered.
Card names, mana costs and types come from MTGA's own local card database, so they work offline.
+11 -3
View File
@@ -74,9 +74,11 @@ class DeckView(QWidget):
self.preview.cleared.connect(self._clear_selection)
split = QSplitter(Qt.Orientation.Horizontal)
split.addWidget(card_frame("My deck", self.mine))
split.addWidget(card_frame("Preview", self.preview))
split.addWidget(card_frame("Opponent", self.opp))
self.my_frame = card_frame("My deck", self.mine)
self.preview_frame = card_frame("Preview", self.preview)
self.opp_frame = card_frame("Opponent", self.opp)
for frame in (self.my_frame, self.preview_frame, self.opp_frame):
split.addWidget(frame)
split.setStretchFactor(0, 3)
split.setStretchFactor(1, 2)
split.setStretchFactor(2, 3)
@@ -86,6 +88,12 @@ class DeckView(QWidget):
lay.setContentsMargins(0, 0, 0, 0)
lay.addWidget(split)
def show_panels(self, my_deck: bool, preview: bool, opp_deck: bool) -> None:
self.my_frame.setVisible(my_deck)
self.preview_frame.setVisible(preview)
self.opp_frame.setVisible(opp_deck)
self.setVisible(my_deck or preview or opp_deck)
def _clear_selection(self) -> None:
for lst in (self.mine, self.opp):
lst.setCurrentItem(None)
+35 -14
View File
@@ -39,7 +39,16 @@ from .toggles import StayOpenMenu, Toggles
from .turn_view import TURN_DEFAULTS_OFF, TURN_TOGGLES, TurnView
REFRESH_MS = 150
DECK_TOGGLES = [("preview", "Card preview pane")]
# Panels of the Match page that can be hidden (rows disappear when all their panels are hidden).
SECTION_TOGGLES = [
("you", "You"),
("match", "Match"),
("opponent", "Opponent"),
("turns", "Turn stats"),
("my_deck", "My deck"),
("preview", "Preview"),
("opp_deck", "Opponent's cards"),
]
VIEWS = ["Match", "History"]
MATCH, HISTORY = 0, 1
@@ -91,7 +100,7 @@ class MainWindow(QMainWindow):
self._settings = QSettings("mtga-assistant", "mtga-assistant")
self.toggles = [
Toggles("deck", DECK_TOGGLES, parent=self),
Toggles("sections", SECTION_TOGGLES, parent=self),
Toggles("match", MATCH_TOGGLES, parent=self),
Toggles("turn", TURN_TOGGLES, TURN_DEFAULTS_OFF, parent=self),
]
@@ -244,14 +253,22 @@ class MainWindow(QMainWindow):
def _fill_customize(self) -> None:
menu = self.customize_menu
menu.clear()
self.toggles[1].fill_menu(menu) # match panels
for title, tg in (("Turn stats", self.toggles[2]), ("My deck sections", self.my_sections),
("Opponent sections", self.opp_sections), ("Preview", self.toggles[0])):
sub = StayOpenMenu(title, menu)
tg.fill_menu(sub)
menu.addMenu(sub)
menu.addSeparator()
menu.addAction("Arrange sections and stats…").triggered.connect(self._open_arrange)
for heading, tg in (("Sections", self.toggles[0]), ("Player panel details", self.toggles[1])):
title = menu.addAction(heading)
title.setEnabled(False) # a heading, not an option
font = title.font()
font.setBold(True)
title.setFont(font)
tg.fill_menu(menu)
menu.addSeparator()
# Deck sections and turn stats (order and visibility) live in the Arrange dialog.
menu.addAction("Arrange deck sections and turn stats…").triggered.connect(self._open_arrange)
def _apply_sections(self) -> None:
on = self.toggles[0].on
self.match_view.show_panels(you=on("you"), match=on("match"), opponent=on("opponent"),
turns=on("turns"))
self.deck_view.show_panels(my_deck=on("my_deck"), preview=on("preview"), opp_deck=on("opp_deck"))
def _open_arrange(self) -> None:
ArrangeDialog(self._arrangeable(), "Arrange sections and stats", self).exec()
@@ -380,16 +397,20 @@ class MainWindow(QMainWindow):
if self._rendered.get(index) == key:
return
self._rendered[index] = key
self._render_header(t)
self.deck_view.preview.parentWidget().setVisible(self.toggles[0].on("preview"))
self._render_header(t, show_match=index == MATCH)
self._apply_sections()
if index == MATCH:
self.deck_view.render(t)
self.match_view.render(t, self.now_ms())
self.turn_view.render(t)
def _render_header(self, t: MatchTracker) -> None:
def _render_header(self, t: MatchTracker, show_match: bool = True) -> None:
g = t.game
if g is None:
if not show_match:
# The match summary belongs to the Match tab only.
self.header.setText("")
self.header.setToolTip("")
elif g is None:
self.header.setText("<span style='color:#8b949c'>Waiting for a match…</span>")
else:
parts = [f"<b>vs {t.opponent_name or 'opponent'}</b>"]
+19 -3
View File
@@ -214,7 +214,8 @@ class MatchView(QWidget):
grid.addWidget(w, row, 0, Qt.AlignmentFlag.AlignHCenter)
grid.setRowStretch(7, 1)
top = QWidget()
self.turn_view = turn_view
top = self.top = QWidget()
top_lay = QHBoxLayout(top)
top_lay.setContentsMargins(0, 0, 0, 0)
top_lay.addWidget(self.me, 3)
@@ -233,11 +234,26 @@ class MatchView(QWidget):
lay = QVBoxLayout(self)
lay.setContentsMargins(0, 0, 0, 0)
lay.addWidget(self.split)
self._panels = {"you": True, "match": True, "opponent": True}
self._has_game = False
def show_panels(self, you: bool, match: bool, opponent: bool, turns: bool) -> None:
"""Hide/show panels; a row with every panel hidden collapses out of the splitter."""
self._panels = {"you": you, "match": match, "opponent": opponent}
self.top.setVisible(you or match or opponent)
self.turn_view.setVisible(turns)
self._apply_panels()
def _apply_panels(self) -> None:
# render() toggles these for "no game yet", so combine both conditions.
self.me.setVisible(self._has_game and self._panels["you"])
self.info.setVisible(self._has_game and self._panels["match"])
self.opp.setVisible(self._has_game and self._panels["opponent"])
def render(self, t: MatchTracker, now_ms: int) -> None:
g = t.game
for w in (self.me, self.opp, self.info):
w.setVisible(g is not None)
self._has_game = g is not None
self._apply_panels()
if g is None:
return
self.me.render(g, g.my_seat, "You", now_ms)