from enum import StrEnum from enum import Enum, auto from pathlib import Path from typing import Dict from PyQt6.QtCore import Qt from PyQt6.QtGui import QIcon, QPixmap, QPainter # Paths PACKAGE_ROOT = Path(__file__).resolve().parent VIEW_ROOT = PACKAGE_ROOT / "view" RESOURCES_DIR = VIEW_ROOT / "resources" ROLE_TYPE = Qt.ItemDataRole.UserRole ROLE_ID = Qt.ItemDataRole.UserRole + 1 class IconName(str, Enum): UNDO = "undo" REDO = "redo" PAIRING = "pairing" DRAW = "draw" TIEBREAK = "tie-break" DELETE = "delete" SAVE_AS = "save_as" SAVE = "save" NEW = "new" EXIT = "exit" END = "end" OPEN = "load" ONGOING = "ongoing" EXPORT = "export" EDIT = "edit" ADD = "add" ABOUT = "about" WARS = "wars" DONE = "done" WIN = "win" PLAYERS = "players" WARCHRON = "warchron" TOKEN = "token" TOKENS = "tokens" VP1ST = "vp1st" VP2ND = "vp2nd" VP3RD = "vp3rd" VPNTH = "vpnth" NP1ST = "np1st" NP2ND = "np2nd" NP3RD = "np3rd" TIEBREAK_TOKEN = auto() VP1STDRAW = auto() VP1STBREAK = auto() VP1STTIEDRAW = auto() VP2NDDRAW = auto() VP2NDBREAK = auto() VP2NDTIEDRAW = auto() VP3RDDRAW = auto() VP3RDBREAK = auto() VP3RDTIEDRAW = auto() VPNTHDRAW = auto() VPNTHBREAK = auto() VPNTHTIEDRAW = auto() RANK_TO_ICON = { 1: IconName.VP1ST, 2: IconName.VP2ND, 3: IconName.VP3RD, 4: IconName.VPNTH, } class Icons: _icon_cache: Dict[IconName, QIcon] = {} _pixmap_cache: Dict[IconName, QPixmap] = {} _paths = { IconName.UNDO: "arrow-curve-180-left", IconName.REDO: "arrow-curve", IconName.PAIRING: "arrow-switch", IconName.DRAW: "balance.png", IconName.TIEBREAK: "balance-unbalance.png", IconName.DELETE: "cross.png", IconName.SAVE_AS: "disk--pencil.png", IconName.SAVE: "disk.png", IconName.NEW: "document.png", IconName.EXIT: "door--arrow.png", IconName.END: "flag-white.png", IconName.OPEN: "folder.png", IconName.ONGOING: "hourglass.png", IconName.EXPORT: "notebook--arrow.png", IconName.EDIT: "pencil.png", IconName.ADD: "plus.png", IconName.ABOUT: "question.png", IconName.WARS: "swords-small.png", IconName.DONE: "tick.png", IconName.WIN: "trophy.png", IconName.PLAYERS: "users.png", IconName.WARCHRON: "warchron_logo_background.png", IconName.TOKEN: "point.png", IconName.TOKENS: "points.png", IconName.VP1ST: "trophy.png", IconName.VP2ND: "trophy-silver.png", IconName.VP3RD: "trophy-bronze.png", IconName.VPNTH: "ribbon.png", IconName.NP1ST: "medal.png", IconName.NP2ND: "medal-silver.png", IconName.NP3RD: "medal-bronze.png", } @classmethod def get(cls, name: IconName) -> QIcon: if name not in cls._icon_cache: path = RESOURCES_DIR / cls._paths[name] cls._icon_cache[name] = QIcon(str(path)) return cls._icon_cache[name] @classmethod def get_pixmap(cls, name: IconName) -> QPixmap: if name in cls._pixmap_cache: return cls._pixmap_cache[name] if name == IconName.TIEBREAK_TOKEN: pix = cls._compose( cls.get_pixmap(IconName.TIEBREAK), cls.get_pixmap(IconName.TOKEN), ) elif name == IconName.VP1STDRAW: pix = cls._compose( cls.get_pixmap(IconName.VP1ST), cls.get_pixmap(IconName.DRAW), ) elif name == IconName.VP1STBREAK: pix = cls._compose( cls.get_pixmap(IconName.VP1ST), cls.get_pixmap(IconName.TOKEN), ) elif name == IconName.VP1STTIEDRAW: pix = cls._compose( cls.get_pixmap(IconName.VP1ST), cls.get_pixmap(IconName.DRAW), cls.get_pixmap(IconName.TOKEN), ) elif name == IconName.VP2NDDRAW: pix = cls._compose( cls.get_pixmap(IconName.VP2ND), cls.get_pixmap(IconName.DRAW), ) elif name == IconName.VP2NDBREAK: pix = cls._compose( cls.get_pixmap(IconName.VP2ND), cls.get_pixmap(IconName.TOKEN), ) elif name == IconName.VP2NDTIEDRAW: pix = cls._compose( cls.get_pixmap(IconName.VP2ND), cls.get_pixmap(IconName.DRAW), cls.get_pixmap(IconName.TOKEN), ) elif name == IconName.VP3RDDRAW: pix = cls._compose( cls.get_pixmap(IconName.VP3RD), cls.get_pixmap(IconName.DRAW), ) elif name == IconName.VP3RDBREAK: pix = cls._compose( cls.get_pixmap(IconName.VP3RD), cls.get_pixmap(IconName.TOKEN), ) elif name == IconName.VP3RDTIEDRAW: pix = cls._compose( cls.get_pixmap(IconName.VP3RD), cls.get_pixmap(IconName.DRAW), cls.get_pixmap(IconName.TOKEN), ) elif name == IconName.VPNTHDRAW: pix = cls._compose( cls.get_pixmap(IconName.VPNTH), cls.get_pixmap(IconName.DRAW), ) elif name == IconName.VPNTHBREAK: pix = cls._compose( cls.get_pixmap(IconName.VPNTH), cls.get_pixmap(IconName.TOKEN), ) elif name == IconName.VPNTHTIEDRAW: pix = cls._compose( cls.get_pixmap(IconName.VPNTH), cls.get_pixmap(IconName.DRAW), cls.get_pixmap(IconName.TOKEN), ) else: path = RESOURCES_DIR / cls._paths[name] pix = QPixmap(path.as_posix()) cls._pixmap_cache[name] = pix return pix @staticmethod def _compose(*pixmaps: QPixmap) -> QPixmap: if not pixmaps: return QPixmap() if len(pixmaps) == 1: return pixmaps[0] w = sum(p.width() for p in pixmaps) h = max(p.height() for p in pixmaps) result = QPixmap(w, h) result.fill(Qt.GlobalColor.transparent) painter = QPainter(result) x = 0 for p in pixmaps: painter.drawPixmap(x, (h - p.height()) // 2, p) x += p.width() painter.end() return result class ItemType(StrEnum): PLAYER = "player" WAR = "war" CAMPAIGN = "campaign" ROUND = "round" OBJECTIVE = "objective" WAR_PARTICIPANT = "war_participant" SECTOR = "sector" CAMPAIGN_PARTICIPANT = "campaign_participant" CHOICE = "choice" BATTLE = "battle" class RefreshScope(Enum): NONE = auto() PLAYERS_LIST = auto() WARS_TREE = auto() WAR_DETAILS = auto() CAMPAIGN_DETAILS = auto() ROUND_DETAILS = auto() CURRENT_SELECTION_DETAILS = auto() class ContextType(StrEnum): WAR = "war" CAMPAIGN = "campaign" CHOICE = "choice" BATTLE = "battle"