Files
mtga-assistant/src/mtga_assistant/ui/preview.py
T
Aeth3randClaude Opus 5.5 b3325e8ecc 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>
2026-10-16 12:34:02 -04:00

109 lines
3.7 KiB
Python

"""Large image of the hovered card, with a text fallback while it downloads."""
from __future__ import annotations
from PySide6.QtCore import QSize, Qt, Signal
from PySide6.QtGui import QMouseEvent, QPixmap, QResizeEvent
from PySide6.QtWidgets import QHBoxLayout, QLabel, QSizePolicy, QToolButton, QVBoxLayout, QWidget
from ..carddb import CardDB
from ..images import ImageCache
class CardPreview(QWidget):
cleared = Signal()
def __init__(self, cards: CardDB, images: ImageCache, parent=None):
super().__init__(parent)
self.cards = cards
self.images = images
self.grp_id: int | None = None
self._pixmap: QPixmap | None = None
self.image = QLabel()
self.image.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.image.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Ignored)
self.image.setMinimumSize(200, 280)
self.caption = QLabel()
self.caption.setWordWrap(True)
self.caption.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.close_btn = QToolButton()
self.close_btn.setText("✕")
self.close_btn.setToolTip("Stop previewing this card (Esc, or click the image)")
self.close_btn.clicked.connect(self.clear)
top = QHBoxLayout()
top.addStretch(1)
top.addWidget(self.close_btn)
lay = QVBoxLayout(self)
lay.setContentsMargins(4, 4, 4, 4)
lay.addLayout(top)
lay.addWidget(self.image, 1)
lay.addWidget(self.caption)
images.image_ready.connect(self._on_image_ready)
self.clear()
def clear(self) -> None:
"""Back to the empty state."""
was_showing = self.grp_id is not None
self.grp_id = None
self.caption.setText("")
self._show_placeholder("Hover a card to preview it")
self.close_btn.setVisible(False)
if was_showing:
self.cleared.emit()
def mousePressEvent(self, event: QMouseEvent) -> None:
if event.button() == Qt.MouseButton.LeftButton and self.grp_id is not None:
self.clear()
return
super().mousePressEvent(event)
def show_card(self, grp_id: int) -> None:
if grp_id == self.grp_id:
return
self.grp_id = grp_id
self.close_btn.setVisible(True)
info = self.cards.get(grp_id)
details = [info.type_line]
if info.power or info.toughness:
details.append(f"{info.power}/{info.toughness}")
if info.set_code:
details.append(f"{info.set_code} #{info.collector_number}")
self.caption.setText(f"<b>{info.name}</b><br>{' · '.join(d for d in details if d)}")
pm = self.images.pixmap(grp_id)
if pm is not None:
self._pixmap = pm
self._rescale()
elif self.images.is_failed(grp_id):
self._show_placeholder("No image available")
else:
self._show_placeholder("Loading image…")
def _show_placeholder(self, text: str) -> None:
self._pixmap = None
self.image.setPixmap(QPixmap())
self.image.setText(text)
def _on_image_ready(self, grp_id: int) -> None:
if grp_id == self.grp_id:
self.grp_id = None
self.show_card(grp_id)
def _rescale(self) -> None:
if self._pixmap is None:
return
self.image.setPixmap(
self._pixmap.scaled(self.image.size(), Qt.AspectRatioMode.KeepAspectRatio,
Qt.TransformationMode.SmoothTransformation)
)
def resizeEvent(self, event: QResizeEvent) -> None:
super().resizeEvent(event)
self._rescale()
def sizeHint(self) -> QSize:
return QSize(320, 600)