#include "Glyphs.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Theme.hpp" namespace geopro::app { namespace { // 每个图标的 SVG 内容(线性风格,24x24 视窗,描边由外层注入颜色)。 // 路径取自 Lucide 图标集(MIT 许可,业界通用),保证专业、清晰、语义正确。 QString svgPathFor(Glyph t) { switch (t) { case Glyph::Tree: return QStringLiteral( "" ""); case Glyph::Dataset: return QStringLiteral( "" ""); case Glyph::Map: return QStringLiteral( "" "" ""); case Glyph::Detail: return QStringLiteral( ""); case Glyph::Anomaly: return QStringLiteral( ""); case Glyph::Property: return QStringLiteral( ""); case Glyph::Plus: return QStringLiteral(""); case Glyph::Filter: return QStringLiteral( ""); case Glyph::Upload: return QStringLiteral( "" ""); case Glyph::Download: return QStringLiteral( "" ""); case Glyph::Collapse: return QStringLiteral(""); case Glyph::Workspace: return QStringLiteral( "" "" "" ""); case Glyph::Folder: return QStringLiteral( ""); case Glyph::Help: return QStringLiteral( "" ""); case Glyph::Bell: return QStringLiteral( "" ""); case Glyph::Gear: return QStringLiteral( ""); } return QString(); } } // namespace QIcon makeGlyph(Glyph type, const QColor& color, int px, int padRight) { const QString svg = QStringLiteral("%2") .arg(color.name(), svgPathFor(type)); QSvgRenderer renderer(svg.toUtf8()); // 以 3x 超采样渲染再设 devicePixelRatio,保证在任意缩放/DPI 下都清晰。 constexpr qreal kSuper = 3.0; const int dim = qRound(px * kSuper); // 图标本体边长(方形) const int dimW = qRound((px + padRight) * kSuper); // 含右透明内边距的画布宽 QImage img(dimW, dim, QImage::Format_ARGB32_Premultiplied); img.fill(Qt::transparent); QPainter p(&img); p.setRenderHint(QPainter::Antialiasing, true); renderer.render(&p, QRectF(0, 0, dim, dim)); // 图标居左方形渲染,右侧 padRight 留透明 p.end(); QPixmap pm = QPixmap::fromImage(img); pm.setDevicePixelRatio(kSuper); return QIcon(pm); } QString writeChevronIcon(bool open, const QColor& color) { constexpr int px = 16; constexpr int kScale = 3; QPixmap pm(px * kScale, px * kScale); pm.fill(Qt::transparent); QPainter p(&pm); p.setRenderHint(QPainter::Antialiasing, true); QPen pen(color, px * kScale * 0.1); pen.setCapStyle(Qt::RoundCap); pen.setJoinStyle(Qt::RoundJoin); p.setPen(pen); const double w = px * kScale; const double h = px * kScale; if (open) { // ▼ 向下 p.drawLine(QPointF(w * 0.32, h * 0.42), QPointF(w * 0.5, h * 0.60)); p.drawLine(QPointF(w * 0.5, h * 0.60), QPointF(w * 0.68, h * 0.42)); } else { // ▶ 向右 p.drawLine(QPointF(w * 0.42, h * 0.32), QPointF(w * 0.60, h * 0.5)); p.drawLine(QPointF(w * 0.60, h * 0.5), QPointF(w * 0.42, h * 0.68)); } p.end(); const QString path = QDir(QDir::tempPath()).filePath(open ? QStringLiteral("geopro_tree_open.png") : QStringLiteral("geopro_tree_closed.png")); pm.save(path, "PNG"); return path; } QString writeCheckboxIcon(bool checked, const QColor& border, const QColor& fill, const QColor& check, const QString& tag) { constexpr int px = 16; constexpr int kScale = 3; QPixmap pm(px * kScale, px * kScale); pm.fill(Qt::transparent); QPainter p(&pm); p.setRenderHint(QPainter::Antialiasing, true); const double s = px * kScale; const double inset = s * 0.14; const QRectF box(inset, inset, s - 2 * inset, s - 2 * inset); const double r = s * 0.14; if (checked) { p.setPen(Qt::NoPen); p.setBrush(fill); p.drawRoundedRect(box, r, r); QPen cpen(check, s * 0.12); cpen.setCapStyle(Qt::RoundCap); cpen.setJoinStyle(Qt::RoundJoin); p.setPen(cpen); p.drawLine(QPointF(s * 0.30, s * 0.52), QPointF(s * 0.44, s * 0.66)); p.drawLine(QPointF(s * 0.44, s * 0.66), QPointF(s * 0.70, s * 0.34)); } else { QPen bpen(border, s * 0.085); p.setPen(bpen); p.setBrush(fill); p.drawRoundedRect(box, r, r); } p.end(); const QString path = QDir(QDir::tempPath()) .filePath(QStringLiteral("geopro_chk_%1_%2.png") .arg(tag, checked ? QStringLiteral("on") : QStringLiteral("off"))); pm.save(path, "PNG"); return path; } namespace { // 当前主题下的 chrome 图标色:取主题主文本色(暗=浅、亮=深),保证两种模式都清晰。 QColor themedIconColor() { return isDarkTheme() ? QColor(0xE6, 0xE8, 0xEB) : QColor(0x1F, 0x2A, 0x3D); // 主文字明/暗 } } // namespace void setThemedGlyph(QLabel* label, Glyph type, int px) { if (!label) return; auto apply = [label, type, px]() { label->setPixmap(makeGlyph(type, themedIconColor(), px).pixmap(px, px)); }; apply(); QObject::connect(&ThemeManager::instance(), &ThemeManager::changed, label, [apply]() { apply(); }); } void setThemedGlyph(QAbstractButton* button, Glyph type, int px, int padRight) { if (!button) return; auto apply = [button, type, px, padRight]() { button->setIcon(makeGlyph(type, themedIconColor(), px, padRight)); if (padRight > 0) button->setIconSize(QSize(px + padRight, px)); // 含右内边距,文字被右推 }; apply(); QObject::connect(&ThemeManager::instance(), &ThemeManager::changed, button, [apply]() { apply(); }); } } // namespace geopro::app