The button leaves the header bar for the world, centered on the columns the next purchase unlocks and panning with them until it leaves the view. Those columns now take a lighter tint than the locked ground behind them, so the boundary between the two says how far one purchase reaches. Nothing in the code reported a scroll: the camera knew it had moved and told only the hover. A ViewScrolledEvent says it now, which is what a widget keeping a place in the world rather than on the screen needs. The panels want none of it -- they are placed against the screen and stay put as the world moves under them -- so the button is placed after their pass and never joins the rectangles they step around. Its face is two lines, "Expand" over the cost. That makes the cost a display of its own, so it carries the building-block tooltip the way every other item value does, on hover alone since the click buys the expansion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
161 lines
5.2 KiB
C++
161 lines
5.2 KiB
C++
#include "HeaderBar.h"
|
|
|
|
#include <cmath>
|
|
#include <string>
|
|
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
#include <QPalette>
|
|
#include <QPushButton>
|
|
#include <QSignalMapper>
|
|
|
|
#include "EventManager.h"
|
|
#include "IconCaption.h"
|
|
#include "ItemIconCache.h"
|
|
#include "ItemTooltip.h"
|
|
#include "Simulation.h"
|
|
#include "SpeedChangeRequestedEvent.h"
|
|
#include "Tick.h"
|
|
#include "TooltipTrigger.h"
|
|
|
|
const double HeaderBar::kSpeeds[] = { 0.0, 0.5, 1.0, 2.0, 10.0 };
|
|
const int HeaderBar::kSpeedCount = 5;
|
|
|
|
HeaderBar::HeaderBar(const ItemTooltipContext& context, QWidget* parent)
|
|
: QWidget(parent)
|
|
, m_context(context)
|
|
{
|
|
QHBoxLayout* layout = new QHBoxLayout(this);
|
|
layout->setContentsMargins(8, 4, 8, 4);
|
|
layout->setSpacing(8);
|
|
|
|
m_timeLabel = new QLabel("00:00", this);
|
|
// Both displays state a value and do nothing when clicked, so a click brings the
|
|
// tooltip up at once rather than waiting the hover out (REQ-UI-TOOLTIP-TRIGGER).
|
|
// The stock states an amount of an item, so what it shows is that item's production
|
|
// tooltip, as every other display naming an item does
|
|
// (REQ-UI-BLOCKS-ICON, REQ-UI-ITEM-VALUE-TOOLTIP).
|
|
m_blocksLabel = new QLabel(this);
|
|
ItemTooltip::attachTo(*m_blocksLabel, m_context, kBlockItemId,
|
|
TooltipTrigger::Trigger::HoverAndClick);
|
|
updateBlocksLabel();
|
|
// An artifact count is no item amount, so it has no production path to show and
|
|
// states configured text instead (REQ-UI-ARTIFACTS-TOOLTIP).
|
|
m_artifactsLabel = new QLabel(tr("Artifacts: 0/?"), this);
|
|
if (m_context.config->world.artifactTooltip)
|
|
{
|
|
TooltipTrigger::attachText(
|
|
*m_artifactsLabel,
|
|
QString::fromStdString(*m_context.config->world.artifactTooltip),
|
|
TooltipTrigger::Trigger::HoverAndClick);
|
|
}
|
|
m_bossWaveLabel = new QLabel(tr("Boss Wave #1"), this);
|
|
m_nextBossLabel = new QLabel(tr("Next boss: 5:00"), this);
|
|
|
|
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);
|
|
|
|
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<int>(&QSignalMapper::mapped), this, &HeaderBar::onSpeedButton);
|
|
|
|
setFixedHeight(sizeHint().height());
|
|
|
|
registerForEvents();
|
|
}
|
|
|
|
HeaderBar::~HeaderBar()
|
|
{
|
|
unregisterForEvents();
|
|
}
|
|
|
|
void HeaderBar::handleEvent(std::shared_ptr<const TickAdvancedEvent> /*event*/)
|
|
{
|
|
const int totalSeconds = static_cast<int>(ticksToSeconds(m_context.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<const BuildingBlocksChangedEvent> /*event*/)
|
|
{
|
|
updateBlocksLabel();
|
|
}
|
|
|
|
void HeaderBar::updateBlocksLabel()
|
|
{
|
|
const int blocks = m_context.sim->getBuildingBlocksStock();
|
|
|
|
const QPixmap icon = m_context.itemIcons->getInlineIcon(kBlockItemId, font());
|
|
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::handleEvent(std::shared_ptr<const GameSpeedChangedEvent> event)
|
|
{
|
|
for (int i = 0; i < kSpeedCount; ++i)
|
|
{
|
|
m_speedButtons[static_cast<std::size_t>(i)]->setChecked(
|
|
std::abs(kSpeeds[i] - event->speed) < 0.001);
|
|
}
|
|
}
|
|
|
|
void HeaderBar::handleEvent(std::shared_ptr<const BossWaveUpdatedEvent> /*event*/)
|
|
{
|
|
const Tick countdownTicks = m_context.sim->getBossCountdownTicks();
|
|
const int bossSeconds = static_cast<int>(
|
|
ticksToSeconds(countdownTicks > 0 ? countdownTicks : 0));
|
|
m_bossWaveLabel->setText(
|
|
tr("Boss Wave #%1")
|
|
.arg(m_context.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<const ArtifactCountChangedEvent> /*event*/)
|
|
{
|
|
m_artifactsLabel->setText(
|
|
tr("Artifacts: %1/%2")
|
|
.arg(m_context.sim->getArtifactCount())
|
|
.arg(m_context.sim->getConfig().world.artifacts.artifactWinCount));
|
|
}
|
|
|
|
void HeaderBar::onSpeedButton(int index)
|
|
{
|
|
if (index >= 0 && index < kSpeedCount)
|
|
{
|
|
EventManager::getInstance()->sendEventImmediately(
|
|
std::make_shared<SpeedChangeRequestedEvent>(kSpeeds[index]));
|
|
}
|
|
}
|