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
+68 -22

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 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. 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* shows or hides each panel of the Match page (You, Match, Opponent, Turn stats,
*Customize → Arrange…* reorders (drag, or ↑/↓) and shows/hides the My deck and Opponent sections My deck, Preview, Opponent's cards) and the details in the player panels. *Customize → Arrange…*
and the turn stats. Your choices are remembered. 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. 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) self.preview.cleared.connect(self._clear_selection)
split = QSplitter(Qt.Orientation.Horizontal) split = QSplitter(Qt.Orientation.Horizontal)
split.addWidget(card_frame("My deck", self.mine)) self.my_frame = card_frame("My deck", self.mine)
split.addWidget(card_frame("Preview", self.preview)) self.preview_frame = card_frame("Preview", self.preview)
split.addWidget(card_frame("Opponent", self.opp)) 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(0, 3)
split.setStretchFactor(1, 2) split.setStretchFactor(1, 2)
split.setStretchFactor(2, 3) split.setStretchFactor(2, 3)
@@ -86,6 +88,12 @@ class DeckView(QWidget):
lay.setContentsMargins(0, 0, 0, 0) lay.setContentsMargins(0, 0, 0, 0)
lay.addWidget(split) 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: def _clear_selection(self) -> None:
for lst in (self.mine, self.opp): for lst in (self.mine, self.opp):
lst.setCurrentItem(None) lst.setCurrentItem(None)
+34 -13
View File
@@ -39,7 +39,16 @@ from .toggles import StayOpenMenu, Toggles
from .turn_view import TURN_DEFAULTS_OFF, TURN_TOGGLES, TurnView from .turn_view import TURN_DEFAULTS_OFF, TURN_TOGGLES, TurnView
REFRESH_MS = 150 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"] VIEWS = ["Match", "History"]
MATCH, HISTORY = 0, 1 MATCH, HISTORY = 0, 1
@@ -91,7 +100,7 @@ class MainWindow(QMainWindow):
self._settings = QSettings("mtga-assistant", "mtga-assistant") self._settings = QSettings("mtga-assistant", "mtga-assistant")
self.toggles = [ self.toggles = [
Toggles("deck", DECK_TOGGLES, parent=self), Toggles("sections", SECTION_TOGGLES, parent=self),
Toggles("match", MATCH_TOGGLES, parent=self), Toggles("match", MATCH_TOGGLES, parent=self),
Toggles("turn", TURN_TOGGLES, TURN_DEFAULTS_OFF, parent=self), Toggles("turn", TURN_TOGGLES, TURN_DEFAULTS_OFF, parent=self),
] ]
@@ -244,14 +253,22 @@ class MainWindow(QMainWindow):
def _fill_customize(self) -> None: def _fill_customize(self) -> None:
menu = self.customize_menu menu = self.customize_menu
menu.clear() menu.clear()
self.toggles[1].fill_menu(menu) # match panels for heading, tg in (("Sections", self.toggles[0]), ("Player panel details", self.toggles[1])):
for title, tg in (("Turn stats", self.toggles[2]), ("My deck sections", self.my_sections), title = menu.addAction(heading)
("Opponent sections", self.opp_sections), ("Preview", self.toggles[0])): title.setEnabled(False) # a heading, not an option
sub = StayOpenMenu(title, menu) font = title.font()
tg.fill_menu(sub) font.setBold(True)
menu.addMenu(sub) title.setFont(font)
tg.fill_menu(menu)
menu.addSeparator() menu.addSeparator()
menu.addAction("Arrange sections and stats…").triggered.connect(self._open_arrange) # 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: def _open_arrange(self) -> None:
ArrangeDialog(self._arrangeable(), "Arrange sections and stats", self).exec() ArrangeDialog(self._arrangeable(), "Arrange sections and stats", self).exec()
@@ -380,16 +397,20 @@ class MainWindow(QMainWindow):
if self._rendered.get(index) == key: if self._rendered.get(index) == key:
return return
self._rendered[index] = key self._rendered[index] = key
self._render_header(t) self._render_header(t, show_match=index == MATCH)
self.deck_view.preview.parentWidget().setVisible(self.toggles[0].on("preview")) self._apply_sections()
if index == MATCH: if index == MATCH:
self.deck_view.render(t) self.deck_view.render(t)
self.match_view.render(t, self.now_ms()) self.match_view.render(t, self.now_ms())
self.turn_view.render(t) 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 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>") self.header.setText("<span style='color:#8b949c'>Waiting for a match…</span>")
else: else:
parts = [f"<b>vs {t.opponent_name or 'opponent'}</b>"] 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.addWidget(w, row, 0, Qt.AlignmentFlag.AlignHCenter)
grid.setRowStretch(7, 1) grid.setRowStretch(7, 1)
top = QWidget() self.turn_view = turn_view
top = self.top = QWidget()
top_lay = QHBoxLayout(top) top_lay = QHBoxLayout(top)
top_lay.setContentsMargins(0, 0, 0, 0) top_lay.setContentsMargins(0, 0, 0, 0)
top_lay.addWidget(self.me, 3) top_lay.addWidget(self.me, 3)
@@ -233,11 +234,26 @@ class MatchView(QWidget):
lay = QVBoxLayout(self) lay = QVBoxLayout(self)
lay.setContentsMargins(0, 0, 0, 0) lay.setContentsMargins(0, 0, 0, 0)
lay.addWidget(self.split) 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: def render(self, t: MatchTracker, now_ms: int) -> None:
g = t.game g = t.game
for w in (self.me, self.opp, self.info): self._has_game = g is not None
w.setVisible(g is not None) self._apply_panels()
if g is None: if g is None:
return return
self.me.render(g, g.my_seat, "You", now_ms) self.me.render(g, g.my_seat, "You", now_ms)