aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/qqcstylegenerator/config.json31
-rw-r--r--tools/qqcstylegenerator/howto.txt8
-rw-r--r--tools/qqcstylegenerator/src/stylegenerator.h159
3 files changed, 185 insertions, 13 deletions
diff --git a/tools/qqcstylegenerator/config.json b/tools/qqcstylegenerator/config.json
index ea7300ed21..117e0b236b 100644
--- a/tools/qqcstylegenerator/config.json
+++ b/tools/qqcstylegenerator/config.json
@@ -6,11 +6,20 @@
{"atom": "background", "export": ["image"]},
{"atom": "contentItem", "export": ["layout"]},
{"atom": "indicator", "export": ["image"]},
- {"atom": "label", "export": ["text", "geometry"]}
+ {"atom": "label", "export": ["text", "geometry"]},
+ {"atom": "icon", "export": ["geometry"]}
],
"qml": {
"copy": [":/templates/*.qml", ":/templates/impl/*.qml"]
},
+ "icons": [
+ {
+ "name": "Icons",
+ "page": "Components",
+ "component set": "Icon",
+ "export": ["image"]
+ }
+ ],
"default controls": [
{
"name": "ApplicationWindow"
@@ -38,10 +47,12 @@
"name": "Button",
"page": "Components",
"component set": "ButtonTemplate${Theme}",
+ "contents": ["icon", "label"],
"atoms": [
{"atom": "background", "figmaPath": "Background"},
{"atom": "contentItem", "figmaPath": "Layout"},
- {"atom": "label", "figmaPath": "label"}
+ {"atom": "label", "figmaPath": "label"},
+ {"atom": "icon", "figmaPath": "Icon"}
],
"states": [
{"state": "normal", "figmaState": "normal"},
@@ -166,10 +177,12 @@
"name": "FlatButton",
"page": "Components",
"component set": "FlatButtonTemplate${Theme}",
+ "contents": ["icon", "label"],
"atoms": [
{"atom": "background", "figmaPath": "Background"},
{"atom": "contentItem", "figmaPath": "Layout"},
- {"atom": "label", "figmaPath": "label"}
+ {"atom": "label", "figmaPath": "label"},
+ {"atom": "icon", "figmaPath": "Icon"}
],
"states": [
{"state": "normal", "figmaState": "normal"},
@@ -328,10 +341,12 @@
"name": "RoundButton",
"page": "Components",
"component set": "RoundButtonTemplate${Theme}",
+ "contents": ["icon", "label"],
"atoms": [
{"atom": "background", "figmaPath": "Background"},
{"atom": "contentItem", "figmaPath": "Layout"},
- {"atom": "label", "figmaPath": "label"}
+ {"atom": "label", "figmaPath": "label"},
+ {"atom": "icon", "figmaPath": "Icon"}
],
"states": [
{"state": "normal", "figmaState": "normal"},
@@ -462,10 +477,12 @@
"name": "TabButton",
"page": "Components",
"component set": "TabButtonTemplate${Theme}",
+ "contents": ["icon", "label"],
"atoms": [
{"atom": "background", "figmaPath": "Background"},
{"atom": "contentItem", "figmaPath": "Layout"},
- {"atom": "label", "figmaPath": "label"}
+ {"atom": "label", "figmaPath": "label"},
+ {"atom": "icon", "figmaPath": "Icon"}
],
"states": [
{"state": "checked", "figmaState": "checked"},
@@ -532,10 +549,12 @@
"name": "ToolButton",
"page": "Components",
"component set": "ToolButtonTemplate${Theme}",
+ "contents": ["icon", "label"],
"atoms": [
{"atom": "background", "figmaPath": "Background"},
{"atom": "contentItem", "figmaPath": "Layout"},
- {"atom": "label", "figmaPath": "label"}
+ {"atom": "label", "figmaPath": "label"},
+ {"atom": "icon", "figmaPath": "Icon"}
],
"states": [
{"state": "checked", "figmaState": "checked"},
diff --git a/tools/qqcstylegenerator/howto.txt b/tools/qqcstylegenerator/howto.txt
index 30bd601188..31ffcce54d 100644
--- a/tools/qqcstylegenerator/howto.txt
+++ b/tools/qqcstylegenerator/howto.txt
@@ -13,3 +13,11 @@ If you want to use compile time selection of the style, import the style directl
<code>import "qrc:/qt/qml/@styleName@"</code>
+The style also contains theme icons. In order to use them, you need to add the following line to main.cpp:
+
+<code>QIcon::setThemeName("@styleName@");</code>
+
+You can then use the them from e.g a Button:
+
+<code>Button { icon.name: "navigation_settings" }</code>
+
diff --git a/tools/qqcstylegenerator/src/stylegenerator.h b/tools/qqcstylegenerator/src/stylegenerator.h
index 4d041928c9..ebbe44d04d 100644
--- a/tools/qqcstylegenerator/src/stylegenerator.h
+++ b/tools/qqcstylegenerator/src/stylegenerator.h
@@ -13,6 +13,8 @@
#include <QDir>
#include <QPixmap>
+#include <set>
+
#include "jsontools.h"
#include "bridge.h"
@@ -84,8 +86,10 @@ public:
if (!m_abort)
generateControls();
if (!m_abort)
+ generateIcons();
+ if (!m_abort)
downloadImages();
- progressTo(3);
+ progressTo(4);
progressLabel("Generating configuration files");
if (!m_abort)
generateConfiguration();
@@ -93,6 +97,8 @@ public:
generateQmlDir();
if (!m_abort)
generateQrcFile();
+ if (!m_abort)
+ generateIndexThemeFile();
} catch (std::exception &e) {
error(e.what());
}
@@ -540,6 +546,45 @@ private:
m_outputConfig[m_currentTheme].insert(controlNameModified, outputControlConfig);
}
+ void generateIcons()
+ {
+ // Note that we don't generate different icons per theme, since
+ // they will be colored with a shader in QML to follow the
+ // button icon color.
+ try {
+ QJsonArray iconGroupsArray = getArray("icons", m_inputConfig);
+ for (const auto iconGroupValue : iconGroupsArray) {
+ const QJsonObject iconGroupConfig = iconGroupValue.toObject();
+ const auto name = getString("name", iconGroupConfig);
+ progressLabel("Generating " + name);
+
+ QStringList exportList = getStringList("export", iconGroupConfig);
+ if (exportList.contains("image")) {
+ exportList.removeAll("image");
+ exportList += m_imageFormats;
+ }
+
+ const auto componentSetName = getThemeString("component set", iconGroupConfig);
+ const QJsonObject searchRoot = getComponentSearchRoot(iconGroupConfig);
+ const QJsonObject componentSet = getComponentSet(searchRoot, componentSetName);
+ const QString componentSetId = JsonTools::getString("id", componentSet);
+ const QString componentSetPath = JsonTools::resolvedPath(componentSetId);
+ debug("using component set: " + componentSetPath);
+
+ // All the children of the component represents an icon
+ const auto children = componentSet.value("children").toArray();
+ progressTo(children.count());
+
+ for (auto it = children.constBegin(); it != children.constEnd(); ++it) {
+ exportIcon(it->toObject(), exportList);
+ progress();
+ }
+ }
+ } catch (std::exception &e) {
+ warning("failed exporting icons: " + QString(e.what()));
+ }
+ }
+
QString generateQMLForJsonObject(const QJsonObject &object, const QString &objectName, QString &indent) {
QString qml;
@@ -707,6 +752,9 @@ private:
: imageName + '.' + imageFormat.format);
auto &figmaIdToFileNameMap = m_imagesToDownload[imageFormat.name];
+ if (figmaIdToFileNameMap.contains(figmaId))
+ warning("'" + figmaIdToFileNameMap[figmaId] + "' has the same figmaId '" + figmaId
+ + "' as '" + fileNameForWriting + "', and will be overwritten");
figmaIdToFileNameMap.insert(figmaId, fileNameForWriting);
m_imageCount++;
@@ -726,6 +774,46 @@ private:
exportBorderImageOffset(atom, outputConfig);
}
+ void exportIcon(const QJsonObject &iconObj, const QStringList &imageFormats)
+ {
+ const QString figmaId = getString("id", iconObj);
+ const QString figmaName = getString("name", iconObj);
+
+ QString imageName;
+ static QRegularExpression re(R"(Property.*=(.*))");
+ QRegularExpressionMatch match = re.match(figmaName);
+ if (match.hasMatch()) {
+ // The name might be a combination of many properties
+ QStringList propertyNames;
+ const auto properties = figmaName.split(',');
+ for (const auto &propertyName : properties) {
+ QRegularExpressionMatch match = re.match(propertyName);
+ propertyNames << match.captured(1);
+ }
+ imageName = propertyNames.join('_').toLower();
+ } else {
+ imageName = figmaName;
+ }
+ imageName.replace(' ', '_');
+ imageName.replace('-', '_');
+
+ for (const ImageFormat imageFormat : imageFormats) {
+ const QString imageFolder = "icons/icons"
+ + (imageFormat.hasScale ? + "@" + imageFormat.scale + "x" : "") + "/";
+ const QString fileName = imageFolder + imageName + "." + imageFormat.format;
+
+ auto &figmaIdToFileNameMap = m_imagesToDownload[imageFormat.name];
+ if (figmaIdToFileNameMap.contains(figmaId))
+ warning("'" + figmaIdToFileNameMap[figmaId] + "' has the same figmaId '" + figmaId
+ + "' as '" + fileName + "', and will be overwritten");
+ figmaIdToFileNameMap.insert(figmaId, fileName);
+ m_icons.insert(fileName);
+ m_imageCount++;
+
+ debug("exporting icon: " + fileName);
+ }
+ }
+
void exportJson(const QJsonObject &atom, QJsonObject &outputConfig)
{
const QString name = getString("name", outputConfig);
@@ -1008,12 +1096,14 @@ private:
{
debug("Generating Qt resource file");
const QString styleName = QFileInfo(m_bridge->m_targetDirectory).fileName();
- const QString targetPath = QFileInfo(m_bridge->m_targetDirectory).absolutePath();
+ const QString targetPath = QFileInfo(m_bridge->m_targetDirectory).absolutePath() + QDir::separator();
QString resources;
resources += "<RCC>\n";
- resources += "\t<qresource prefix=\"/qt/qml\">\n";
+ // Add the style into the prefix "/qt/qml", since this path is the
+ // default controls style search path, and therefore works out-of-the-box
+ resources += "\t<qresource prefix=\"/qt/qml\">\n";
QDirIterator it(styleName, QDirIterator::Subdirectories);
while (it.hasNext()) {
QString file = it.next();
@@ -1021,16 +1111,70 @@ private:
// QDir::NoDotAndDotDot
continue;
}
- resources += "\t\t<file alias=\"" + file + "\">"
- + targetPath + QDir::separator() + file
- + "</file>\n";
+ if (file.startsWith(styleName + "/icons/")) {
+ // icons go into a separate prefix below
+ continue;
+ }
+ resources += "\t\t<file alias=\"" + file + "\">" + targetPath + file + "</file>\n";
}
+ resources += "\t</qresource>\n";
+ // Add icons into the prefix "/icons", since this path is the
+ // default controls theme search path, and therefore works out-of-the-box
+ resources += "\t<qresource prefix=\"/icons\">\n";
+ resources += "\t\t<file alias=\"" + styleName + "/index.theme\">" + targetPath
+ + styleName + "/icons/index.theme</file>\n";
+
+ for (const QString &iconPath : m_icons) {
+ QString alias = iconPath;
+ alias.replace(QRegularExpression("^icons"), styleName);
+ resources += "\t\t<file alias=\"" + alias + "\">" + targetPath
+ + styleName + QDir::separator() + iconPath + "</file>\n";
+ }
resources += "\t</qresource>\n";
+
resources += "</RCC>\n";
+ createTextFileInStylefolder(styleName + ".qrc", resources);
+ progress();
+ }
+
+ void generateIndexThemeFile()
+ {
+ debug("Generating icons/index.theme");
+ const QString styleName = QFileInfo(m_bridge->m_targetDirectory).fileName();
+ const QString targetPath = QFileInfo(m_bridge->m_targetDirectory).absolutePath() + QDir::separator();
+ QString scaleDirectoriesConfig;
+ QStringList scaleDirectories;
+ QDirIterator it(styleName + QDir::separator() + "icons");
+ QRegularExpression reGetScale(R"(@(.*)x)");
- createTextFileInStylefolder(styleName + ".qrc", resources);
+ while (it.hasNext()) {
+ const QString file = it.next();
+ const QFileInfo fileInfo(file);
+ if (file.endsWith('.') || !fileInfo.isDir())
+ continue;
+
+ const QString directoryName = fileInfo.fileName();
+ scaleDirectories += directoryName;
+
+ auto scale = reGetScale.match(directoryName).captured(1);
+ if (scale.isEmpty())
+ scale = "1";
+
+ scaleDirectoriesConfig += "[" + directoryName + "]\n"
+ + "Scale=" + scale + "\n"
+ + "Size=32\n"
+ + "Type=Fixed\n\n";
+ }
+
+ const QString contents = QStringLiteral("[Icon Theme]\n")
+ + "Name=" + styleName + "\n"
+ + "Comment=Generated by Qt StyleGenerator\n\n"
+ + "Directories=" + scaleDirectories.join(',') + "\n\n"
+ + scaleDirectoriesConfig;
+
+ createTextFileInStylefolder("icons/index.theme", contents);
progress();
}
@@ -1210,6 +1354,7 @@ private:
QJsonDocument m_document;
QJsonObject m_inputConfig;
QMap<QString, QJsonObject> m_outputConfig;
+ std::set<QString> m_icons;
QStringList m_qmlDirControls;
QString m_cachedPageName;