#include "HeaderBar.h" #include #include #include #include #include #include #include #include #include #include #include "Command.h" #include "CommandRequestedEvent.h" #include "EventManager.h" #include "IconCaption.h" #include "ItemIconCache.h" #include "Simulation.h" #include "SpeedChangeRequestedEvent.h" #include "Tick.h" namespace { // Item id of the building blocks resource, whose icon stands in for the "Blocks" // word in the header stock and expand button (REQ-UI-BLOCKS-ICON). const char* const kBlockItemId = "building_block"; } const double HeaderBar::kSpeeds[] = { 0.0, 0.5, 1.0, 2.0, 10.0 }; const int HeaderBar::kSpeedCount = 5; HeaderBar::HeaderBar(const Simulation* sim, const GameConfig* config, ItemIconCache* itemIcons, QWidget* parent) : QWidget(parent) , m_itemIcons(itemIcons) , m_sim(sim) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setContentsMargins(8, 4, 8, 4); layout->setSpacing(8); m_timeLabel = new QLabel("00:00", this); m_blocksLabel = new QLabel(this); if (config->world.buildingBlocksTooltip) { m_blocksLabel->setToolTip( QString::fromStdString(*config->world.buildingBlocksTooltip)); } updateBlocksLabel(); m_artifactsLabel = new QLabel(tr("Artifacts: 0/?"), this); if (config->world.artifactTooltip) { m_artifactsLabel->setToolTip( QString::fromStdString(*config->world.artifactTooltip)); } m_bossWaveLabel = new QLabel(tr("Boss Wave #1"), this); m_nextBossLabel = new QLabel(tr("Next boss: 5:00"), this); // Asteroid expansion button, to the left of the speed buttons (REQ-UI-HEADER, // REQ-UI-EXPAND-BUTTON). Caption/enabled state are set on the first // ExpansionCostChangedEvent; clicking requests an ExpandAsteroidCommand. m_expandButton = new QPushButton(tr("Expand"), this); connect(m_expandButton, &QPushButton::clicked, this, []() { EventManager::getInstance()->sendEventImmediately( std::make_shared( std::make_shared())); }); const int itemSpacing = 20; layout->addWidget(m_timeLabel); layout->addSpacing(itemSpacing); layout->addWidget(m_blocksLabel); layout->addSpacing(itemSpacing); layout->addWidget(m_artifactsLabel); layout->addStretch(); layout->addWidget(m_bossWaveLabel); layout->addSpacing(itemSpacing); layout->addWidget(m_nextBossLabel); layout->addSpacing(itemSpacing); layout->addWidget(m_expandButton); layout->addSpacing(itemSpacing); const char* labels[] = { "0x", "0.5x", "1x", "2x", "10x" }; QSignalMapper* mapper = new QSignalMapper(this); for (int i = 0; i < kSpeedCount; ++i) { QPushButton* btn = new QPushButton(labels[i], this); btn->setCheckable(true); btn->setChecked(i == 2); layout->addWidget(btn); m_speedButtons.push_back(btn); mapper->setMapping(btn, i); connect(btn, &QPushButton::clicked, mapper, qOverload<>(&QSignalMapper::map)); } connect(mapper, qOverload(&QSignalMapper::mapped), this, &HeaderBar::onSpeedButton); setFixedHeight(sizeHint().height()); registerForEvents(); } HeaderBar::~HeaderBar() { unregisterForEvents(); } void HeaderBar::handleEvent(std::shared_ptr /*event*/) { const int totalSeconds = static_cast(ticksToSeconds(m_sim->getCurrentTick())); m_timeLabel->setText( QString("%1:%2") .arg(totalSeconds / 60, 2, 10, QChar('0')) .arg(totalSeconds % 60, 2, 10, QChar('0'))); } void HeaderBar::handleEvent(std::shared_ptr /*event*/) { updateBlocksLabel(); updateExpandButton(); } void HeaderBar::handleEvent(std::shared_ptr /*event*/) { updateExpandButton(); } QPixmap HeaderBar::blockIcon() const { if (!m_itemIcons->hasIcon(kBlockItemId)) { return QPixmap(); } // Sized to the header text height so it sits inline with the caption. const int sizePx = QFontMetrics(font()).height(); return m_itemIcons->getPixmap(kBlockItemId, sizePx); } void HeaderBar::updateBlocksLabel() { const int blocks = m_sim->getBuildingBlocksStock(); const QPixmap icon = blockIcon(); if (icon.isNull()) { // Fallback text form when no building_block icon exists (REQ-UI-BLOCKS-ICON). m_blocksLabel->setText(tr("Stock: %1 Blocks").arg(blocks)); return; } m_blocksLabel->setPixmap(renderCaptionWithIcon( tr("Stock: %1").arg(blocks), icon, font(), m_blocksLabel->palette().color(QPalette::WindowText))); } void HeaderBar::updateExpandButton() { const int blocks = m_sim->getBuildingBlocksStock(); const int expansionCost = m_sim->getCurrentExpansionCost(); m_expandButton->setEnabled(blocks >= expansionCost); const QPixmap icon = blockIcon(); if (icon.isNull()) { // Fallback text form when no building_block icon exists (REQ-UI-EXPAND-BUTTON). m_expandButton->setIcon(QIcon()); m_expandButton->setText(tr("Expand: %1 Blocks").arg(expansionCost)); return; } const QString text = tr("Expand: %1").arg(expansionCost); const QPalette& pal = m_expandButton->palette(); const QPixmap normal = renderCaptionWithIcon( text, icon, m_expandButton->font(), pal.color(QPalette::ButtonText)); const QPixmap greyed = renderCaptionWithIcon( text, icon, m_expandButton->font(), pal.color(QPalette::Disabled, QPalette::ButtonText)); QIcon buttonIcon; buttonIcon.addPixmap(normal, QIcon::Normal); buttonIcon.addPixmap(greyed, QIcon::Disabled); m_expandButton->setText(QString()); m_expandButton->setIcon(buttonIcon); const qreal dpr = normal.devicePixelRatio(); m_expandButton->setIconSize(QSize( static_cast(normal.width() / dpr), static_cast(normal.height() / dpr))); } void HeaderBar::handleEvent(std::shared_ptr event) { for (int i = 0; i < kSpeedCount; ++i) { m_speedButtons[static_cast(i)]->setChecked( std::abs(kSpeeds[i] - event->speed) < 0.001); } } void HeaderBar::handleEvent(std::shared_ptr /*event*/) { const Tick countdownTicks = m_sim->getBossCountdownTicks(); const int bossSeconds = static_cast( ticksToSeconds(countdownTicks > 0 ? countdownTicks : 0)); m_bossWaveLabel->setText( tr("Boss Wave #%1") .arg(m_sim->getBossWaveCounter())); m_nextBossLabel->setText( tr("Next boss: %1:%2") .arg(bossSeconds / 60) .arg(bossSeconds % 60, 2, 10, QChar('0'))); } void HeaderBar::handleEvent(std::shared_ptr /*event*/) { m_artifactsLabel->setText( tr("Artifacts: %1/%2") .arg(m_sim->getArtifactCount()) .arg(m_sim->getConfig().world.artifacts.artifactWinCount)); } void HeaderBar::onSpeedButton(int index) { if (index >= 0 && index < kSpeedCount) { EventManager::getInstance()->sendEventImmediately( std::make_shared(kSpeeds[index])); } }