Allow customizing foreground color of status icons

This commit is contained in:
Martchus 2019-05-21 17:56:08 +02:00
parent 0e7cc8ba9b
commit 96ed888e20
5 changed files with 120 additions and 88 deletions

View File

@ -8,10 +8,28 @@
namespace Data {
/*!
* \brief Generates the SVG code for the Syncthing icon with the specified colors and status emblem.
* \brief Generates the SVG code for the Syncthing icon with the specified \a colors and status emblem.
*/
QByteArray makeSyncthingIcon(const GradientColor &gradientColor, StatusEmblem statusEmblem)
QByteArray makeSyncthingIcon(const StatusIconColorSet &colors, StatusEmblem statusEmblem)
{
// serialize colors
auto gradientStartColor = colors.backgroundStart.name(QColor::HexRgb);
auto gradientEndColor = colors.backgroundEnd.name(QColor::HexRgb);
if (colors.backgroundStart.alphaF() < 1.0) {
gradientStartColor += QStringLiteral(";stop-opacity:") + QString::number(colors.backgroundStart.alphaF());
}
if (colors.backgroundEnd.alphaF() < 1.0) {
gradientEndColor += QStringLiteral(";stop-opacity:") + QString::number(colors.backgroundEnd.alphaF());
}
auto fillColor = colors.foreground.name(QColor::HexRgb);
auto strokeColor = fillColor;
if (colors.foreground.alphaF() < 1.0) {
const auto alpha = QString::number(colors.foreground.alphaF());
fillColor += QStringLiteral(";fill-opacity:") + alpha;
strokeColor += QStringLiteral(";stroke-opacity:") + alpha;
}
// make SVG document
// clang-format off
static const QString emblems[] = {
QString(),
@ -60,21 +78,13 @@ QByteArray makeSyncthingIcon(const GradientColor &gradientColor, StatusEmblem st
),
};
const auto &emblemData = emblems[static_cast<int>(statusEmblem)];
auto gradientStart = gradientColor.start.name(QColor::HexRgb);
auto gradientEnd = gradientColor.end.name(QColor::HexRgb);
if (gradientColor.start.alphaF() < 1.0) {
gradientStart += QStringLiteral(";stop-opacity:") + QString::number(gradientColor.start.alphaF());
}
if (gradientColor.end.alphaF() < 1.0) {
gradientEnd += QStringLiteral(";stop-opacity:") + QString::number(gradientColor.end.alphaF());
}
return (QStringLiteral(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<svg xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\">"
"<defs>"
"<linearGradient id=\"grad\" gradientUnits=\"userSpaceOnUse\" x1=\"8\" y1=\"0\" x2=\"8\" y2=\"16\">"
"<stop offset=\"0\" style=\"stop-color:") % gradientStart % QStringLiteral("\"/>"
"<stop offset=\"1\" style=\"stop-color:") % gradientEnd % QStringLiteral("\"/>"
"<stop offset=\"0\" style=\"stop-color:") % gradientStartColor % QStringLiteral("\"/>"
"<stop offset=\"1\" style=\"stop-color:") % gradientEndColor % QStringLiteral("\"/>"
"</linearGradient>"
"<mask id=\"bitemask\" maskUnits=\"userSpaceOnUse\">"
"<g>"
@ -85,14 +95,14 @@ QByteArray makeSyncthingIcon(const GradientColor &gradientColor, StatusEmblem st
"</defs>"
"<g id=\"syncthing-logo\" mask=\"url(#bitemask)\">"
"<circle id=\"outer\" cx=\"8\" cy=\"8\" r=\"8\" style=\"fill:url(#grad)\"/>"
"<circle id=\"inner\" cx=\"8\" cy=\"7.9727402\" r=\"5.9557071\" style=\"fill:none;stroke:#ffffff;stroke-width:0.81771719\"/>"
"<line id=\"arm-l\" x1=\"9.1993189\" y1=\"8.776825\" x2=\"2.262351\" y2=\"9.4173737\" style=\"stroke:#ffffff;stroke-width:0.81771719\"/>"
"<line id=\"arm-tr\" x1=\"9.1993189\" y1=\"8.776825\" x2=\"13.301533\" y2=\"5.3696747\" style=\"stroke:#ffffff;stroke-width:0.81771719\"/>"
"<line id=\"arm-br\" x1=\"9.1993189\" y1=\"8.776825\" x2=\"11.788756\" y2=\"12.51107\" style=\"stroke:#ffffff;stroke-width:0.81771719\"/>"
"<circle id=\"node-c\" cx=\"9.1993189\" cy=\"8.776825\" r=\"1.22\" style=\"fill:#ffffff\"/>"
"<circle id=\"node-l\" cx=\"2.262351\" cy=\"9.4173737\" r=\"1.22\" style=\"fill:#ffffff\"/>"
"<circle id=\"node-tr\" cx=\"13.301533\" cy=\"5.3696747\" r=\"1.22\" style=\"fill:#ffffff\"/>"
"<circle id=\"node-br\" cx=\"11.788756\" cy=\"12.51107\" r=\"1.22\" style=\"fill:#ffffff\"/>"
"<circle id=\"inner\" cx=\"8\" cy=\"7.9727402\" r=\"5.9557071\" style=\"fill:none;stroke:") % strokeColor % QStringLiteral(";stroke-width:0.81771719\"/>"
"<line id=\"arm-l\" x1=\"9.1993189\" y1=\"8.776825\" x2=\"2.262351\" y2=\"9.4173737\" style=\"stroke:") % strokeColor % QStringLiteral(";stroke-width:0.81771719\"/>"
"<line id=\"arm-tr\" x1=\"9.1993189\" y1=\"8.776825\" x2=\"13.301533\" y2=\"5.3696747\" style=\"stroke:") % strokeColor % QStringLiteral(";stroke-width:0.81771719\"/>"
"<line id=\"arm-br\" x1=\"9.1993189\" y1=\"8.776825\" x2=\"11.788756\" y2=\"12.51107\" style=\"stroke:") % strokeColor % QStringLiteral(";stroke-width:0.81771719\"/>"
"<circle id=\"node-c\" cx=\"9.1993189\" cy=\"8.776825\" r=\"1.22\" style=\"fill:") % fillColor % QStringLiteral("\"/>"
"<circle id=\"node-l\" cx=\"2.262351\" cy=\"9.4173737\" r=\"1.22\" style=\"fill:") % fillColor % QStringLiteral("\"/>"
"<circle id=\"node-tr\" cx=\"13.301533\" cy=\"5.3696747\" r=\"1.22\" style=\"fill:") % fillColor % QStringLiteral("\"/>"
"<circle id=\"node-br\" cx=\"11.788756\" cy=\"12.51107\" r=\"1.22\" style=\"fill:") % fillColor % QStringLiteral("\"/>"
"</g>") %
(emblemData.isEmpty() ? QString() : emblemData) % QStringLiteral(
"</svg>"
@ -165,14 +175,14 @@ QByteArray loadFontAwesomeIcon(const QString &iconName, const QColor &color, boo
}
StatusIconSettings::StatusIconSettings()
: defaultColor({ QStringLiteral("#26B6DB"), QStringLiteral("#0882C8") })
, errorColor({ QStringLiteral("#DB3C26"), QStringLiteral("#C80828") })
, warningColor({ QStringLiteral("#c9ce3b"), QStringLiteral("#ebb83b") })
, idleColor({ QStringLiteral("#2D9D69"), QStringLiteral("#2D9D69") })
, scanningColor({ QStringLiteral("#26B6DB"), QStringLiteral("#0882C8") })
, synchronizingColor({ QStringLiteral("#26B6DB"), QStringLiteral("#0882C8") })
, pausedColor({ QStringLiteral("#A9A9A9"), QStringLiteral("#58656C") })
, disconnectedColor({ QStringLiteral("#A9A9A9"), QStringLiteral("#58656C") })
: defaultColor({ QStringLiteral("#26B6DB"), QStringLiteral("#0882C8"), QStringLiteral("#FFFFFF") })
, errorColor({ QStringLiteral("#DB3C26"), QStringLiteral("#C80828"), QStringLiteral("#FFFFFF") })
, warningColor({ QStringLiteral("#c9ce3b"), QStringLiteral("#ebb83b"), QStringLiteral("#FFFFFF") })
, idleColor({ QStringLiteral("#2D9D69"), QStringLiteral("#2D9D69"), QStringLiteral("#FFFFFF") })
, scanningColor({ QStringLiteral("#26B6DB"), QStringLiteral("#0882C8"), QStringLiteral("#FFFFFF") })
, synchronizingColor({ QStringLiteral("#26B6DB"), QStringLiteral("#0882C8"), QStringLiteral("#FFFFFF") })
, pausedColor({ QStringLiteral("#A9A9A9"), QStringLiteral("#58656C"), QStringLiteral("#FFFFFF") })
, disconnectedColor({ QStringLiteral("#A9A9A9"), QStringLiteral("#58656C"), QStringLiteral("#FFFFFF") })
{
}
@ -202,8 +212,11 @@ StatusIconSettings::StatusIconSettings(const QString &str)
}
const auto colors = parts[index].split(QChar(','));
if (colors.size() >= 2) {
field->start = colors[0].toString();
field->end = colors[1].toString();
field->backgroundStart = colors[0].toString();
field->backgroundEnd = colors[1].toString();
}
if (colors.size() >= 3) {
field->foreground = colors[2].toString();
}
++index;
}
@ -218,7 +231,8 @@ QString StatusIconSettings::toString() const
if (!res.isEmpty()) {
res += QChar(';');
}
res += field->start.name(QColor::HexArgb) % QChar(',') % field->end.name(QColor::HexArgb);
res += field->backgroundStart.name(QColor::HexArgb) % QChar(',') % field->backgroundEnd.name(QColor::HexArgb) % QChar(',')
% field->foreground.name(QColor::HexArgb);
}
return res;
}

View File

@ -22,35 +22,39 @@ enum class StatusEmblem {
Add,
};
struct GradientColor {
GradientColor(const QColor &start, const QColor &end);
GradientColor(QColor &&start, QColor &&end);
GradientColor(const QString &start, const QString &end);
struct StatusIconColorSet {
StatusIconColorSet(const QColor &backgroundStart, const QColor &backgroundEnd, const QColor &foreground);
StatusIconColorSet(QColor &&backgroundStart, QColor &&backgroundEnd, QColor &&foreground);
StatusIconColorSet(const QString &backgroundStart, const QString &backgroundEnd, const QString &foreground);
QColor start;
QColor end;
QColor backgroundStart;
QColor backgroundEnd;
QColor foreground;
};
inline GradientColor::GradientColor(const QColor &start, const QColor &end)
: start(start)
, end(end)
inline StatusIconColorSet::StatusIconColorSet(const QColor &backgroundStart, const QColor &backgroundEnd, const QColor &foreground)
: backgroundStart(backgroundStart)
, backgroundEnd(backgroundEnd)
, foreground(foreground)
{
}
inline GradientColor::GradientColor(QColor &&start, QColor &&end)
: start(start)
, end(end)
inline StatusIconColorSet::StatusIconColorSet(QColor &&backgroundStart, QColor &&backgroundEnd, QColor &&foreground)
: backgroundStart(backgroundStart)
, backgroundEnd(backgroundEnd)
, foreground(foreground)
{
}
inline GradientColor::GradientColor(const QString &start, const QString &end)
: start(start)
, end(end)
inline StatusIconColorSet::StatusIconColorSet(const QString &backgroundStart, const QString &backgroundEnd, const QString &foreground)
: backgroundStart(backgroundStart)
, backgroundEnd(backgroundEnd)
, foreground(foreground)
{
}
QByteArray LIB_SYNCTHING_MODEL_EXPORT makeSyncthingIcon(
const GradientColor &gradientColor = GradientColor{ QStringLiteral("#26B6DB"), QStringLiteral("#0882C8") },
const StatusIconColorSet &colors = StatusIconColorSet{ QStringLiteral("#26B6DB"), QStringLiteral("#0882C8"), QStringLiteral("#FFFFFF") },
StatusEmblem statusEmblem = StatusEmblem::None);
QPixmap LIB_SYNCTHING_MODEL_EXPORT renderSvgImage(const QString &path, const QSize &size = QSize(128, 128), int margin = 0);
QPixmap LIB_SYNCTHING_MODEL_EXPORT renderSvgImage(const QByteArray &contents, const QSize &size = QSize(128, 128), int margin = 0);
@ -60,21 +64,21 @@ struct LIB_SYNCTHING_MODEL_EXPORT StatusIconSettings {
explicit StatusIconSettings();
explicit StatusIconSettings(const QString &str);
GradientColor defaultColor;
GradientColor errorColor;
GradientColor warningColor;
GradientColor idleColor;
GradientColor scanningColor;
GradientColor synchronizingColor;
GradientColor pausedColor;
GradientColor disconnectedColor;
StatusIconColorSet defaultColor;
StatusIconColorSet errorColor;
StatusIconColorSet warningColor;
StatusIconColorSet idleColor;
StatusIconColorSet scanningColor;
StatusIconColorSet synchronizingColor;
StatusIconColorSet pausedColor;
StatusIconColorSet disconnectedColor;
static constexpr auto distinguishableColorCount = 8;
struct ColorMapping {
QString colorName;
StatusEmblem defaultEmblem;
GradientColor &setting;
StatusIconColorSet &setting;
};
std::vector<ColorMapping> colorMapping();
QString toString() const;

View File

@ -2,14 +2,6 @@
<ui version="4.0">
<class>QtGui::IconsOptionPage</class>
<widget class="QWidget" name="QtGui::IconsOptionPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>538</width>
<height>167</height>
</rect>
</property>
<property name="windowTitle">
<string>Icons</string>
</property>
@ -19,17 +11,17 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="statusColorsGroupBox">
<widget class="QGroupBox" name="statusIconsGroupBox">
<property name="title">
<string>Status colors</string>
<string>Status icons</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QLabel" name="color1Label">
<widget class="QLabel" name="bgColor1Label">
<property name="text">
<string>Color 1</string>
<string>Background color 1</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@ -37,16 +29,16 @@
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="color2Label">
<widget class="QLabel" name="bgColor2Label">
<property name="text">
<string>Color 2</string>
<string>Background color 2</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="3">
<item row="0" column="4">
<widget class="QLabel" name="previewLabel">
<property name="text">
<string>Preview</string>
@ -59,6 +51,16 @@
<item row="1" column="0">
<widget class="QWidget" name="widget" native="true"/>
</item>
<item row="0" column="3">
<widget class="QLabel" name="foregroundColorLabel">
<property name="text">
<string>Foreground color</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>

View File

@ -508,40 +508,47 @@ QWidget *IconsOptionPage::setupWidget()
// populate form for status icon colors
auto *const gridLayout = ui()->gridLayout;
auto *const statusColorsGroupBox = ui()->statusColorsGroupBox;
auto *const statusIconsGroupBox = ui()->statusIconsGroupBox;
int index = 0;
for (auto &colorMapping : m_settings.colorMapping()) {
// populate widgets array
auto &widgetsForColor = m_widgets[index++] = {
{
new Widgets::ColorButton(statusColorsGroupBox),
new Widgets::ColorButton(statusColorsGroupBox),
new Widgets::ColorButton(statusIconsGroupBox),
new Widgets::ColorButton(statusIconsGroupBox),
new Widgets::ColorButton(statusIconsGroupBox),
},
new QLabel(statusColorsGroupBox),
new QLabel(statusIconsGroupBox),
&colorMapping.setting,
colorMapping.defaultEmblem,
};
// add label for color name
gridLayout->addWidget(new QLabel(colorMapping.colorName, statusColorsGroupBox), index, 0, Qt::AlignRight | Qt::AlignVCenter);
gridLayout->addWidget(new QLabel(colorMapping.colorName, statusIconsGroupBox), index, 0, Qt::AlignRight | Qt::AlignVCenter);
// setup preview
gridLayout->addWidget(widgetsForColor.previewLabel, index, 3, Qt::AlignCenter);
gridLayout->addWidget(widgetsForColor.previewLabel, index, 4, Qt::AlignCenter);
const auto updatePreview = [&widgetsForColor] {
widgetsForColor.previewLabel->setPixmap(
renderSvgImage(makeSyncthingIcon(GradientColor{ widgetsForColor.colorButtons[0]->color(), widgetsForColor.colorButtons[1]->color() },
widgetsForColor.statusEmblem),
QSize(32, 32)));
widgetsForColor.previewLabel->setPixmap(renderSvgImage(makeSyncthingIcon(
StatusIconColorSet{
widgetsForColor.colorButtons[0]->color(),
widgetsForColor.colorButtons[1]->color(),
widgetsForColor.colorButtons[2]->color(),
},
widgetsForColor.statusEmblem),
QSize(32, 32)));
};
for (const auto &colorButton : widgetsForColor.colorButtons) {
QObject::connect(colorButton, &Widgets::ColorButton::colorChanged, updatePreview);
}
// setup color buttons
widgetsForColor.colorButtons[0]->setColor(colorMapping.setting.start);
widgetsForColor.colorButtons[1]->setColor(colorMapping.setting.end);
widgetsForColor.colorButtons[0]->setColor(colorMapping.setting.backgroundStart);
widgetsForColor.colorButtons[1]->setColor(colorMapping.setting.backgroundEnd);
widgetsForColor.colorButtons[2]->setColor(colorMapping.setting.foreground);
gridLayout->addWidget(widgetsForColor.colorButtons[0], index, 1);
gridLayout->addWidget(widgetsForColor.colorButtons[1], index, 2);
gridLayout->addWidget(widgetsForColor.colorButtons[2], index, 3);
if (index >= StatusIconSettings::distinguishableColorCount) {
break;
@ -561,7 +568,11 @@ QWidget *IconsOptionPage::setupWidget()
bool IconsOptionPage::apply()
{
for (auto &widgetsForColor : m_widgets) {
*widgetsForColor.setting = GradientColor{ widgetsForColor.colorButtons[0]->color(), widgetsForColor.colorButtons[1]->color() };
*widgetsForColor.setting = StatusIconColorSet{
widgetsForColor.colorButtons[0]->color(),
widgetsForColor.colorButtons[1]->color(),
widgetsForColor.colorButtons[2]->color(),
};
}
values().statusIcons = m_settings;
return true;
@ -570,8 +581,9 @@ bool IconsOptionPage::apply()
void IconsOptionPage::update()
{
for (auto &widgetsForColor : m_widgets) {
widgetsForColor.colorButtons[0]->setColor(widgetsForColor.setting->start);
widgetsForColor.colorButtons[1]->setColor(widgetsForColor.setting->end);
widgetsForColor.colorButtons[0]->setColor(widgetsForColor.setting->backgroundStart);
widgetsForColor.colorButtons[1]->setColor(widgetsForColor.setting->backgroundEnd);
widgetsForColor.colorButtons[2]->setColor(widgetsForColor.setting->foreground);
}
}

View File

@ -84,9 +84,9 @@ private:
void update();
Data::StatusIconSettings m_settings;
struct {
Widgets::ColorButton *colorButtons[2] = {};
Widgets::ColorButton *colorButtons[3] = {};
QLabel *previewLabel = nullptr;
Data::GradientColor *setting = nullptr;
Data::StatusIconColorSet *setting = nullptr;
Data::StatusEmblem statusEmblem = Data::StatusEmblem::None;
} m_widgets[Data::StatusIconSettings::distinguishableColorCount];
END_DECLARE_OPTION_PAGE