Lemontropia-Suite/ui/loadout_selection_dialog_si...

263 lines
9.1 KiB
Python

"""
Lemontropia Suite - Loadout Selection Dialog v2.0
Simplified cost-focused selection.
"""
import json
import logging
from decimal import Decimal
from pathlib import Path
from PyQt6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QLabel,
QPushButton, QListWidget, QListWidgetItem,
QMessageBox, QWidget, QGridLayout
)
from PyQt6.QtCore import Qt, pyqtSignal
from ui.loadout_manager_simple import LoadoutConfig
logger = logging.getLogger(__name__)
class LoadoutSelectionDialog(QDialog):
"""Simplified dialog for selecting a loadout to start a session.
Emits cost data that can be used directly by MainWindow for tracking.
"""
# Signal emits: {
# 'id': 0, # 0 = file-based
# 'name': 'Loadout Name',
# 'source': 'file',
# 'costs': {
# 'cost_per_shot': Decimal,
# 'cost_per_hit': Decimal,
# 'cost_per_heal': Decimal,
# },
# 'display': {
# 'weapon_name': str,
# 'armor_name': str,
# 'healing_name': str,
# }
# }
loadout_selected = pyqtSignal(dict)
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Select Loadout for Session")
self.setMinimumSize(500, 400)
self.config_dir = Path.home() / ".lemontropia" / "loadouts"
self.selected_loadout = None
self._setup_ui()
self._load_loadouts()
def _setup_ui(self):
"""Setup simple UI."""
layout = QVBoxLayout(self)
layout.setSpacing(10)
# Header
header = QLabel("Select a loadout to track costs during your session:")
header.setStyleSheet("font-size: 12px; color: #888;")
layout.addWidget(header)
# Loadout list
self.loadout_list = QListWidget()
self.loadout_list.itemClicked.connect(self._on_select)
self.loadout_list.itemDoubleClicked.connect(self._on_double_click)
layout.addWidget(self.loadout_list)
# Preview panel
self.preview = QWidget()
preview_layout = QGridLayout(self.preview)
# Weapon
preview_layout.addWidget(QLabel("⚔️ Weapon:"), 0, 0)
self.preview_weapon = QLabel("-")
preview_layout.addWidget(self.preview_weapon, 0, 1)
preview_layout.addWidget(QLabel(" Cost/Shot:"), 1, 0)
self.preview_cost_shot = QLabel("-")
self.preview_cost_shot.setStyleSheet("color: #7FFF7F;")
preview_layout.addWidget(self.preview_cost_shot, 1, 1)
# Armor
preview_layout.addWidget(QLabel("🛡️ Armor:"), 2, 0)
self.preview_armor = QLabel("-")
preview_layout.addWidget(self.preview_armor, 2, 1)
preview_layout.addWidget(QLabel(" Cost/Hit:"), 3, 0)
self.preview_cost_hit = QLabel("-")
self.preview_cost_hit.setStyleSheet("color: #7FFF7F;")
preview_layout.addWidget(self.preview_cost_hit, 3, 1)
# Healing
preview_layout.addWidget(QLabel("💚 Healing:"), 4, 0)
self.preview_healing = QLabel("-")
preview_layout.addWidget(self.preview_healing, 4, 1)
preview_layout.addWidget(QLabel(" Cost/Heal:"), 5, 0)
self.preview_cost_heal = QLabel("-")
self.preview_cost_heal.setStyleSheet("color: #7FFF7F;")
preview_layout.addWidget(self.preview_cost_heal, 5, 1)
preview_layout.setColumnStretch(1, 1)
layout.addWidget(self.preview)
# Buttons
button_layout = QHBoxLayout()
button_layout.addStretch()
self.skip_btn = QPushButton("Skip (No Cost Tracking)")
self.skip_btn.clicked.connect(self._on_skip)
self.skip_btn.setStyleSheet("color: #888;")
button_layout.addWidget(self.skip_btn)
self.ok_btn = QPushButton("Start Session ▶")
self.ok_btn.clicked.connect(self._on_accept)
self.ok_btn.setEnabled(False)
self.ok_btn.setStyleSheet("""
QPushButton {
background-color: #2E7D32;
color: white;
padding: 8px 16px;
font-weight: bold;
}
QPushButton:disabled {
background-color: #333;
color: #666;
}
""")
button_layout.addWidget(self.ok_btn)
self.cancel_btn = QPushButton("Cancel")
self.cancel_btn.clicked.connect(self.reject)
button_layout.addWidget(self.cancel_btn)
layout.addLayout(button_layout)
def _load_loadouts(self):
"""Load all saved loadouts."""
self.loadout_list.clear()
self.loadouts = []
if not self.config_dir.exists():
return
try:
for filepath in sorted(self.config_dir.glob("*.json")):
try:
with open(filepath, 'r') as f:
data = json.load(f)
config = LoadoutConfig.from_dict(data)
# Build display text
has_costs = (
config.weapon_cost_per_shot > 0 or
config.armor_cost_per_hit > 0 or
config.healing_cost_per_heal > 0
)
display = f"📋 {config.name}"
if has_costs:
display += f" (💰 {config.weapon_cost_per_shot:.3f}/shot)"
item = QListWidgetItem(display)
item.setData(Qt.ItemDataRole.UserRole, config)
# Tooltip
tooltip = (
f"Weapon: {config.weapon_name}\n"
f"Armor: {config.armor_name}\n"
f"Healing: {config.healing_name}\n"
f"\n"
f"Cost/Shot: {config.weapon_cost_per_shot:.4f} PED\n"
f"Cost/Hit: {config.armor_cost_per_hit:.4f} PED\n"
f"Cost/Heal: {config.healing_cost_per_heal:.4f} PED"
)
item.setToolTip(tooltip)
self.loadout_list.addItem(item)
self.loadouts.append(config)
except Exception as e:
logger.error(f"Failed to load {filepath}: {e}")
except Exception as e:
logger.error(f"Failed to list loadouts: {e}")
if not self.loadouts:
item = QListWidgetItem("(No saved loadouts)")
item.setFlags(Qt.ItemFlag.NoItemFlags)
self.loadout_list.addItem(item)
def _on_select(self, item: QListWidgetItem):
"""Update preview when loadout selected."""
config = item.data(Qt.ItemDataRole.UserRole)
if not isinstance(config, LoadoutConfig):
return
self.selected_loadout = config
# Update preview
self.preview_weapon.setText(config.weapon_name if config.weapon_name != "None" else "Not set")
self.preview_armor.setText(config.armor_name if config.armor_name != "None" else "Not set")
self.preview_healing.setText(config.healing_name if config.healing_name != "None" else "Not set")
self.preview_cost_shot.setText(f"{config.weapon_cost_per_shot:.4f} PED")
self.preview_cost_hit.setText(f"{config.armor_cost_per_hit:.4f} PED")
self.preview_cost_heal.setText(f"{config.healing_cost_per_heal:.4f} PED")
self.ok_btn.setEnabled(True)
def _on_double_click(self, item: QListWidgetItem):
"""Double-click to select immediately."""
self._on_select(item)
self._on_accept()
def _on_skip(self):
"""Start session without cost tracking."""
self.loadout_selected.emit({
'id': 0,
'name': 'No Loadout',
'source': 'none',
'costs': {
'cost_per_shot': Decimal('0'),
'cost_per_hit': Decimal('0'),
'cost_per_heal': Decimal('0'),
},
'display': {
'weapon_name': 'None',
'armor_name': 'None',
'healing_name': 'None',
}
})
self.accept()
def _on_accept(self):
"""Emit selected loadout and close."""
if not self.selected_loadout:
QMessageBox.warning(self, "No Selection", "Please select a loadout")
return
config = self.selected_loadout
self.loadout_selected.emit({
'id': 0, # File-based
'name': config.name,
'source': 'file',
'costs': {
'cost_per_shot': config.weapon_cost_per_shot,
'cost_per_hit': config.armor_cost_per_hit,
'cost_per_heal': config.healing_cost_per_heal,
},
'display': {
'weapon_name': config.weapon_name,
'armor_name': config.armor_name,
'healing_name': config.healing_name,
}
})
self.accept()