Files
Курнат Андрей 2315f25754 Initial QSfera import
2026-06-07 10:20:04 +03:00

281 lines
9.8 KiB
C++

// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2025 OpenCloud FontIcon Collector Tool
#include <QApplication>
#include <QMainWindow>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QListWidget>
#include <QTextEdit>
#include <QLabel>
#include <QSplitter>
#include <QScrollArea>
#include <QGridLayout>
#include <QFrame>
#include <QDir>
#include <QDirIterator>
#include <QTextStream>
#include <QRegularExpression>
#include <QDebug>
#include <QFont>
#include <QCommandLineParser>
#include <QGroupBox>
#include <QFontDatabase>
#include "fonticon.h"
using namespace Qt::Literals::StringLiterals;
using namespace OCC::Resources;
struct FontIconUsage {
QString filePath;
int lineNumber;
QString sourceContext;
QChar glyph;
FontIcon::FontFamily family;
FontIcon::Size size;
QString fullConstructorCall;
};
class FontIconCollectorWidget : public QWidget
{
Q_OBJECT
public:
explicit FontIconCollectorWidget(QWidget *parent = nullptr)
: QWidget(parent)
{
setupUI();
scanForFontIconUsages();
}
private Q_SLOTS:
void onUsageSelected(int row)
{
if (row < 0 || row >= m_usages.size()) {
return;
}
const auto &usage = m_usages[row];
// Update the glyph display
FontIcon icon(usage.family, usage.glyph, usage.size);
QPixmap pixmap = icon.pixmap(QSize(64, 64));
m_glyphLabel->setPixmap(pixmap);
// Update the details
m_detailsText->clear();
m_detailsText->append(u"File: %1"_s.arg(usage.filePath));
m_detailsText->append(u"Line: %1"_s.arg(usage.lineNumber));
m_detailsText->append(u"Glyph: %1 (0x%2)"_s.arg(usage.glyph).arg(static_cast<uint16_t>(usage.glyph.unicode()), 4, 16, QChar('0'_L1)));
m_detailsText->append(u"Family: %1"_s.arg(usage.family == FontIcon::FontFamily::FontAwesome ? u"FontAwesome"_s : u"RemixIcon"_s));
m_detailsText->append(u"Size: %1"_s.arg(usage.size == FontIcon::Size::Normal ? u"Normal"_s : u"Half"_s));
m_detailsText->append(u"\n"_s);
m_detailsText->append(u"Context:"_s);
m_detailsText->append(usage.sourceContext);
}
private:
void setupUI()
{
setWindowTitle(u"FontIcon Usage Collector"_s);
resize(1200, 800);
auto *layout = new QHBoxLayout(this);
auto *splitter = new QSplitter(Qt::Horizontal, this);
// Left side: List of usages
auto *leftWidget = new QWidget;
auto *leftLayout = new QVBoxLayout(leftWidget);
leftLayout->addWidget(new QLabel(u"FontIcon Usages Found:"_s));
m_usagesList = new QListWidget;
leftLayout->addWidget(m_usagesList);
connect(m_usagesList, &QListWidget::currentRowChanged, this, &FontIconCollectorWidget::onUsageSelected);
// Right side: Details and glyph display
auto *rightWidget = new QWidget;
auto *rightLayout = new QVBoxLayout(rightWidget);
// Glyph display
auto *glyphGroup = new QGroupBox(u"Glyph Preview"_s);
auto *glyphLayout = new QVBoxLayout(glyphGroup);
m_glyphLabel = new QLabel;
m_glyphLabel->setAlignment(Qt::AlignCenter);
m_glyphLabel->setStyleSheet(u"border: 1px solid gray; padding: 10px;"_s);
m_glyphLabel->setMinimumSize(100, 100);
glyphLayout->addWidget(m_glyphLabel);
// Details text
auto *detailsGroup = new QGroupBox(u"Details"_s);
auto *detailsLayout = new QVBoxLayout(detailsGroup);
m_detailsText = new QTextEdit;
m_detailsText->setReadOnly(true);
m_detailsText->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
detailsLayout->addWidget(m_detailsText);
rightLayout->addWidget(glyphGroup);
rightLayout->addWidget(detailsGroup);
splitter->addWidget(leftWidget);
splitter->addWidget(rightWidget);
splitter->setSizes({400, 800});
layout->addWidget(splitter);
}
void scanForFontIconUsages()
{
QDir srcDir = QDir(QStringLiteral(SRC_DIR));
if (!srcDir.exists()) {
qWarning() << "Could not find src directory";
return;
}
qDebug() << "Scanning directory:" << srcDir.absolutePath();
// Recursively scan for C++ files
QDirIterator it(srcDir.absolutePath(), QStringList() << u"*.cpp"_s << u"*.h"_s, QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext()) {
QString filePath = it.next();
scanFile(filePath);
}
qDebug() << "Found" << m_usages.size() << "FontIcon usages";
// Populate the list widget
for (const auto &usage : m_usages) {
QString displayText = u"%1:%2 - %3"_s.arg(QFileInfo(usage.filePath).fileName(), QString::number(usage.lineNumber), usage.glyph);
m_usagesList->addItem(displayText);
}
if (!m_usages.isEmpty()) {
m_usagesList->setCurrentRow(0);
}
}
void scanFile(const QString &filePath)
{
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return;
}
QTextStream in(&file);
QString content = in.readAll();
QStringList lines = content.split('\n'_L1);
// Regular expressions to match FontIcon constructor calls
QRegularExpression fontIconRegex(uR"(FontIcon\s*\(\s*([^)]+)\s*\))"_s);
QRegularExpression glyphRegex(uR"(u'(.)')"_s);
QRegularExpression familyRegex(uR"(FontIcon::FontFamily::(\w+))"_s);
QRegularExpression sizeRegex(uR"(FontIcon::Size::(\w+))"_s);
for (int lineNum = 0; lineNum < lines.size(); ++lineNum) {
const QString &line = lines[lineNum];
auto fontIconMatch = fontIconRegex.match(line);
if (fontIconMatch.hasMatch()) {
FontIconUsage usage;
usage.filePath = filePath;
usage.lineNumber = lineNum + 1;
usage.fullConstructorCall = fontIconMatch.captured(0);
// Extract glyph
auto glyphMatch = glyphRegex.match(line);
if (glyphMatch.hasMatch()) {
QString glyphStr = glyphMatch.captured(1);
if (!glyphStr.isEmpty()) {
usage.glyph = glyphStr[0];
}
} else {
// Look for hex unicode like u'\uf015'
QRegularExpression hexGlyphRegex(uR"(u'\\u([0-9a-fA-F]{4})')"_s);
auto hexMatch = hexGlyphRegex.match(line);
if (hexMatch.hasMatch()) {
bool ok;
uint unicode = hexMatch.captured(1).toUInt(&ok, 16);
if (ok) {
usage.glyph = QChar(unicode);
}
} else {
// Try to find the glyph directly in the line
QRegularExpression directGlyphRegex(uR"(u'(.)')"_s);
auto directMatch = directGlyphRegex.match(line);
if (directMatch.hasMatch()) {
usage.glyph = directMatch.captured(1)[0];
} else {
usage.glyph = QChar('?'_L1); // Unknown glyph
}
}
}
// Extract family (default to FontAwesome)
auto familyMatch = familyRegex.match(line);
if (familyMatch.hasMatch()) {
QString familyStr = familyMatch.captured(1);
usage.family = (familyStr == u"RemixIcon"_s) ? FontIcon::FontFamily::RemixIcon : FontIcon::FontFamily::FontAwesome;
} else {
usage.family = FontIcon::FontFamily::FontAwesome;
}
// Extract size (default to Normal)
auto sizeMatch = sizeRegex.match(line);
if (sizeMatch.hasMatch()) {
QString sizeStr = sizeMatch.captured(1);
usage.size = (sizeStr == u"Half"_s) ? FontIcon::Size::Half : FontIcon::Size::Normal;
} else {
usage.size = FontIcon::Size::Normal;
}
// Get context (current line and a few lines around it)
QStringList contextLines;
for (int ctx = qMax(0, lineNum - 2); ctx <= qMin(lines.size() - 1, lineNum + 2); ++ctx) {
QString prefix = (ctx == lineNum) ? u">>> "_s : u" "_s;
contextLines << u"%1%2: %3"_s.arg(prefix, QString::number(ctx + 1), lines[ctx]);
}
usage.sourceContext = contextLines.join('\n'_L1);
m_usages.append(usage);
}
}
}
private:
QListWidget *m_usagesList = nullptr;
QLabel *m_glyphLabel = nullptr;
QTextEdit *m_detailsText = nullptr;
QList<FontIconUsage> m_usages;
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.setApplicationName(u"FontIcon Collector"_s);
app.setApplicationVersion(u"1.0"_s);
const QString cascadiaCodeFontPath = u"Cascadia Code NF"_s;
if (QFontDatabase::hasFamily(cascadiaCodeFontPath)) {
QApplication::setFont(cascadiaCodeFontPath);
}
QCommandLineParser parser;
parser.setApplicationDescription(u"Collects and displays FontIcon usages from the QSfera Desktop codebase"_s);
parser.addHelpOption();
parser.addVersionOption();
parser.process(app);
FontIconCollectorWidget window;
window.show();
return app.exec();
}
#include "fonticon_collector.moc"