Allow configuring thickness of status icons

This commit is contained in:
Martchus 2022-12-20 18:28:38 +01:00
parent 6114949233
commit 3132c006c5
5 changed files with 68 additions and 30 deletions

View File

@ -25,7 +25,7 @@ namespace Data {
/*!
* \brief Generates the SVG code for the Syncthing icon with the specified \a colors and status emblem.
*/
QByteArray makeSyncthingIcon(const StatusIconColorSet &colors, StatusEmblem statusEmblem)
QByteArray makeSyncthingIcon(const StatusIconColorSet &colors, StatusEmblem statusEmblem, StatusIconStrokeWidth strokeWidth)
{
// serialize colors
auto gradientStartColor = colors.backgroundStart.name(QColor::HexRgb);
@ -93,7 +93,11 @@ QByteArray makeSyncthingIcon(const StatusIconColorSet &colors, StatusEmblem stat
"</g>"
),
};
static const auto normalStrokeWidth = QStringLiteral("0.81771719"), thickStrokeWidth = QStringLiteral("1.22");
static const auto normalCircleRadius = QStringLiteral("1.22"), largeCircleRadius = QStringLiteral("1.5");
const auto &emblemData = emblems[static_cast<int>(statusEmblem)];
const auto &strokeWidthF = strokeWidth == StatusIconStrokeWidth::Normal ? normalStrokeWidth : thickStrokeWidth;
const auto &circleRadius = strokeWidth == StatusIconStrokeWidth::Normal ? normalCircleRadius : largeCircleRadius;
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\">"
@ -111,16 +115,16 @@ QByteArray makeSyncthingIcon(const StatusIconColorSet &colors, StatusEmblem stat
"</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:") % 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("\"/>"
"<circle id=\"inner\" cx=\"8\" cy=\"7.9727402\" r=\"5.9557071\" style=\"fill:none;stroke:") % strokeColor % QStringLiteral(";stroke-width:") % strokeWidthF % QStringLiteral("\"/>"
"<line id=\"arm-l\" x1=\"9.1993189\" y1=\"8.776825\" x2=\"2.262351\" y2=\"9.4173737\" style=\"stroke:") % strokeColor % QStringLiteral(";stroke-width:") % strokeWidthF % QStringLiteral("\"/>"
"<line id=\"arm-tr\" x1=\"9.1993189\" y1=\"8.776825\" x2=\"13.301533\" y2=\"5.3696747\" style=\"stroke:") % strokeColor % QStringLiteral(";stroke-width:") % strokeWidthF % QStringLiteral("\"/>"
"<line id=\"arm-br\" x1=\"9.1993189\" y1=\"8.776825\" x2=\"11.788756\" y2=\"12.51107\" style=\"stroke:") % strokeColor % QStringLiteral(";stroke-width:") % strokeWidthF % QStringLiteral("\"/>"
"<circle id=\"node-c\" cx=\"9.1993189\" cy=\"8.776825\" r=\"") % circleRadius % QStringLiteral("\" style=\"fill:") % fillColor % QStringLiteral("\"/>"
"<circle id=\"node-l\" cx=\"2.262351\" cy=\"9.4173737\" r=\"") % circleRadius % QStringLiteral("\" style=\"fill:") % fillColor % QStringLiteral("\"/>"
"<circle id=\"node-tr\" cx=\"13.301533\" cy=\"5.3696747\" r=\"") % circleRadius % QStringLiteral("\" style=\"fill:") % fillColor % QStringLiteral("\"/>"
"<circle id=\"node-br\" cx=\"11.788756\" cy=\"12.51107\" r=\"") % circleRadius % QStringLiteral("\" style=\"fill:") % fillColor % QStringLiteral("\"/>"
"</g>") %
(emblemData.isEmpty() ? QString() : emblemData) % QStringLiteral(
emblemData % QStringLiteral(
"</svg>"
)).toUtf8();
// clang-format on
@ -266,16 +270,18 @@ QString StatusIconSettings::toString() const
}
StatusIcons::StatusIcons(const StatusIconSettings &settings)
: disconnected(QIcon(renderSvgImage(makeSyncthingIcon(settings.disconnectedColor, StatusEmblem::None), settings.renderSize)))
, idling(QIcon(renderSvgImage(makeSyncthingIcon(settings.idleColor, StatusEmblem::None), settings.renderSize)))
, scanninig(QIcon(renderSvgImage(makeSyncthingIcon(settings.scanningColor, StatusEmblem::Scanning), settings.renderSize)))
, notify(QIcon(renderSvgImage(makeSyncthingIcon(settings.warningColor, StatusEmblem::Alert), settings.renderSize)))
, pause(QIcon(renderSvgImage(makeSyncthingIcon(settings.pausedColor, StatusEmblem::Paused), settings.renderSize)))
, sync(QIcon(renderSvgImage(makeSyncthingIcon(settings.synchronizingColor, StatusEmblem::Synchronizing), settings.renderSize)))
, syncComplete(QIcon(renderSvgImage(makeSyncthingIcon(settings.defaultColor, StatusEmblem::Complete), settings.renderSize)))
, error(QIcon(renderSvgImage(makeSyncthingIcon(settings.errorColor, StatusEmblem::Alert), settings.renderSize)))
, errorSync(QIcon(renderSvgImage(makeSyncthingIcon(settings.errorColor, StatusEmblem::Synchronizing), settings.renderSize)))
, newItem(QIcon(renderSvgImage(makeSyncthingIcon(settings.defaultColor, StatusEmblem::Add), settings.renderSize)))
: disconnected(
QIcon(renderSvgImage(makeSyncthingIcon(settings.disconnectedColor, StatusEmblem::None, settings.strokeWidth), settings.renderSize)))
, idling(QIcon(renderSvgImage(makeSyncthingIcon(settings.idleColor, StatusEmblem::None, settings.strokeWidth), settings.renderSize)))
, scanninig(QIcon(renderSvgImage(makeSyncthingIcon(settings.scanningColor, StatusEmblem::Scanning, settings.strokeWidth), settings.renderSize)))
, notify(QIcon(renderSvgImage(makeSyncthingIcon(settings.warningColor, StatusEmblem::Alert, settings.strokeWidth), settings.renderSize)))
, pause(QIcon(renderSvgImage(makeSyncthingIcon(settings.pausedColor, StatusEmblem::Paused, settings.strokeWidth), settings.renderSize)))
, sync(QIcon(
renderSvgImage(makeSyncthingIcon(settings.synchronizingColor, StatusEmblem::Synchronizing, settings.strokeWidth), settings.renderSize)))
, syncComplete(QIcon(renderSvgImage(makeSyncthingIcon(settings.defaultColor, StatusEmblem::Complete, settings.strokeWidth), settings.renderSize)))
, error(QIcon(renderSvgImage(makeSyncthingIcon(settings.errorColor, StatusEmblem::Alert, settings.strokeWidth), settings.renderSize)))
, errorSync(QIcon(renderSvgImage(makeSyncthingIcon(settings.errorColor, StatusEmblem::Synchronizing, settings.strokeWidth), settings.renderSize)))
, newItem(QIcon(renderSvgImage(makeSyncthingIcon(settings.defaultColor, StatusEmblem::Add, settings.strokeWidth), settings.renderSize)))
, isValid(true)
{
}

