draw item icons on their colored square in the UI
Item icons were drawn bare in the panels and dialogs while only the game world composed them onto the item's visuals.toml square, so the same item read differently on a belt than in a panel. Move the composition into ItemIconCache, which now holds the visuals and offers it in two forms: paintItem() for the world's fractional geometry and getSquarePixmap() for widgets. WorldRenderer::drawWorldItem collapses onto the former, and the recipe-selection dialog, the selection panel's item chips and the recipe summary onto the latter. The inset fraction that shapes the composition now has one definition instead of two. An item with no icon file now shows its square alone rather than falling back to text, so callers gate on the composed pixmap being null -- the case of an item with neither square nor icon -- instead of on hasIcon(). The inline building_block icon beside a number keeps using the bare getPixmap() and takes no square. The chip and summary squares are drawn a few pixels larger than the icons were, so the artwork keeps its size once the square insets it. Reloading the visuals on a restart drops the composed pixmaps, which carry the colors they were painted with (REQ-CFG-RELOAD). REQ-UI-ITEM-ICON, REQ-UI-RECIPE-ICON, REQ-UI-SINGLE-SELECTION, REQ-UI-RECIPE-SUMMARY, REQ-UI-HQ-PANEL Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ne3mejABZoLWKLh8fgpM3x
This commit is contained in:
@@ -2,10 +2,26 @@
|
|||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
#include <QRectF>
|
||||||
#include <QSvgRenderer>
|
#include <QSvgRenderer>
|
||||||
|
|
||||||
ItemIconCache::ItemIconCache(const QString& iconDir)
|
#include "VisualsConfig.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
// How far the icon is inset within the item's colored square, as a fraction of the
|
||||||
|
// square's size on each side (REQ-UI-ITEM-ICON). The inset is what keeps a frame of the
|
||||||
|
// square's color visible all around the icon: each icon's viewBox is cropped tight to
|
||||||
|
// its artwork, so an icon drawn at the full rect would cover the square entirely.
|
||||||
|
const double kIconInsetFraction = 0.15;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
ItemIconCache::ItemIconCache(const QString& iconDir, const VisualsConfig* visuals)
|
||||||
: m_iconDir(iconDir)
|
: m_iconDir(iconDir)
|
||||||
|
, m_visuals(visuals)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,14 +50,59 @@ bool ItemIconCache::hasIcon(const std::string& itemId)
|
|||||||
return !getSvg(itemId).isEmpty();
|
return !getSvg(itemId).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ItemIconCache::paintItem(QPainter& painter, const QRectF& rect,
|
||||||
|
const std::string& itemId)
|
||||||
|
{
|
||||||
|
// The colored square from visuals.toml backs every item, icon or not: it is what
|
||||||
|
// gives the item contrast against the tile beneath it in the world, and its outline
|
||||||
|
// is what separates neighbouring items where they overlap on a belt (REQ-GW-TILE-SIZE,
|
||||||
|
// REQ-UI-ITEM-ICON).
|
||||||
|
if (m_visuals != nullptr)
|
||||||
|
{
|
||||||
|
const std::map<std::string, ItemVisuals>::const_iterator it =
|
||||||
|
m_visuals->items.find(itemId);
|
||||||
|
if (it != m_visuals->items.end())
|
||||||
|
{
|
||||||
|
painter.fillRect(rect, it->second.fill);
|
||||||
|
painter.setPen(QPen(it->second.outline, 1));
|
||||||
|
painter.setBrush(Qt::NoBrush);
|
||||||
|
painter.drawRect(rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasIcon(itemId)) { return; }
|
||||||
|
|
||||||
|
// The icon goes on top, inset so a frame of the square's color stays visible all
|
||||||
|
// around it. It is rasterized once at the inset pixel size and cached, so this is a
|
||||||
|
// plain pixmap blit per frame.
|
||||||
|
const double inset = kIconInsetFraction * rect.width();
|
||||||
|
const QRectF iconRect = rect.adjusted(inset, inset, -inset, -inset);
|
||||||
|
|
||||||
|
int sizePx = qRound(iconRect.width());
|
||||||
|
if (sizePx < 1) { sizePx = 1; }
|
||||||
|
painter.drawPixmap(iconRect, getPixmap(itemId, sizePx),
|
||||||
|
QRectF(0, 0, sizePx, sizePx));
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap ItemIconCache::getSquarePixmap(const std::string& itemId, int sizePx)
|
||||||
|
{
|
||||||
|
return getPixmap("square:" + itemId, itemId, sizePx, true);
|
||||||
|
}
|
||||||
|
|
||||||
QPixmap ItemIconCache::getPixmap(const std::string& itemId, int sizePx)
|
QPixmap ItemIconCache::getPixmap(const std::string& itemId, int sizePx)
|
||||||
|
{
|
||||||
|
return getPixmap(itemId, itemId, sizePx, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap ItemIconCache::getPixmap(const std::string& cacheKey, const std::string& itemId,
|
||||||
|
int sizePx, bool withSquare)
|
||||||
{
|
{
|
||||||
if (sizePx <= 0)
|
if (sizePx <= 0)
|
||||||
{
|
{
|
||||||
return QPixmap();
|
return QPixmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::pair<std::string, int> key(itemId, sizePx);
|
const std::pair<std::string, int> key(cacheKey, sizePx);
|
||||||
const std::map<std::pair<std::string, int>, QPixmap>::const_iterator cached =
|
const std::map<std::pair<std::string, int>, QPixmap>::const_iterator cached =
|
||||||
m_pixmapCache.find(key);
|
m_pixmapCache.find(key);
|
||||||
if (cached != m_pixmapCache.end())
|
if (cached != m_pixmapCache.end())
|
||||||
@@ -49,8 +110,31 @@ QPixmap ItemIconCache::getPixmap(const std::string& itemId, int sizePx)
|
|||||||
return cached->second;
|
return cached->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QByteArray& svg = getSvg(itemId);
|
|
||||||
QPixmap pixmap;
|
QPixmap pixmap;
|
||||||
|
if (withSquare)
|
||||||
|
{
|
||||||
|
// Null unless the item has something to draw -- a square, an icon, or both --
|
||||||
|
// so a caller with nothing to show can fall back to text (REQ-UI-RECIPE-ICON).
|
||||||
|
const bool hasSquare = m_visuals != nullptr
|
||||||
|
&& m_visuals->items.find(itemId) != m_visuals->items.end();
|
||||||
|
if (hasSquare || hasIcon(itemId))
|
||||||
|
{
|
||||||
|
pixmap = QPixmap(sizePx, sizePx);
|
||||||
|
pixmap.fill(Qt::transparent);
|
||||||
|
QPainter painter(&pixmap);
|
||||||
|
// No antialiasing: the square's edges are axis-aligned and land on pixel
|
||||||
|
// boundaries, and smoothing a 1-pixel outline only blurs it. The icon is
|
||||||
|
// blitted into a slightly smaller rect than it was rasterized at, which is
|
||||||
|
// what the smooth transform is for.
|
||||||
|
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
|
||||||
|
// One pixel short of the pixmap so the square's right and bottom edges land
|
||||||
|
// inside it rather than on its border.
|
||||||
|
paintItem(painter, QRectF(0, 0, sizePx - 1, sizePx - 1), itemId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const QByteArray& svg = getSvg(itemId);
|
||||||
if (!svg.isEmpty())
|
if (!svg.isEmpty())
|
||||||
{
|
{
|
||||||
QSvgRenderer renderer(svg);
|
QSvgRenderer renderer(svg);
|
||||||
@@ -60,5 +144,11 @@ QPixmap ItemIconCache::getPixmap(const std::string& itemId, int sizePx)
|
|||||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||||
renderer.render(&painter);
|
renderer.render(&painter);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return m_pixmapCache.emplace(key, std::move(pixmap)).first->second;
|
return m_pixmapCache.emplace(key, std::move(pixmap)).first->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ItemIconCache::clearPixmapCache()
|
||||||
|
{
|
||||||
|
m_pixmapCache.clear();
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,38 +8,76 @@
|
|||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
// Rasterizes and caches per-item icon SVGs (REQ-UI-ITEM-ICON). Item icons are
|
class QPainter;
|
||||||
// self-contained, full-color SVGs loaded from a directory, one file per item type
|
class QRectF;
|
||||||
// named after the item's id (e.g. "iron_ore.svg"). Shared by the recipe-selection
|
struct VisualsConfig;
|
||||||
// dialog (REQ-UI-RECIPE-ICON) and the game world's belt/port item rendering so the
|
|
||||||
// rasterization is not duplicated.
|
// Rasterizes and caches per-item icon SVGs, and composes them onto the item's colored
|
||||||
|
// square (REQ-UI-ITEM-ICON). Item icons are self-contained, full-color SVGs loaded from
|
||||||
|
// a directory, one file per item type named after the item's id (e.g. "iron_ore.svg").
|
||||||
//
|
//
|
||||||
// A missing icon file is not an error: hasIcon() returns false for it and the caller
|
// The square is the item's `fill` and `outline` from visuals.toml with the icon inset
|
||||||
// falls back (a colored square in the world, a name caption in the dialog).
|
// within it, and it backs the icon both in the game world and wherever the UI displays
|
||||||
|
// an item as an item: the recipe-selection dialog's option buttons (REQ-UI-RECIPE-ICON),
|
||||||
|
// the selection panel's item chips (REQ-UI-SINGLE-SELECTION, REQ-UI-HQ-PANEL), and the
|
||||||
|
// recipe summary (REQ-UI-RECIPE-SUMMARY). Defined here once rather than per widget, in
|
||||||
|
// two forms: paintItem() for the world's fractional geometry, getSquarePixmap() for
|
||||||
|
// widgets that want a ready-made pixmap.
|
||||||
|
//
|
||||||
|
// The bare icon of getPixmap() has one remaining use: the inline building_block icon
|
||||||
|
// that stands in for the word "Blocks" beside a number (REQ-UI-BLOCKS-ICON), which is a
|
||||||
|
// decoration on a line of text rather than an item display and takes no square.
|
||||||
|
//
|
||||||
|
// A missing icon file is not an error: hasIcon() returns false for it and the item shows
|
||||||
|
// its colored square alone.
|
||||||
class ItemIconCache
|
class ItemIconCache
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// iconDir is the directory holding the "<item_id>.svg" icon files
|
// iconDir is the directory holding the "<item_id>.svg" icon files (typically
|
||||||
// (typically "<configDir>/../icons/items").
|
// "<configDir>/../icons/items"). visuals supplies the per-item square colors and
|
||||||
explicit ItemIconCache(const QString& iconDir);
|
// must outlive the cache; its contents may be replaced on a restart (REQ-CFG-RELOAD),
|
||||||
|
// which is what clearPixmapCache() is for.
|
||||||
|
ItemIconCache(const QString& iconDir, const VisualsConfig* visuals);
|
||||||
|
|
||||||
// True if an icon SVG file exists for the given item id. Loads the file's bytes
|
// True if an icon SVG file exists for the given item id. Loads the file's bytes
|
||||||
// on first query and remembers the result (including absence) so repeated calls
|
// on first query and remembers the result (including absence) so repeated calls
|
||||||
// are cheap.
|
// are cheap.
|
||||||
bool hasIcon(const std::string& itemId);
|
bool hasIcon(const std::string& itemId);
|
||||||
|
|
||||||
// Returns the item's icon rasterized to a transparent sizePx*sizePx pixmap,
|
// Paints the item's colored square into rect and its icon inset within it
|
||||||
// cached per (item id, size) so it is rendered once and reused across frames and
|
// (REQ-UI-ITEM-ICON). Takes the rect as a QRectF so the world can keep painting at
|
||||||
// only re-rasterized when the target size changes (REQ-UI-ITEM-ICON). Returns a
|
// sub-pixel geometry. An item with no visuals entry gets no square, one with no icon
|
||||||
// null pixmap if the item has no icon file (callers should gate on hasIcon()).
|
// file no icon; with neither, this paints nothing.
|
||||||
|
void paintItem(QPainter& painter, const QRectF& rect, const std::string& itemId);
|
||||||
|
|
||||||
|
// The same composition rasterized to a sizePx*sizePx pixmap for widget use, cached
|
||||||
|
// per (item id, size) so it is rendered once and reused. Returns a null pixmap only
|
||||||
|
// when the item has neither a visuals entry nor an icon file, which is the one case
|
||||||
|
// in which a caller has nothing to show and falls back to text.
|
||||||
|
QPixmap getSquarePixmap(const std::string& itemId, int sizePx);
|
||||||
|
|
||||||
|
// Returns the item's icon alone, without its square, rasterized to a transparent
|
||||||
|
// sizePx*sizePx pixmap and cached per (item id, size) so it is rendered once and
|
||||||
|
// reused across frames (REQ-UI-ITEM-ICON). Returns a null pixmap if the item has no
|
||||||
|
// icon file (callers should gate on hasIcon()).
|
||||||
QPixmap getPixmap(const std::string& itemId, int sizePx);
|
QPixmap getPixmap(const std::string& itemId, int sizePx);
|
||||||
|
|
||||||
|
// Drops every rasterized pixmap. Called when the visuals are reloaded on a restart
|
||||||
|
// (REQ-CFG-RELOAD), because the composed squares carry the colors they were painted
|
||||||
|
// with; they are re-rasterized on next use.
|
||||||
|
void clearPixmapCache();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Returns the raw SVG bytes for an item id, loading and caching them on first
|
// Returns the raw SVG bytes for an item id, loading and caching them on first
|
||||||
// access. An absent file caches an empty QByteArray so it is not retried.
|
// access. An absent file caches an empty QByteArray so it is not retried.
|
||||||
const QByteArray& getSvg(const std::string& itemId);
|
const QByteArray& getSvg(const std::string& itemId);
|
||||||
|
// Shared rasterize-and-cache step. cacheKey distinguishes the bare and squared
|
||||||
|
// variants of one item within the single pixmap cache.
|
||||||
|
QPixmap getPixmap(const std::string& cacheKey, const std::string& itemId,
|
||||||
|
int sizePx, bool withSquare);
|
||||||
|
|
||||||
QString m_iconDir;
|
QString m_iconDir;
|
||||||
|
const VisualsConfig* m_visuals; // Not owned; lives in MainWindow.
|
||||||
std::map<std::string, QByteArray> m_svgById;
|
std::map<std::string, QByteArray> m_svgById;
|
||||||
std::map<std::pair<std::string, int>, QPixmap> m_pixmapCache;
|
std::map<std::pair<std::string, int>, QPixmap> m_pixmapCache;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ MainWindow::MainWindow(Simulation* sim, const std::string& configDir,
|
|||||||
// REQ-UI-BUILD-ICON).
|
// REQ-UI-BUILD-ICON).
|
||||||
const QString configDirPath = QString::fromStdString(m_configDir);
|
const QString configDirPath = QString::fromStdString(m_configDir);
|
||||||
m_itemIcons = std::make_unique<ItemIconCache>(
|
m_itemIcons = std::make_unique<ItemIconCache>(
|
||||||
QDir::cleanPath(configDirPath + "/../icons/items"));
|
QDir::cleanPath(configDirPath + "/../icons/items"), &m_visuals);
|
||||||
m_buildingIcons = std::make_unique<BuildingIconCache>(
|
m_buildingIcons = std::make_unique<BuildingIconCache>(
|
||||||
QDir::cleanPath(configDirPath + "/../icons/buildings"));
|
QDir::cleanPath(configDirPath + "/../icons/buildings"));
|
||||||
|
|
||||||
@@ -283,6 +283,9 @@ std::optional<GameConfig> MainWindow::reloadConfig()
|
|||||||
VisualsConfig newVisuals = VisualsLoader::load(m_configDir + "/visuals.toml");
|
VisualsConfig newVisuals = VisualsLoader::load(m_configDir + "/visuals.toml");
|
||||||
m_visuals = std::move(newVisuals);
|
m_visuals = std::move(newVisuals);
|
||||||
m_dimOverlay->setDimColor(m_visuals.overlays.modalDim);
|
m_dimOverlay->setDimColor(m_visuals.overlays.modalDim);
|
||||||
|
// The composed item squares carry the colors they were painted with, so they
|
||||||
|
// are dropped for the new ones to take effect (REQ-UI-ITEM-ICON).
|
||||||
|
m_itemIcons->clearPixmapCache();
|
||||||
return newConfig;
|
return newConfig;
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
|
|||||||
@@ -127,12 +127,17 @@ RecipeSelectionDialog::RecipeSelectionDialog(
|
|||||||
const RecipeSelectionOption& option = options[static_cast<std::size_t>(i)];
|
const RecipeSelectionOption& option = options[static_cast<std::size_t>(i)];
|
||||||
|
|
||||||
QPushButton* button = new QPushButton(this);
|
QPushButton* button = new QPushButton(this);
|
||||||
// Icon-only when the produced item has an icon (REQ-UI-RECIPE-ICON); otherwise
|
// Icon-only for a recipe option: the produced item's icon on its colored square,
|
||||||
// fall back to the caption. The name stays reachable via the tooltip.
|
// or the square alone when the item has no icon file (REQ-UI-RECIPE-ICON). The
|
||||||
if (!option.iconItemId.empty() && itemIcons->hasIcon(option.iconItemId))
|
// name stays reachable via the tooltip. Options carrying no item at all -- the
|
||||||
|
// "(None)" entry and the Shipyard's schematics -- keep their text caption, as
|
||||||
|
// does an item with neither square nor icon to show.
|
||||||
|
const QPixmap icon = option.iconItemId.empty()
|
||||||
|
? QPixmap()
|
||||||
|
: itemIcons->getSquarePixmap(option.iconItemId, kOptionIconSize.width());
|
||||||
|
if (!icon.isNull())
|
||||||
{
|
{
|
||||||
button->setIcon(QIcon(itemIcons->getPixmap(
|
button->setIcon(QIcon(icon));
|
||||||
option.iconItemId, kOptionIconSize.width())));
|
|
||||||
button->setIconSize(kOptionIconSize);
|
button->setIconSize(kOptionIconSize);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -541,37 +541,14 @@ void WorldRenderer::drawPortItems(QPainter& painter, const WorldCoordinates& coo
|
|||||||
void WorldRenderer::drawWorldItem(QPainter& painter, const std::string& itemId,
|
void WorldRenderer::drawWorldItem(QPainter& painter, const std::string& itemId,
|
||||||
QPointF center, float halfPx)
|
QPointF center, float halfPx)
|
||||||
{
|
{
|
||||||
|
if (!m_itemIcons) { return; }
|
||||||
|
|
||||||
|
// The colored square carrying the item's icon (REQ-GW-TILE-SIZE, REQ-UI-ITEM-ICON).
|
||||||
|
// The composition lives in the icon cache, which draws it the same way here and in
|
||||||
|
// the UI's item displays, so an item reads the same on a belt as in a panel.
|
||||||
const QRectF itemRect(center.x() - halfPx, center.y() - halfPx,
|
const QRectF itemRect(center.x() - halfPx, center.y() - halfPx,
|
||||||
halfPx * 2, halfPx * 2);
|
halfPx * 2, halfPx * 2);
|
||||||
|
m_itemIcons->paintItem(painter, itemRect, itemId);
|
||||||
// The colored square from visuals.toml (REQ-GW-TILE-SIZE) backs every item, icon or
|
|
||||||
// not: it is what gives the item contrast against the tile beneath it, and its dark
|
|
||||||
// outline is what separates neighbouring items where they overlap on a belt.
|
|
||||||
const std::map<std::string, ItemVisuals>::const_iterator it =
|
|
||||||
m_visuals.items.find(itemId);
|
|
||||||
if (it != m_visuals.items.end())
|
|
||||||
{
|
|
||||||
painter.fillRect(itemRect, it->second.fill);
|
|
||||||
painter.setPen(QPen(it->second.outline, 1));
|
|
||||||
painter.setBrush(Qt::NoBrush);
|
|
||||||
painter.drawRect(itemRect);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_itemIcons || !m_itemIcons->hasIcon(itemId)) { return; }
|
|
||||||
|
|
||||||
// The icon goes on top, inset so a frame of the square's color stays visible all
|
|
||||||
// around it (REQ-UI-ITEM-ICON). The inset is needed because each icon's viewBox is
|
|
||||||
// cropped tight to its artwork: drawn at the full rect, a solid icon would cover the
|
|
||||||
// square entirely. It is rasterized once at the inset pixel size and cached, so this
|
|
||||||
// is a plain pixmap blit per frame.
|
|
||||||
constexpr double kIconInsetFraction = 0.15;
|
|
||||||
const double inset = kIconInsetFraction * static_cast<double>(halfPx * 2.0f);
|
|
||||||
const QRectF iconRect = itemRect.adjusted(inset, inset, -inset, -inset);
|
|
||||||
|
|
||||||
int sizePx = qRound(iconRect.width());
|
|
||||||
if (sizePx < 1) { sizePx = 1; }
|
|
||||||
painter.drawPixmap(iconRect, m_itemIcons->getPixmap(itemId, sizePx),
|
|
||||||
QRectF(0, 0, sizePx, sizePx));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::drawBeltItems(QPainter& painter, const WorldCoordinates& coordinates,
|
void WorldRenderer::drawBeltItems(QPainter& painter, const WorldCoordinates& coordinates,
|
||||||
|
|||||||
@@ -8,8 +8,9 @@
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
// Size the item icon is drawn at inside a chip, in device-independent pixels.
|
// Size the item's colored square is drawn at inside a chip, in device-independent
|
||||||
const int kChipIconSizePx = 18;
|
// pixels. Larger than the artwork it carries, which the square insets (REQ-UI-ITEM-ICON).
|
||||||
|
const int kChipIconSizePx = 22;
|
||||||
|
|
||||||
// Chips per row. Two fit the panel's capped width side by side; a third would force the
|
// Chips per row. Two fit the panel's capped width side by side; a third would force the
|
||||||
// counts to shrink.
|
// counts to shrink.
|
||||||
@@ -64,11 +65,10 @@ void ItemChipRow::rebuildChips(const std::vector<Entry>& entries)
|
|||||||
for (std::size_t index = 0; index < entries.size(); ++index)
|
for (std::size_t index = 0; index < entries.size(); ++index)
|
||||||
{
|
{
|
||||||
const std::string& itemId = entries[index].itemId;
|
const std::string& itemId = entries[index].itemId;
|
||||||
// A missing icon file is not an error (REQ-UI-ITEM-ICON): the chip is then laid
|
// The item's icon on its colored square (REQ-UI-ITEM-ICON). A missing icon file
|
||||||
// out around its count alone.
|
// is not an error: the chip then carries the square alone, and only an item with
|
||||||
const QPixmap icon = m_itemIcons->hasIcon(itemId)
|
// no square either leaves the chip laid out around its count alone.
|
||||||
? m_itemIcons->getPixmap(itemId, kChipIconSizePx)
|
const QPixmap icon = m_itemIcons->getSquarePixmap(itemId, kChipIconSizePx);
|
||||||
: QPixmap();
|
|
||||||
|
|
||||||
ItemChip* chip = new ItemChip(icon, this);
|
ItemChip* chip = new ItemChip(icon, this);
|
||||||
m_layout->addWidget(chip, static_cast<int>(index) / kChipsPerRow,
|
m_layout->addWidget(chip, static_cast<int>(index) / kChipsPerRow,
|
||||||
|
|||||||
@@ -10,8 +10,9 @@
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
// Size the item icons are drawn at on the summary line, in device-independent pixels.
|
// Size the items' colored squares are drawn at on the summary line, in device-independent
|
||||||
const int kSummaryIconSizePx = 14;
|
// pixels. Larger than the artwork they carry, which the square insets (REQ-UI-ITEM-ICON).
|
||||||
|
const int kSummaryIconSizePx = 18;
|
||||||
|
|
||||||
// Adds a freshly built label to the summary and shows it.
|
// Adds a freshly built label to the summary and shows it.
|
||||||
//
|
//
|
||||||
@@ -101,13 +102,15 @@ void RecipeSummaryRow::addAmounts(const std::vector<Amount>& amounts)
|
|||||||
{
|
{
|
||||||
for (const Amount& entry : amounts)
|
for (const Amount& entry : amounts)
|
||||||
{
|
{
|
||||||
// A missing icon file is not an error (REQ-UI-ITEM-ICON): the item's id then
|
// The item's icon on its colored square (REQ-UI-ITEM-ICON). A missing icon file
|
||||||
// stands in for its icon.
|
// is not an error: the square stands alone then, and only an item with no square
|
||||||
if (m_itemIcons->hasIcon(entry.itemId))
|
// either falls back to its id in text.
|
||||||
|
const QPixmap icon =
|
||||||
|
m_itemIcons->getSquarePixmap(entry.itemId, kSummaryIconSizePx);
|
||||||
|
if (!icon.isNull())
|
||||||
{
|
{
|
||||||
QLabel* iconLabel = new QLabel(this);
|
QLabel* iconLabel = new QLabel(this);
|
||||||
iconLabel->setPixmap(
|
iconLabel->setPixmap(icon);
|
||||||
m_itemIcons->getPixmap(entry.itemId, kSummaryIconSizePx));
|
|
||||||
addAndShow(m_layout, iconLabel);
|
addAndShow(m_layout, iconLabel);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user