warchron_app/src/warchron/controller/controller.py

195 lines
6.6 KiB
Python
Raw Normal View History

2026-01-19 11:16:23 +01:00
from pathlib import Path
from PyQt6.QtWidgets import QMessageBox, QDialog
2026-01-21 07:43:04 +01:00
from warchron.model.model import Model
from warchron.view.view import View
2026-01-19 11:16:23 +01:00
2026-01-21 07:43:04 +01:00
from warchron.view.view import PlayerDialog, WarDialog, CampaignDialog
2026-01-19 11:16:23 +01:00
class Controller:
2026-01-21 07:43:04 +01:00
def __init__(self, model: Model, view: View):
2026-01-19 11:16:23 +01:00
self.model = model
self.view = view
self.current_file: Path | None = None
2026-01-21 07:43:04 +01:00
self.selected_war_id = None
self.selected_campaign_id = None
self.selected_round_id = None
2026-01-19 11:16:23 +01:00
self.view.on_close_callback = self.on_app_close
self.is_dirty = False
self.__connect()
self.refresh_players_view()
2026-01-20 08:46:58 +01:00
self.refresh_wars_view()
2026-01-19 11:29:41 +01:00
self.update_window_title()
2026-01-21 07:43:04 +01:00
self.update_actions_state()
self.view.on_tree_selection_changed = self.on_tree_selection_changed
self.view.on_add_campaign = self.add_campaign
self.view.on_add_round = self.add_round
2026-01-19 11:16:23 +01:00
def __connect(self):
self.view.actionExit.triggered.connect(self.view.close)
self.view.actionNew.triggered.connect(self.new)
self.view.actionOpen.triggered.connect(self.open_file)
self.view.actionSave.triggered.connect(self.save)
self.view.actionSave_as.triggered.connect(self.save_as)
2026-01-20 08:46:58 +01:00
self.view.addPlayerBtn.clicked.connect(self.add_player)
self.view.addWarBtn.clicked.connect(self.add_war)
2026-01-19 11:16:23 +01:00
2026-01-20 08:46:58 +01:00
def on_app_close(self) -> bool:
if self.is_dirty:
reply = QMessageBox.question(
self.view,
"Unsaved changes",
"You have unsaved changes. Do you want to save before quitting?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No | QMessageBox.StandardButton.Cancel
)
if reply == QMessageBox.StandardButton.Yes:
self.save()
elif reply == QMessageBox.StandardButton.Cancel:
return False
return True
2026-01-19 11:16:23 +01:00
def new(self):
if self.is_dirty:
reply = QMessageBox.question(
self.view,
"Unsaved changes",
"Discard current campaign?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
)
if reply != QMessageBox.StandardButton.Yes:
return
self.model.new()
self.current_file = None
self.is_dirty = False
self.refresh_players_view()
2026-01-20 08:46:58 +01:00
self.refresh_wars_view()
2026-01-19 11:16:23 +01:00
self.update_window_title()
def open_file(self):
if self.is_dirty:
reply = QMessageBox.question(
self.view,
"Unsaved changes",
"Discard current campaign?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
)
if reply != QMessageBox.StandardButton.Yes:
return
path = self.view.ask_open_file()
if not path:
return
self.model.load(path)
self.current_file = path
self.is_dirty = False
self.refresh_players_view()
2026-01-20 08:46:58 +01:00
self.refresh_wars_view()
2026-01-19 11:16:23 +01:00
self.update_window_title()
2026-01-20 08:46:58 +01:00
2026-01-19 11:16:23 +01:00
def save(self):
if not self.current_file:
self.save_as()
return
self.model.save(self.current_file)
self.is_dirty = False
self.update_window_title()
def save_as(self):
path = self.view.ask_save_file()
if not path:
return
self.current_file = path
self.model.save(path)
self.is_dirty = False
self.update_window_title()
def update_window_title(self):
base = "WarChron"
if self.current_file:
base += f" - {self.current_file.name}"
2026-01-19 11:29:41 +01:00
else:
base += f" - New file"
2026-01-19 11:16:23 +01:00
if self.is_dirty:
2026-01-19 11:29:41 +01:00
base = base + " *"
2026-01-19 11:16:23 +01:00
self.view.setWindowTitle(base)
2026-01-20 08:46:58 +01:00
def refresh_players_view(self):
players = self.model.get_all_players()
self.view.display_players(players)
2026-01-19 11:16:23 +01:00
2026-01-21 07:43:04 +01:00
def refresh_wars_view(self):
wars = self.model.get_all_wars()
self.view.display_wars(wars)
def on_tree_selection_changed(self, selection):
self.selected_war_id = None
self.selected_campaign_id = None
self.selected_round_id = None
if selection:
if selection["type"] == "war":
self.selected_war_id = selection["id"]
elif selection["type"] == "campaign":
self.selected_campaign_id = selection["id"]
elif selection["type"] == "round":
self.selected_round_id = selection["id"]
self.update_actions_state()
def update_actions_state(self):
self.view.set_add_campaign_enabled(self.selected_war_id is not None)
self.view.set_add_round_enabled(self.selected_campaign_id is not None)
2026-01-19 11:16:23 +01:00
def add_player(self):
dialog = PlayerDialog(self.view)
result = dialog.exec() # modal blocking dialog
if result == QDialog.DialogCode.Accepted:
name = dialog.get_player_name()
if not name:
QMessageBox.warning(
self.view,
"Invalid name",
"Player name cannot be empty."
)
return
self.model.add_player(name)
self.is_dirty = True
self.refresh_players_view()
self.update_window_title()
2026-01-21 07:43:04 +01:00
2026-01-20 08:46:58 +01:00
def add_war(self):
dialog = WarDialog(self.view)
result = dialog.exec() # modal blocking dialog
if result == QDialog.DialogCode.Accepted:
name = dialog.get_war_name()
if not name:
QMessageBox.warning(
self.view,
"Invalid name",
"War name cannot be empty."
)
return
self.model.add_war(name)
self.is_dirty = True
self.refresh_wars_view()
self.update_window_title()
2026-01-21 07:43:04 +01:00
def add_campaign(self):
if not self.selected_war_id:
return
dialog = CampaignDialog(self.view)
if dialog.exec() != QDialog.DialogCode.Accepted:
return
name = dialog.get_campaign_name()
if not name:
return
self.model.add_campaign(self.selected_war_id, name)
self.is_dirty = True
self.refresh_wars_view()
self.update_window_title()
def add_round(self):
if not self.selected_campaign_id:
return
self.model.add_round(self.selected_campaign_id)
self.is_dirty = True
self.refresh_wars_view()
self.update_window_title()