- 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>
109 lines
3.7 KiB
Python
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)
|