from pathlib import Path import calendar from PyQt6 import QtWidgets from PyQt6.QtCore import Qt from PyQt6.QtWidgets import QDialog, QFileDialog, QTreeWidgetItem, QMenu from PyQt6.QtGui import QCloseEvent from warchron.view.ui.ui_main_window import Ui_MainWindow from warchron.view.ui.ui_player_dialog import Ui_playerDialog from warchron.view.ui.ui_war_dialog import Ui_warDialog from warchron.view.ui.ui_campaign_dialog import Ui_campaignDialog ROLE_TYPE = Qt.ItemDataRole.UserRole ROLE_ID = Qt.ItemDataRole.UserRole + 1 class View(QtWidgets.QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(View, self).__init__(parent) self.setupUi(self) self.on_close_callback = None self.on_selection_changed = None self.on_add_campaign = None self.on_add_round = None self.on_edit_item = None self.on_delete_item = None self.playersTable.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.playersTable.customContextMenuRequested.connect(self._on_players_table_context_menu) self.warsTree.currentItemChanged.connect(self._emit_selection_changed) self.warsTree.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.warsTree.customContextMenuRequested.connect(self._on_wars_tree_context_menu) self.addCampaignBtn.clicked.connect(self._on_add_campaign_clicked) self.addRoundBtn.clicked.connect(self._on_add_round_clicked) def _emit_selection_changed(self, current, previous): if not self.on_tree_selection_changed: return if not current: self.on_tree_selection_changed(None) return self.on_tree_selection_changed({ "type": current.data(0, ROLE_TYPE), "id": current.data(0, ROLE_ID), }) def _on_players_table_context_menu(self, pos): item = self.playersTable.itemAt(pos) if not item: return player_id = item.data(Qt.ItemDataRole.UserRole) menu = QMenu(self) edit_action = menu.addAction("Edit") delete_action = menu.addAction("Delete") action = menu.exec(self.playersTable.viewport().mapToGlobal(pos)) if action == edit_action and self.on_edit_item: self.on_edit_item("player", player_id) elif action == delete_action and self.on_delete_item: self.on_delete_item("player", player_id) def _on_wars_tree_context_menu(self, pos): item = self.warsTree.itemAt(pos) if not item: return item_type = item.data(0, ROLE_TYPE) item_id = item.data(0, ROLE_ID) menu = QMenu(self) edit_action = None if item_type != "round": edit_action = menu.addAction("Edit") delete_action = menu.addAction("Delete") action = menu.exec(self.warsTree.viewport().mapToGlobal(pos)) if action == edit_action: if self.on_edit_item: self.on_edit_item(item_type, item_id) elif action == delete_action: if self.on_delete_item: self.on_delete_item(item_type, item_id) def _on_add_campaign_clicked(self): if self.on_add_campaign: self.on_add_campaign() def _on_add_round_clicked(self): if self.on_add_round: self.on_add_round() def closeEvent(self, event: QCloseEvent): if self.on_close_callback: proceed = self.on_close_callback() if not proceed: event.ignore() return event.accept() def ask_open_file(self) -> Path | None: filename, _ = QFileDialog.getOpenFileName( self, "Open war history", "", "WarChron files (*.json)" ) return Path(filename) if filename else None def ask_save_file(self) -> Path | None: filename, _ = QFileDialog.getSaveFileName( self, "Save war history", "", "WarChron files (*.json)" ) return Path(filename) if filename else None def display_players(self, players: list): table = self.playersTable table.setRowCount(len(players)) for row, player in enumerate(players): play_item = QtWidgets.QTableWidgetItem(player.name) play_item.setData(Qt.ItemDataRole.UserRole, player.id) table.setItem(row, 0, play_item) table.resizeColumnsToContents() def display_wars(self, wars: list): tree = self.warsTree tree.clear() tree.setColumnCount(1) tree.setHeaderLabels(["Wars"]) for war in wars: war_item = QTreeWidgetItem([f"{war.name} ({war.year})"]) war_item.setData(0, ROLE_TYPE, "war") war_item.setData(0, ROLE_ID, war.id) tree.addTopLevelItem(war_item) for camp in war.get_all_campaigns(): camp_item = QTreeWidgetItem([f"{camp.name} ({calendar.month_name[camp.month]})"]) camp_item.setData(0, ROLE_TYPE, "campaign") camp_item.setData(0, ROLE_ID, camp.id) war_item.addChild(camp_item) for index, rnd in enumerate(camp.get_all_rounds(), start=1): rnd_item = QTreeWidgetItem([f"Round {index}"]) rnd_item.setData(0, ROLE_TYPE, "round") rnd_item.setData(0, ROLE_ID, rnd.id) camp_item.addChild(rnd_item) tree.expandAll() def get_selected_tree_item(self): item = self.warsTree.currentItem() if not item: return None return { "type": item.data(0, ROLE_TYPE), "id": item.data(0, ROLE_ID) } def set_add_campaign_enabled(self, enabled: bool): self.addCampaignBtn.setEnabled(enabled) def set_add_round_enabled(self, enabled: bool): self.addRoundBtn.setEnabled(enabled) def get_current_tab(self) -> str: index = self.tabWidget.currentIndex() if index == 0: return "players" elif index == 1: return "wars" return "" class PlayerDialog(QDialog): def __init__(self, parent=None, *, default_name: str = ""): super().__init__(parent) self.ui = Ui_playerDialog() self.ui.setupUi(self) self.ui.playerName.setText(default_name) def get_player_name(self) -> str: return self.ui.playerName.text().strip() class WarDialog(QDialog): def __init__(self, parent=None, default_name: str = "", default_year: int | None = None): super().__init__(parent) self.ui = Ui_warDialog() self.ui.setupUi(self) self.ui.warName.setText(default_name) if default_year is not None: self.ui.warYear.setValue(default_year) def get_war_name(self) -> str: return self.ui.warName.text().strip() def get_war_year(self) -> int: return int(self.ui.warYear.value()) class CampaignDialog(QDialog): def __init__(self, parent=None, default_name: str = "", default_month: int | None = None): super().__init__(parent) self.ui = Ui_campaignDialog() self.ui.setupUi(self) self.ui.campaignName.setText(default_name) if default_month is not None: self.ui.campaignMonth.setValue(default_month) def get_campaign_name(self) -> str: return self.ui.campaignName.text().strip() def get_campaign_month(self) -> int: return int(self.ui.campaignMonth.value())