2026-01-27 11:49:37 +01:00
|
|
|
from enum import StrEnum
|
2026-01-28 16:25:40 +01:00
|
|
|
from enum import Enum, auto
|
2026-02-12 15:12:28 +01:00
|
|
|
from pathlib import Path
|
2026-02-17 16:37:36 +01:00
|
|
|
from typing import Dict
|
2026-01-27 11:49:37 +01:00
|
|
|
|
|
|
|
|
from PyQt6.QtCore import Qt
|
2026-02-18 11:15:53 +01:00
|
|
|
from PyQt6.QtGui import QIcon, QPixmap, QPainter
|
2026-02-12 15:12:28 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# Paths
|
|
|
|
|
PACKAGE_ROOT = Path(__file__).resolve().parent
|
|
|
|
|
VIEW_ROOT = PACKAGE_ROOT / "view"
|
|
|
|
|
RESOURCES_DIR = VIEW_ROOT / "resources"
|
2026-01-27 11:49:37 +01:00
|
|
|
|
|
|
|
|
ROLE_TYPE = Qt.ItemDataRole.UserRole
|
|
|
|
|
ROLE_ID = Qt.ItemDataRole.UserRole + 1
|
|
|
|
|
|
2026-02-02 10:41:16 +01:00
|
|
|
|
2026-02-12 15:12:28 +01:00
|
|
|
class IconName(str, Enum):
|
2026-02-18 11:15:53 +01:00
|
|
|
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"
|
2026-02-12 15:12:28 +01:00
|
|
|
WARCHRON = "warchron"
|
2026-02-18 11:15:53 +01:00
|
|
|
TOKEN = "token"
|
|
|
|
|
TOKENS = "tokens"
|
2026-02-23 17:36:28 +01:00
|
|
|
VP1ST = "vp1st"
|
|
|
|
|
VP2ND = "vp2nd"
|
|
|
|
|
VP3RD = "vp3rd"
|
|
|
|
|
VPNTH = "vpnth"
|
|
|
|
|
NP1ST = "np1st"
|
|
|
|
|
NP2ND = "np2nd"
|
|
|
|
|
NP3RD = "np3rd"
|
2026-02-18 11:15:53 +01:00
|
|
|
TIEBREAK_TOKEN = auto()
|
2026-02-23 17:36:28 +01:00
|
|
|
VP1STDRAW = auto()
|
2026-02-23 19:28:13 +01:00
|
|
|
VP1STBREAK = auto()
|
|
|
|
|
VP1STTIEDRAW = auto()
|
2026-02-23 17:36:28 +01:00
|
|
|
VP2NDDRAW = auto()
|
2026-02-23 19:28:13 +01:00
|
|
|
VP2NDBREAK = auto()
|
|
|
|
|
VP2NDTIEDRAW = auto()
|
2026-02-23 17:36:28 +01:00
|
|
|
VP3RDDRAW = auto()
|
2026-02-23 19:28:13 +01:00
|
|
|
VP3RDBREAK = auto()
|
|
|
|
|
VP3RDTIEDRAW = auto()
|
2026-02-23 17:36:28 +01:00
|
|
|
VPNTHDRAW = auto()
|
2026-02-23 19:28:13 +01:00
|
|
|
VPNTHBREAK = auto()
|
|
|
|
|
VPNTHTIEDRAW = auto()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RANK_TO_ICON = {
|
|
|
|
|
1: IconName.VP1ST,
|
|
|
|
|
2: IconName.VP2ND,
|
|
|
|
|
3: IconName.VP3RD,
|
|
|
|
|
4: IconName.VPNTH,
|
|
|
|
|
}
|
2026-02-12 15:12:28 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class Icons:
|
2026-02-18 11:15:53 +01:00
|
|
|
_icon_cache: Dict[IconName, QIcon] = {}
|
|
|
|
|
|
|
|
|
|
_pixmap_cache: Dict[IconName, QPixmap] = {}
|
2026-02-12 15:12:28 +01:00
|
|
|
|
|
|
|
|
_paths = {
|
|
|
|
|
IconName.UNDO: "arrow-curve-180-left",
|
|
|
|
|
IconName.REDO: "arrow-curve",
|
|
|
|
|
IconName.PAIRING: "arrow-switch",
|
|
|
|
|
IconName.DRAW: "balance.png",
|
2026-02-18 11:15:53 +01:00
|
|
|
IconName.TIEBREAK: "balance-unbalance.png",
|
2026-02-12 15:12:28 +01:00
|
|
|
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",
|
2026-02-18 19:06:39 +01:00
|
|
|
IconName.WARCHRON: "warchron_logo_background.png",
|
2026-02-18 11:15:53 +01:00
|
|
|
IconName.TOKEN: "point.png",
|
|
|
|
|
IconName.TOKENS: "points.png",
|
2026-02-23 17:36:28 +01:00
|
|
|
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",
|
2026-02-12 15:12:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def get(cls, name: IconName) -> QIcon:
|
2026-02-18 11:15:53 +01:00
|
|
|
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),
|
|
|
|
|
)
|
2026-02-23 17:36:28 +01:00
|
|
|
elif name == IconName.VP1STDRAW:
|
|
|
|
|
pix = cls._compose(
|
|
|
|
|
cls.get_pixmap(IconName.VP1ST),
|
|
|
|
|
cls.get_pixmap(IconName.DRAW),
|
|
|
|
|
)
|
2026-02-23 19:28:13 +01:00
|
|
|
elif name == IconName.VP1STBREAK:
|
2026-02-23 17:36:28 +01:00
|
|
|
pix = cls._compose(
|
|
|
|
|
cls.get_pixmap(IconName.VP1ST),
|
|
|
|
|
cls.get_pixmap(IconName.TOKEN),
|
|
|
|
|
)
|
2026-02-23 19:28:13 +01:00
|
|
|
elif name == IconName.VP1STTIEDRAW:
|
|
|
|
|
pix = cls._compose(
|
|
|
|
|
cls.get_pixmap(IconName.VP1ST),
|
|
|
|
|
cls.get_pixmap(IconName.DRAW),
|
|
|
|
|
cls.get_pixmap(IconName.TOKEN),
|
|
|
|
|
)
|
2026-02-23 17:36:28 +01:00
|
|
|
elif name == IconName.VP2NDDRAW:
|
|
|
|
|
pix = cls._compose(
|
|
|
|
|
cls.get_pixmap(IconName.VP2ND),
|
|
|
|
|
cls.get_pixmap(IconName.DRAW),
|
|
|
|
|
)
|
2026-02-23 19:28:13 +01:00
|
|
|
elif name == IconName.VP2NDBREAK:
|
|
|
|
|
pix = cls._compose(
|
|
|
|
|
cls.get_pixmap(IconName.VP2ND),
|
|
|
|
|
cls.get_pixmap(IconName.TOKEN),
|
|
|
|
|
)
|
|
|
|
|
elif name == IconName.VP2NDTIEDRAW:
|
2026-02-23 17:36:28 +01:00
|
|
|
pix = cls._compose(
|
|
|
|
|
cls.get_pixmap(IconName.VP2ND),
|
2026-02-23 19:28:13 +01:00
|
|
|
cls.get_pixmap(IconName.DRAW),
|
2026-02-23 17:36:28 +01:00
|
|
|
cls.get_pixmap(IconName.TOKEN),
|
|
|
|
|
)
|
|
|
|
|
elif name == IconName.VP3RDDRAW:
|
|
|
|
|
pix = cls._compose(
|
|
|
|
|
cls.get_pixmap(IconName.VP3RD),
|
|
|
|
|
cls.get_pixmap(IconName.DRAW),
|
|
|
|
|
)
|
2026-02-23 19:28:13 +01:00
|
|
|
elif name == IconName.VP3RDBREAK:
|
|
|
|
|
pix = cls._compose(
|
|
|
|
|
cls.get_pixmap(IconName.VP3RD),
|
|
|
|
|
cls.get_pixmap(IconName.TOKEN),
|
|
|
|
|
)
|
|
|
|
|
elif name == IconName.VP3RDTIEDRAW:
|
2026-02-23 17:36:28 +01:00
|
|
|
pix = cls._compose(
|
|
|
|
|
cls.get_pixmap(IconName.VP3RD),
|
2026-02-23 19:28:13 +01:00
|
|
|
cls.get_pixmap(IconName.DRAW),
|
2026-02-23 17:36:28 +01:00
|
|
|
cls.get_pixmap(IconName.TOKEN),
|
|
|
|
|
)
|
|
|
|
|
elif name == IconName.VPNTHDRAW:
|
|
|
|
|
pix = cls._compose(
|
|
|
|
|
cls.get_pixmap(IconName.VPNTH),
|
|
|
|
|
cls.get_pixmap(IconName.DRAW),
|
|
|
|
|
)
|
2026-02-23 19:28:13 +01:00
|
|
|
elif name == IconName.VPNTHBREAK:
|
2026-02-23 17:36:28 +01:00
|
|
|
pix = cls._compose(
|
|
|
|
|
cls.get_pixmap(IconName.VPNTH),
|
|
|
|
|
cls.get_pixmap(IconName.TOKEN),
|
|
|
|
|
)
|
2026-02-23 19:28:13 +01:00
|
|
|
elif name == IconName.VPNTHTIEDRAW:
|
|
|
|
|
pix = cls._compose(
|
|
|
|
|
cls.get_pixmap(IconName.VPNTH),
|
|
|
|
|
cls.get_pixmap(IconName.DRAW),
|
|
|
|
|
cls.get_pixmap(IconName.TOKEN),
|
|
|
|
|
)
|
2026-02-18 11:15:53 +01:00
|
|
|
else:
|
2026-02-12 15:12:28 +01:00
|
|
|
path = RESOURCES_DIR / cls._paths[name]
|
2026-02-18 11:15:53 +01:00
|
|
|
pix = QPixmap(path.as_posix())
|
|
|
|
|
cls._pixmap_cache[name] = pix
|
|
|
|
|
return pix
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
2026-02-23 19:28:13 +01:00
|
|
|
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)
|
2026-02-18 11:15:53 +01:00
|
|
|
result = QPixmap(w, h)
|
|
|
|
|
result.fill(Qt.GlobalColor.transparent)
|
|
|
|
|
painter = QPainter(result)
|
2026-02-23 19:28:13 +01:00
|
|
|
x = 0
|
|
|
|
|
for p in pixmaps:
|
|
|
|
|
painter.drawPixmap(x, (h - p.height()) // 2, p)
|
|
|
|
|
x += p.width()
|
2026-02-18 11:15:53 +01:00
|
|
|
painter.end()
|
|
|
|
|
return result
|
2026-02-12 15:12:28 +01:00
|
|
|
|
|
|
|
|
|
2026-01-27 11:49:37 +01:00
|
|
|
class ItemType(StrEnum):
|
|
|
|
|
PLAYER = "player"
|
|
|
|
|
WAR = "war"
|
|
|
|
|
CAMPAIGN = "campaign"
|
|
|
|
|
ROUND = "round"
|
2026-01-28 16:25:40 +01:00
|
|
|
OBJECTIVE = "objective"
|
|
|
|
|
WAR_PARTICIPANT = "war_participant"
|
2026-01-30 00:34:22 +01:00
|
|
|
SECTOR = "sector"
|
|
|
|
|
CAMPAIGN_PARTICIPANT = "campaign_participant"
|
2026-01-30 10:52:19 +01:00
|
|
|
CHOICE = "choice"
|
|
|
|
|
BATTLE = "battle"
|
2026-01-28 16:25:40 +01:00
|
|
|
|
2026-02-02 10:41:16 +01:00
|
|
|
|
2026-01-28 16:25:40 +01:00
|
|
|
class RefreshScope(Enum):
|
|
|
|
|
NONE = auto()
|
|
|
|
|
PLAYERS_LIST = auto()
|
|
|
|
|
WARS_TREE = auto()
|
|
|
|
|
WAR_DETAILS = auto()
|
|
|
|
|
CAMPAIGN_DETAILS = auto()
|
|
|
|
|
ROUND_DETAILS = auto()
|
2026-02-10 11:09:58 +01:00
|
|
|
CURRENT_SELECTION_DETAILS = auto()
|
2026-02-11 19:22:43 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class ContextType(StrEnum):
|
|
|
|
|
WAR = "war"
|
|
|
|
|
CAMPAIGN = "campaign"
|
|
|
|
|
CHOICE = "choice"
|
|
|
|
|
BATTLE = "battle"
|