View File

@ -28,6 +28,11 @@ enum class StatusEmblem {
Add,
};
enum class StatusIconStrokeWidth {
Normal,
Thick,
};
struct LIB_SYNCTHING_MODEL_EXPORT StatusIconColorSet {
StatusIconColorSet(const QColor &backgroundStart, const QColor &backgroundEnd, const QColor &foreground);
StatusIconColorSet(QColor &&backgroundStart, QColor &&backgroundEnd, QColor &&foreground);
@ -61,7 +66,7 @@ inline StatusIconColorSet::StatusIconColorSet(const QString &backgroundStart, co
LIB_SYNCTHING_MODEL_EXPORT QByteArray makeSyncthingIcon(
const StatusIconColorSet &colors = StatusIconColorSet{ QStringLiteral("#26B6DB"), QStringLiteral("#0882C8"), QStringLiteral("#FFFFFF") },
StatusEmblem statusEmblem = StatusEmblem::None);
StatusEmblem statusEmblem = StatusEmblem::None, StatusIconStrokeWidth strokeWidth = StatusIconStrokeWidth::Normal);
LIB_SYNCTHING_MODEL_EXPORT QPixmap renderSvgImage(const QString &path, const QSize &size = QSize(32, 32), int margin = 0);
LIB_SYNCTHING_MODEL_EXPORT QPixmap renderSvgImage(const QByteArray &contents, const QSize &size = QSize(32, 32), int margin = 0);
@ -83,6 +88,7 @@ struct LIB_SYNCTHING_MODEL_EXPORT StatusIconSettings {
StatusIconColorSet pausedColor;
StatusIconColorSet disconnectedColor;
QSize renderSize = QSize(32, 32);
StatusIconStrokeWidth strokeWidth = StatusIconStrokeWidth::Normal;
static constexpr auto distinguishableColorCount = 8;

View File

@ -75,7 +75,7 @@
<number>16</number>
</property>
<property name="value">
<number>32</number>
<number>16</number>
</property>
<property name="sliderPosition">
<number>16</number>
@ -100,6 +100,20 @@
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Stroke width</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="thickStrokeWidthCheckBox">
<property name="text">
<string>Thick</string>
</property>
</widget>
</item>
</layout>
</item>
<item>

View File

@ -354,6 +354,10 @@ void restore()
v.icons.tray = StatusIconSettings(settings.value(QStringLiteral("trayIcons")).toString());
v.icons.status.renderSize = settings.value(QStringLiteral("statusIconsRenderSize"), v.icons.status.renderSize).toSize();
v.icons.tray.renderSize = settings.value(QStringLiteral("trayIconsRenderSize"), v.icons.tray.renderSize).toSize();
v.icons.status.strokeWidth = static_cast<StatusIconStrokeWidth>(
settings.value(QStringLiteral("statusIconsStrokeWidth"), static_cast<int>(v.icons.status.strokeWidth)).toInt());
v.icons.tray.strokeWidth = static_cast<StatusIconStrokeWidth>(
settings.value(QStringLiteral("trayIconsStrokeWidth"), static_cast<int>(v.icons.tray.strokeWidth)).toInt());
v.icons.distinguishTrayIcons = settings.value(QStringLiteral("distinguishTrayIcons")).toBool();
v.icons.preferIconsFromTheme = settings.value(QStringLiteral("preferIconsFromTheme")).toBool();
settings.beginGroup(QStringLiteral("positioning"));
@ -472,6 +476,8 @@ void save()
settings.setValue(QStringLiteral("trayIcons"), v.icons.tray.toString());
settings.setValue(QStringLiteral("statusIconsRenderSize"), v.icons.status.renderSize);
settings.setValue(QStringLiteral("trayIconsRenderSize"), v.icons.tray.renderSize);
settings.setValue(QStringLiteral("statusIconsStrokeWidth"), static_cast<int>(v.icons.status.strokeWidth));
settings.setValue(QStringLiteral("trayIconsStrokeWidth"), static_cast<int>(v.icons.tray.strokeWidth));
settings.setValue(QStringLiteral("distinguishTrayIcons"), v.icons.distinguishTrayIcons);
settings.setValue(QStringLiteral("preferIconsFromTheme"), v.icons.preferIconsFromTheme);
settings.beginGroup(QStringLiteral("positioning"));

View File

@ -583,6 +583,10 @@ QWidget *IconsOptionPage::setupWidget()
break;
}
// allow changing stroke thickness
QObject::connect(ui()->thickStrokeWidthCheckBox, &QCheckBox::toggled, widget,
[this](bool thick) { m_settings.strokeWidth = thick ? StatusIconStrokeWidth::Thick : StatusIconStrokeWidth::Normal; });
// populate form for status icon colors
auto *const gridLayout = ui()->gridLayout;
auto *const statusIconsGroupBox = ui()->statusIconsGroupBox;
@ -606,19 +610,20 @@ QWidget *IconsOptionPage::setupWidget()
// setup preview
gridLayout->addWidget(widgetsForColor.previewLabel, index, 4, Qt::AlignCenter);
const auto updatePreview = [&widgetsForColor] {
const auto updatePreview = [this, &widgetsForColor] {
widgetsForColor.previewLabel->setPixmap(renderSvgImage(makeSyncthingIcon(
StatusIconColorSet{
widgetsForColor.colorButtons[0]->color(),
widgetsForColor.colorButtons[1]->color(),
widgetsForColor.colorButtons[2]->color(),
},
widgetsForColor.statusEmblem),
widgetsForColor.statusEmblem, m_settings.strokeWidth),
widgetsForColor.previewLabel->maximumSize()));
};
for (const auto &colorButton : widgetsForColor.colorButtons) {
QObject::connect(colorButton, &ColorButton::colorChanged, updatePreview);
QObject::connect(colorButton, &ColorButton::colorChanged, widget, updatePreview);
}
QObject::connect(ui()->thickStrokeWidthCheckBox, &QCheckBox::toggled, widget, updatePreview);
// setup color buttons
widgetsForColor.colorButtons[0]->setColor(colorMapping.setting.backgroundStart);
@ -635,27 +640,27 @@ QWidget *IconsOptionPage::setupWidget()
// setup presets menu
auto *const presetsMenu = new QMenu(widget);
presetsMenu->addAction(QCoreApplication::translate("QtGui::IconsOptionPageBase", "Colorful background with gradient (default)"), [this] {
presetsMenu->addAction(QCoreApplication::translate("QtGui::IconsOptionPageBase", "Colorful background with gradient (default)"), widget, [this] {
m_settings = Data::StatusIconSettings();
update();
});
presetsMenu->addAction(
QCoreApplication::translate("QtGui::IconsOptionPageBase", "Transparent background and dark foreground (for bright themes)"), [this] {
QCoreApplication::translate("QtGui::IconsOptionPageBase", "Transparent background and dark foreground (for bright themes)"), widget, [this] {
m_settings = Data::StatusIconSettings(Data::StatusIconSettings::BrightTheme{});
update();
});
presetsMenu->addAction(
QCoreApplication::translate("QtGui::IconsOptionPageBase", "Transparent background and bright foreground (for dark themes)"), [this] {
QCoreApplication::translate("QtGui::IconsOptionPageBase", "Transparent background and bright foreground (for dark themes)"), widget, [this] {
m_settings = Data::StatusIconSettings(Data::StatusIconSettings::DarkTheme{});
update();
});
// setup additional buttons
ui()->restoreDefaultsPushButton->setMenu(presetsMenu);
QObject::connect(ui()->restorePreviousPushButton, &QPushButton::clicked, [this] { reset(); });
QObject::connect(ui()->restorePreviousPushButton, &QPushButton::clicked, widget, [this] { reset(); });
// setup slider
QObject::connect(ui()->renderingSizeSlider, &QSlider::valueChanged, [this](int value) {
QObject::connect(ui()->renderingSizeSlider, &QSlider::valueChanged, widget, [this](int value) {
m_settings.renderSize = QSize(value, value);
auto *const label = ui()->renderingSizeLabel;
if (const auto scaleFactor = label->devicePixelRatioF(); scaleFactor == 1.0) {
@ -695,6 +700,7 @@ bool IconsOptionPage::apply()
void IconsOptionPage::update()
{
ui()->renderingSizeSlider->setValue(std::max(m_settings.renderSize.width(), m_settings.renderSize.height()));
ui()->thickStrokeWidthCheckBox->setChecked(m_settings.strokeWidth == StatusIconStrokeWidth::Thick);
for (auto &widgetsForColor : m_widgets) {
widgetsForColor.colorButtons[0]->setColor(widgetsForColor.setting->backgroundStart);
widgetsForColor.colorButtons[1]->setColor(widgetsForColor.setting->backgroundEnd);