mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
UI isn't finished. Bulk transfers where the player doesn't care what aircraft get used work (though they're chosen with no thought at all), but being able to plan your own airlift flight isn't here yet. Cargo planes are not implemented yet. No way to view the cargo of a flight (will come with the cargo flight planning UI). The airlift flight/package creation should probably be moved out of the UI and into the game code. AI doesn't use these yet. https://github.com/Khopa/dcs_liberation/issues/825
203 lines
6.8 KiB
Python
203 lines
6.8 KiB
Python
from typing import Optional
|
|
|
|
from PySide2.QtCore import (
|
|
QItemSelection,
|
|
QItemSelectionModel,
|
|
QModelIndex,
|
|
QSize,
|
|
Qt,
|
|
)
|
|
from PySide2.QtGui import QContextMenuEvent, QFont, QFontMetrics, QIcon, QPainter
|
|
from PySide2.QtWidgets import (
|
|
QAbstractItemView,
|
|
QAction,
|
|
QDialog,
|
|
QHBoxLayout,
|
|
QListView,
|
|
QMenu,
|
|
QPushButton,
|
|
QStyle,
|
|
QStyleOptionViewItem,
|
|
QStyledItemDelegate,
|
|
QVBoxLayout,
|
|
)
|
|
|
|
from game.transfers import TransferOrder
|
|
from qt_ui.delegate_helpers import painter_context
|
|
from qt_ui.models import GameModel, TransferModel
|
|
|
|
|
|
class TransferDelegate(QStyledItemDelegate):
|
|
FONT_SIZE = 10
|
|
HMARGIN = 4
|
|
VMARGIN = 4
|
|
|
|
def __init__(self, transfer_model: TransferModel) -> None:
|
|
super().__init__()
|
|
self.transfer_model = transfer_model
|
|
|
|
def get_font(self, option: QStyleOptionViewItem) -> QFont:
|
|
font = QFont(option.font)
|
|
font.setPointSize(self.FONT_SIZE)
|
|
return font
|
|
|
|
@staticmethod
|
|
def transfer(index: QModelIndex) -> TransferOrder:
|
|
return index.data(TransferModel.TransferRole)
|
|
|
|
def first_row_text(self, index: QModelIndex) -> str:
|
|
return self.transfer_model.data(index, Qt.DisplayRole)
|
|
|
|
def second_row_text(self, index: QModelIndex) -> str:
|
|
return self.transfer(index).description
|
|
|
|
def paint(
|
|
self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex
|
|
) -> None:
|
|
# Draw the list item with all the default selection styling, but with an
|
|
# invalid index so text formatting is left to us.
|
|
super().paint(painter, option, QModelIndex())
|
|
|
|
rect = option.rect.adjusted(
|
|
self.HMARGIN, self.VMARGIN, -self.HMARGIN, -self.VMARGIN
|
|
)
|
|
|
|
with painter_context(painter):
|
|
painter.setFont(self.get_font(option))
|
|
|
|
icon: Optional[QIcon] = index.data(Qt.DecorationRole)
|
|
if icon is not None:
|
|
icon.paint(
|
|
painter,
|
|
rect,
|
|
Qt.AlignLeft | Qt.AlignVCenter,
|
|
self.icon_mode(option),
|
|
self.icon_state(option),
|
|
)
|
|
|
|
rect = rect.adjusted(self.icon_size(option).width() + self.HMARGIN, 0, 0, 0)
|
|
painter.drawText(rect, Qt.AlignLeft, self.first_row_text(index))
|
|
line2 = rect.adjusted(0, rect.height() / 2, 0, rect.height() / 2)
|
|
painter.drawText(line2, Qt.AlignLeft, self.second_row_text(index))
|
|
|
|
@staticmethod
|
|
def icon_mode(option: QStyleOptionViewItem) -> QIcon.Mode:
|
|
if not (option.state & QStyle.State_Enabled):
|
|
return QIcon.Disabled
|
|
elif option.state & QStyle.State_Selected:
|
|
return QIcon.Selected
|
|
elif option.state & QStyle.State_Active:
|
|
return QIcon.Active
|
|
return QIcon.Normal
|
|
|
|
@staticmethod
|
|
def icon_state(option: QStyleOptionViewItem) -> QIcon.State:
|
|
return QIcon.On if option.state & QStyle.State_Open else QIcon.Off
|
|
|
|
@staticmethod
|
|
def icon_size(option: QStyleOptionViewItem) -> QSize:
|
|
icon_size: Optional[QSize] = option.decorationSize
|
|
if icon_size is None:
|
|
return QSize(0, 0)
|
|
else:
|
|
return icon_size
|
|
|
|
def sizeHint(self, option: QStyleOptionViewItem, index: QModelIndex) -> QSize:
|
|
left = self.icon_size(option).width() + self.HMARGIN
|
|
metrics = QFontMetrics(self.get_font(option))
|
|
first = metrics.size(0, self.first_row_text(index))
|
|
second = metrics.size(0, self.second_row_text(index))
|
|
text_width = max(first.width(), second.width())
|
|
return QSize(
|
|
left + text_width + 2 * self.HMARGIN,
|
|
first.height() + second.height() + 2 * self.VMARGIN,
|
|
)
|
|
|
|
|
|
class PendingTransfersList(QListView):
|
|
"""List view for displaying the pending unit transfers."""
|
|
|
|
def __init__(self, transfer_model: TransferModel) -> None:
|
|
super().__init__()
|
|
self.transfer_model = transfer_model
|
|
|
|
self.setItemDelegate(TransferDelegate(self.transfer_model))
|
|
self.setModel(self.transfer_model)
|
|
self.selectionModel().setCurrentIndex(
|
|
self.transfer_model.index(0, 0, QModelIndex()), QItemSelectionModel.Select
|
|
)
|
|
|
|
# self.setIconSize(QSize(91, 24))
|
|
self.setSelectionBehavior(QAbstractItemView.SelectItems)
|
|
|
|
def contextMenuEvent(self, event: QContextMenuEvent) -> None:
|
|
index = self.indexAt(event.pos())
|
|
if not index.isValid():
|
|
return
|
|
if not self.transfer_model.transfer_at_index(index).player:
|
|
return
|
|
|
|
menu = QMenu("Menu")
|
|
|
|
delete_action = QAction("Cancel")
|
|
delete_action.triggered.connect(lambda: self.cancel_transfer(index))
|
|
menu.addAction(delete_action)
|
|
|
|
menu.exec_(event.globalPos())
|
|
|
|
def cancel_transfer(self, index: QModelIndex) -> None:
|
|
"""Cancels the given transfer order."""
|
|
self.transfer_model.cancel_transfer_at_index(index)
|
|
|
|
|
|
class PendingTransfersDialog(QDialog):
|
|
"""Dialog window showing all scheduled transfers for the player."""
|
|
|
|
def __init__(self, game_model: GameModel, parent=None) -> None:
|
|
super().__init__(parent)
|
|
self.transfer_model = game_model.transfer_model
|
|
|
|
self.setMinimumSize(1000, 440)
|
|
self.setWindowTitle(f"Pending Transfers")
|
|
# TODO: self.setWindowIcon()
|
|
|
|
layout = QVBoxLayout()
|
|
self.setLayout(layout)
|
|
|
|
self.transfer_list = PendingTransfersList(self.transfer_model)
|
|
self.transfer_list.selectionModel().selectionChanged.connect(
|
|
self.on_selection_changed
|
|
)
|
|
layout.addWidget(self.transfer_list)
|
|
|
|
button_layout = QHBoxLayout()
|
|
layout.addLayout(button_layout)
|
|
|
|
button_layout.addStretch()
|
|
|
|
self.cancel_button = QPushButton("Cancel Transfer")
|
|
self.cancel_button.setProperty("style", "btn-danger")
|
|
self.cancel_button.clicked.connect(self.on_cancel_transfer)
|
|
self.cancel_button.setEnabled(
|
|
self.can_cancel(self.transfer_list.currentIndex())
|
|
)
|
|
button_layout.addWidget(self.cancel_button)
|
|
|
|
def on_cancel_transfer(self) -> None:
|
|
"""Cancels the selected transfer order."""
|
|
self.transfer_model.cancel_transfer_at_index(self.transfer_list.currentIndex())
|
|
|
|
def can_cancel(self, index: QModelIndex) -> bool:
|
|
if not index.isValid():
|
|
return False
|
|
return self.transfer_model.transfer_at_index(index).player
|
|
|
|
def on_selection_changed(
|
|
self, selected: QItemSelection, _deselected: QItemSelection
|
|
) -> None:
|
|
"""Updates the state of the delete button."""
|
|
if selected.empty():
|
|
self.cancel_button.setEnabled(False)
|
|
return
|
|
self.cancel_button.setEnabled(self.can_cancel(selected.indexes()[0]))
|