#include "ModalDialog.h" #include #include #include #include #include #include #include #include namespace { // The header's own metrics, matching the row the blueprint selection dialog drew // before this class existed (REQ-UI-BLUEPRINT-DIALOG). const int kSpacingPx = 8; const int kSmallButtonSizePx = 22; const QChar kCrossGlyph(0x00D7); // U+00D7 MULTIPLICATION SIGN // Q dismisses an open dialog, beside the Escape that QDialog already handles // (REQ-UI-DIALOG-DISMISS). Ctrl must not be held, matching how the game world's // table separates a chord from the bare key (resolveKeyAction in // lib/core/ControlAction.cpp). This deliberately stays out of that table: the table // answers what an input does in the player's current situation, and a dialog has no // situation -- it holds focus and takes the key whatever the world beneath it is // doing. bool isDismissKey(const QKeyEvent& event) { return event.key() == Qt::Key_Q && (event.modifiers() & Qt::ControlModifier) == 0; } } ModalDialog::ModalDialog(QWidget* parent) : QDialog(parent) { // An ordinary child widget rather than a window: the layer places it and the dim // behind it is the same widget's paint (REQ-UI-MODAL-CHROME). Square corners rather // than rounded ones -- rounding needs a translucent background, which is unreliable // on Windows. The border matches the build bar and the selection panel. setWindowFlags(Qt::Widget); setAttribute(Qt::WA_StyledBackground, true); // A type selector, so it reaches every subclass but none of the child widgets // inside them, which draw themselves. setStyleSheet(QStringLiteral( "ModalDialog { background-color: palette(window);" " border: 1px solid palette(mid); }")); } bool ModalDialog::isDismissible() const { return false; } QWidget* ModalDialog::getInitialFocusWidget() const { return nullptr; } void ModalDialog::requestDismiss() { reject(); } QHBoxLayout* ModalDialog::addHeader(QBoxLayout* mainLayout, const QString& title, bool withCloseButton) { QHBoxLayout* headerLayout = new QHBoxLayout(); headerLayout->setSpacing(kSpacingPx); QLabel* titleLabel = new QLabel(title, this); QFont headerFont = titleLabel->font(); headerFont.setBold(true); titleLabel->setFont(headerFont); headerLayout->addWidget(titleLabel); // The stretch is added before the close button so a subclass inserting after the // title lands left of it, where the blueprint dialog's hotkey badge belongs. headerLayout->addStretch(); if (withCloseButton) { QPushButton* closeButton = new QPushButton(QString(kCrossGlyph), this); closeButton->setFixedSize(kSmallButtonSizePx, kSmallButtonSizePx); connect(closeButton, &QPushButton::clicked, this, &QDialog::reject); headerLayout->addWidget(closeButton); } mainLayout->insertLayout(0, headerLayout); return headerLayout; } void ModalDialog::keyPressEvent(QKeyEvent* event) { if (isDismissible() && isDismissKey(*event)) { requestDismiss(); return; } QDialog::keyPressEvent(event); }