Reapply build button grid icons
Revert of "Undo build button grid icons" (614dda8): restore the per-building
SVG icons, the QtSvg-based icon loading in BuildButtonGrid, the icon dir
plumbing in MainWindow, and the Qt5::Svg dependency.
This returns the unlock-groups branch to having the icons. The preceding
undo commit exists only so the Demolish -> Deconstruct rename can be
cherry-picked on its own (rename+icons commit together with the undo).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y7N59FsLA5e2kuVdqe4Uhc
@@ -24,7 +24,7 @@ set(CMAKE_BUILD_TYPE_INIT "Release")
|
|||||||
|
|
||||||
# Qt ---------------------------------------------------------------------------
|
# Qt ---------------------------------------------------------------------------
|
||||||
|
|
||||||
find_package(Qt5 COMPONENTS Widgets Network Multimedia Charts REQUIRED)
|
find_package(Qt5 COMPONENTS Widgets Network Multimedia Charts Svg REQUIRED)
|
||||||
|
|
||||||
if(Qt5Widgets_FOUND)
|
if(Qt5Widgets_FOUND)
|
||||||
message(STATUS "Found Qt ${Qt5Widgets_VERSION_STRING}")
|
message(STATUS "Found Qt ${Qt5Widgets_VERSION_STRING}")
|
||||||
@@ -64,6 +64,7 @@ function(COPY_QT_BINARIES TARGET_DIR IS_DEBUG)
|
|||||||
configure_file("${QT_BINARY_DIR}/Qt5Network${SUFFIX}.dll" "${TARGET_DIR}/Qt5Network${SUFFIX}.dll" COPYONLY)
|
configure_file("${QT_BINARY_DIR}/Qt5Network${SUFFIX}.dll" "${TARGET_DIR}/Qt5Network${SUFFIX}.dll" COPYONLY)
|
||||||
configure_file("${QT_BINARY_DIR}/Qt5Widgets${SUFFIX}.dll" "${TARGET_DIR}/Qt5Widgets${SUFFIX}.dll" COPYONLY)
|
configure_file("${QT_BINARY_DIR}/Qt5Widgets${SUFFIX}.dll" "${TARGET_DIR}/Qt5Widgets${SUFFIX}.dll" COPYONLY)
|
||||||
configure_file("${QT_BINARY_DIR}/Qt5Multimedia${SUFFIX}.dll" "${TARGET_DIR}/Qt5Multimedia${SUFFIX}.dll" COPYONLY)
|
configure_file("${QT_BINARY_DIR}/Qt5Multimedia${SUFFIX}.dll" "${TARGET_DIR}/Qt5Multimedia${SUFFIX}.dll" COPYONLY)
|
||||||
|
configure_file("${QT_BINARY_DIR}/Qt5Svg${SUFFIX}.dll" "${TARGET_DIR}/Qt5Svg${SUFFIX}.dll" COPYONLY)
|
||||||
endfunction(COPY_QT_BINARIES)
|
endfunction(COPY_QT_BINARIES)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
6
bin/app/data/icons/buildings/assembler.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
||||||
|
<rect width="100" height="100" rx="22" fill="#3a6fa8"/>
|
||||||
|
<g transform="translate(13,13) scale(2.3125)" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<circle cx="16" cy="16" r="6.2"/><circle cx="16" cy="16" r="2.2"/><path d="M16 5.5v3M16 23.5v3M5.5 16h3M23.5 16h3M8.6 8.6 10.7 10.7M23.4 8.6 21.3 10.7M8.6 23.4 10.7 21.3M23.4 23.4 21.3 21.3"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 499 B |
6
bin/app/data/icons/buildings/belt.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
||||||
|
<rect width="100" height="100" rx="22" fill="#6a6a6a"/>
|
||||||
|
<g transform="translate(13,13) scale(2.3125)" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<rect x="4" y="10" width="24" height="12" rx="3"/><path d="M9 13 12.5 16 9 19"/><path d="M14.5 13 18 16 14.5 19"/><path d="M20 13 23.5 16 20 19"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 453 B |
6
bin/app/data/icons/buildings/deconstruct.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
||||||
|
<rect width="100" height="100" rx="22" fill="#cc3333"/>
|
||||||
|
<g transform="translate(13,13) scale(2.3125)" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M7 10h18"/><path d="M9.5 10 11 26h10l1.5-16"/><path d="M13 10V6.5h6V10"/><path d="M13.5 14v8M18.5 14v8"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 421 B |
6
bin/app/data/icons/buildings/miner.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
||||||
|
<rect width="100" height="100" rx="22" fill="#6b4a2c"/>
|
||||||
|
<g transform="translate(13,13) scale(2.3125)" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<rect x="9" y="5" width="14" height="8" rx="1.5"/><path d="M11 13 16 26 21 13"/><path d="M13 17.5h6"/><path d="M14.5 21.5h3"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 433 B |
6
bin/app/data/icons/buildings/reprocessing_plant.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
||||||
|
<rect width="100" height="100" rx="22" fill="#6a3a8a"/>
|
||||||
|
<g transform="translate(13,13) scale(2.3125)" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="18" r="4"/><circle cx="20" cy="18" r="4"/><path d="M10.6 16.6 13.4 19.4M13.4 16.6 10.6 19.4M18.6 16.6 21.4 19.4M21.4 16.6 18.6 19.4"/><path d="M16 4v8"/><path d="M13 9 16 12 19 9"/><path d="M16 24v4"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 528 B |
6
bin/app/data/icons/buildings/salvage_bay.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
||||||
|
<rect width="100" height="100" rx="22" fill="#b8a23a"/>
|
||||||
|
<g transform="translate(13,13) scale(2.3125)" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M7 13h18l-2 13H9z"/><path d="M16 3v7"/><path d="M12 7 16 11 20 7"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 383 B |
6
bin/app/data/icons/buildings/shipyard.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
||||||
|
<rect width="100" height="100" rx="22" fill="#3f6580"/>
|
||||||
|
<g transform="translate(13,13) scale(2.3125)" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M16 4c2 3 2.6 6 2.6 9.5V16h-5.2v-2.5C13.4 10 14 7 16 4z"/><circle cx="16" cy="10" r="1.3"/><path d="M13.4 15 11 18h2.4z"/><path d="M18.6 15 21 18h-2.4z"/><path d="M14.7 17 16 21 17.3 17"/><path d="M9 25h14"/><path d="M12 25v-2M20 25v-2"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 554 B |
6
bin/app/data/icons/buildings/smelter.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
||||||
|
<rect width="100" height="100" rx="22" fill="#b85a1e"/>
|
||||||
|
<g transform="translate(13,13) scale(2.3125)" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M9 9h14l-2.5 8h-9z"/><path d="M8 9h16"/><path d="M11 26c-1-1.8.4-3 .4-4 .9 1 1.6 2.2 1.6 4"/><path d="M16 26c-1-2 .4-3.4.4-4.4 .9 1 1.6 2.6 1.6 4.4"/><path d="M21 26c-1-1.8.4-3 .4-4 .9 1 1.6 2.2 1.6 4"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 519 B |
6
bin/app/data/icons/buildings/splitter.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
||||||
|
<rect width="100" height="100" rx="22" fill="#7a7a5a"/>
|
||||||
|
<g transform="translate(13,13) scale(2.3125)" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M5 16h11"/><path d="M16 8v16"/><path d="M13.5 10.5 16 8 18.5 10.5"/><path d="M13.5 21.5 16 24 18.5 21.5"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 422 B |
6
bin/app/data/icons/buildings/tunnel_entry.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
||||||
|
<rect width="100" height="100" rx="22" fill="#4f7562"/>
|
||||||
|
<g transform="translate(13,13) scale(2.3125)" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M4 9h24"/><path d="M8 9C8 24 24 24 24 9"/><path d="M14.5 18.5 17.5 20 14.5 21.5"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 398 B |
@@ -117,6 +117,7 @@ target_link_libraries(${TARGET_UI_NAME}
|
|||||||
Qt5::Network
|
Qt5::Network
|
||||||
Qt5::Multimedia
|
Qt5::Multimedia
|
||||||
Qt5::Charts
|
Qt5::Charts
|
||||||
|
Qt5::Svg
|
||||||
)
|
)
|
||||||
target_compile_definitions(${TARGET_UI_NAME} PRIVATE TOML_FLOAT_CHARCONV=0)
|
target_compile_definitions(${TARGET_UI_NAME} PRIVATE TOML_FLOAT_CHARCONV=0)
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,18 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QFile>
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPixmap>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QSignalMapper>
|
#include <QSignalMapper>
|
||||||
|
#include <QSize>
|
||||||
|
#include <QString>
|
||||||
|
#include <QSvgRenderer>
|
||||||
|
|
||||||
#include "BuildingType.h"
|
#include "BuildingType.h"
|
||||||
#include "BuildingTypeSelectedEvent.h"
|
#include "BuildingTypeSelectedEvent.h"
|
||||||
@@ -14,11 +23,60 @@
|
|||||||
#include "ExitBuilderModeRequestedEvent.h"
|
#include "ExitBuilderModeRequestedEvent.h"
|
||||||
#include "Simulation.h"
|
#include "Simulation.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Pixel size the SVG chips are rasterized at; downscaled to the button icon size.
|
||||||
|
const int kIconRenderSize = 64;
|
||||||
|
const QSize kIconSize(28, 28);
|
||||||
|
|
||||||
BuildButtonGrid::BuildButtonGrid(Simulation* sim, const GameConfig* config, QWidget* parent)
|
QPixmap renderChip(const QByteArray& svg)
|
||||||
|
{
|
||||||
|
QSvgRenderer renderer(svg);
|
||||||
|
QPixmap pixmap(kIconRenderSize, kIconRenderSize);
|
||||||
|
pixmap.fill(Qt::transparent);
|
||||||
|
QPainter painter(&pixmap);
|
||||||
|
renderer.render(&painter);
|
||||||
|
return pixmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds a build-button icon from a "<id>.svg" chip file. The returned QIcon also
|
||||||
|
// carries a Disabled-mode pixmap whose chip background is recolored grey, so an
|
||||||
|
// unaffordable (disabled) button shows the grey variant automatically
|
||||||
|
// (REQ-UI-BUILD-DISABLED) without any extra work in updateAffordability().
|
||||||
|
QIcon loadBuildingIcon(const QString& path)
|
||||||
|
{
|
||||||
|
QFile file(path);
|
||||||
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
return QIcon();
|
||||||
|
}
|
||||||
|
const QByteArray svg = file.readAll();
|
||||||
|
|
||||||
|
QIcon icon;
|
||||||
|
icon.addPixmap(renderChip(svg), QIcon::Normal);
|
||||||
|
|
||||||
|
// Recolor only the chip background: the first "#rrggbb" fill in the file is the
|
||||||
|
// rounded background rect; the white glyph uses fill="none" and is left alone.
|
||||||
|
QString greyed = QString::fromUtf8(svg);
|
||||||
|
static const QRegularExpression fillPattern(QStringLiteral("fill=\"#[0-9a-fA-F]{6}\""));
|
||||||
|
const QRegularExpressionMatch match = fillPattern.match(greyed);
|
||||||
|
if (match.hasMatch())
|
||||||
|
{
|
||||||
|
greyed.replace(match.capturedStart(), match.capturedLength(),
|
||||||
|
QStringLiteral("fill=\"#5f636e\""));
|
||||||
|
}
|
||||||
|
icon.addPixmap(renderChip(greyed.toUtf8()), QIcon::Disabled);
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BuildButtonGrid::BuildButtonGrid(Simulation* sim, const GameConfig* config,
|
||||||
|
const std::string& iconDir, QWidget* parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, m_sim(sim)
|
, m_sim(sim)
|
||||||
, m_config(config)
|
, m_config(config)
|
||||||
|
, m_iconDir(iconDir)
|
||||||
{
|
{
|
||||||
QGridLayout* layout = new QGridLayout(this);
|
QGridLayout* layout = new QGridLayout(this);
|
||||||
layout->setSpacing(4);
|
layout->setSpacing(4);
|
||||||
@@ -53,6 +111,12 @@ BuildButtonGrid::BuildButtonGrid(Simulation* sim, const GameConfig* config, QWid
|
|||||||
QPushButton* btn = new QPushButton(label, this);
|
QPushButton* btn = new QPushButton(label, this);
|
||||||
btn->setCheckable(true);
|
btn->setCheckable(true);
|
||||||
btn->setFixedHeight(48);
|
btn->setFixedHeight(48);
|
||||||
|
// Icon file name matches the building id (REQ-UI-BUILD-GRID); Tunnel Entry's
|
||||||
|
// "tunnel_entry.svg" serves the shared Tunnel button.
|
||||||
|
const QString iconPath = QString::fromStdString(m_iconDir) + "/"
|
||||||
|
+ QString::fromStdString(def.id) + ".svg";
|
||||||
|
btn->setIcon(loadBuildingIcon(iconPath));
|
||||||
|
btn->setIconSize(kIconSize);
|
||||||
if (def.tooltip)
|
if (def.tooltip)
|
||||||
{
|
{
|
||||||
btn->setToolTip(QString::fromStdString(*def.tooltip));
|
btn->setToolTip(QString::fromStdString(*def.tooltip));
|
||||||
@@ -76,6 +140,9 @@ BuildButtonGrid::BuildButtonGrid(Simulation* sim, const GameConfig* config, QWid
|
|||||||
m_deconstructButton = new QPushButton(tr("Deconstruct"), this);
|
m_deconstructButton = new QPushButton(tr("Deconstruct"), this);
|
||||||
m_deconstructButton->setCheckable(true);
|
m_deconstructButton->setCheckable(true);
|
||||||
m_deconstructButton->setFixedHeight(48);
|
m_deconstructButton->setFixedHeight(48);
|
||||||
|
m_deconstructButton->setIcon(loadBuildingIcon(
|
||||||
|
QString::fromStdString(m_iconDir) + "/deconstruct.svg"));
|
||||||
|
m_deconstructButton->setIconSize(kIconSize);
|
||||||
layout->addWidget(m_deconstructButton, row, col);
|
layout->addWidget(m_deconstructButton, row, col);
|
||||||
connect(m_deconstructButton, &QPushButton::clicked, this, [this]() {
|
connect(m_deconstructButton, &QPushButton::clicked, this, [this]() {
|
||||||
EventManager::getInstance()->sendEventImmediately(
|
EventManager::getInstance()->sendEventImmediately(
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
@@ -28,7 +29,10 @@ class BuildButtonGrid : public QWidget,
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BuildButtonGrid(Simulation* sim, const GameConfig* config, QWidget* parent = nullptr);
|
// iconDir is the directory holding the per-building "<id>.svg" chip icons
|
||||||
|
// (REQ-UI-BUILD-GRID); read from disk at runtime, like the config files.
|
||||||
|
BuildButtonGrid(Simulation* sim, const GameConfig* config,
|
||||||
|
const std::string& iconDir, QWidget* parent = nullptr);
|
||||||
~BuildButtonGrid() override;
|
~BuildButtonGrid() override;
|
||||||
|
|
||||||
void clearActiveButton();
|
void clearActiveButton();
|
||||||
@@ -55,6 +59,7 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
Simulation* m_sim;
|
Simulation* m_sim;
|
||||||
const GameConfig* m_config;
|
const GameConfig* m_config;
|
||||||
|
std::string m_iconDir;
|
||||||
std::vector<BuildingType> m_types;
|
std::vector<BuildingType> m_types;
|
||||||
std::vector<QPushButton*> m_buttons;
|
std::vector<QPushButton*> m_buttons;
|
||||||
std::map<BuildingType, int> m_costs;
|
std::map<BuildingType, int> m_costs;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCloseEvent>
|
#include <QCloseEvent>
|
||||||
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
@@ -51,8 +52,13 @@ MainWindow::MainWindow(Simulation* sim, const std::string& configDir,
|
|||||||
sideLayout->setContentsMargins(1, 1, 1, 1);
|
sideLayout->setContentsMargins(1, 1, 1, 1);
|
||||||
sideLayout->setSpacing(1);
|
sideLayout->setSpacing(1);
|
||||||
|
|
||||||
|
// Building icons live alongside the config (a sibling of the config dir), read
|
||||||
|
// from disk at runtime the same way visuals.toml is.
|
||||||
|
const std::string iconDir = QDir::cleanPath(
|
||||||
|
QString::fromStdString(m_configDir) + "/../icons/buildings").toStdString();
|
||||||
|
|
||||||
m_selectedBuildingPanel = new SelectedBuildingPanel(sim, &sim->getConfig(), m_sidePanel);
|
m_selectedBuildingPanel = new SelectedBuildingPanel(sim, &sim->getConfig(), m_sidePanel);
|
||||||
m_buildButtonGrid = new BuildButtonGrid(sim, &sim->getConfig(), m_sidePanel);
|
m_buildButtonGrid = new BuildButtonGrid(sim, &sim->getConfig(), iconDir, m_sidePanel);
|
||||||
m_blueprintPanel = new BlueprintPanel(sim, &sim->getConfig(), m_sidePanel);
|
m_blueprintPanel = new BlueprintPanel(sim, &sim->getConfig(), m_sidePanel);
|
||||||
|
|
||||||
sideLayout->addWidget(m_selectedBuildingPanel, 1);
|
sideLayout->addWidget(m_selectedBuildingPanel, 1);
|
||||||
|
|||||||