feat: fontbuilder to streamline font styling
C++ / fmt (pull_request) Failing after 4s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 10s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 34s
Rust / build (pull_request) Successful in 1m10s
Python / verify (pull_request) Successful in 1m53s
Rust / fmt (pull_request) Successful in 34s
Rust / clippy (pull_request) Successful in 57s

This commit is contained in:
2026-09-21 15:37:45 +02:00
parent 84d5abcaab
commit 25bee293b0
134 changed files with 1813 additions and 1011 deletions
+4
View File
@@ -8,6 +8,8 @@ qml_module(ZShell-config
config.hpp config.cpp
migration.hpp migration.cpp
anim.hpp anim.cpp
fontbuilder.hpp fontbuilder.cpp
font.hpp font.cpp
tokens.hpp
appearance.hpp
background.hpp
@@ -28,4 +30,6 @@ qml_module(ZShell-config
services.hpp
sidebar.hpp
utilities.hpp
LIBRARIES
Qt::Gui
)
+343 -8
View File
@@ -1,19 +1,78 @@
#pragma once
#include "configobject.hpp"
#include <QFont>
#include <QVariantMap>
#include <limits>
#include <qqmlregistration.h>
namespace ZShell::config {
class AnimDurations : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CFG_PROPERTY(qreal, scale, 1)
Q_PROPERTY(int extraLarge READ extraLarge NOTIFY scaleChanged)
Q_PROPERTY(
int expressiveDefaultEffects READ expressiveDefaultEffects NOTIFY
scaleChanged)
Q_PROPERTY(
int expressiveDefaultSpatial READ expressiveDefaultSpatial NOTIFY
scaleChanged)
Q_PROPERTY(
int expressiveFastEffects READ expressiveFastEffects NOTIFY scaleChanged)
Q_PROPERTY(
int expressiveFastSpatial READ expressiveFastSpatial NOTIFY scaleChanged)
Q_PROPERTY(
int expressiveSlowEffects READ expressiveSlowEffects NOTIFY scaleChanged)
Q_PROPERTY(
int expressiveSlowSpatial READ expressiveSlowSpatial NOTIFY scaleChanged)
Q_PROPERTY(int large READ large NOTIFY scaleChanged)
Q_PROPERTY(int normal READ normal NOTIFY scaleChanged)
Q_PROPERTY(int small READ small NOTIFY scaleChanged)
public:
explicit AnimDurations(QObject* parent = nullptr) : ConfigObject(parent) {}
[[nodiscard]] int small() const { return static_cast<int>(200 * scale()); }
[[nodiscard]] int normal() const { return static_cast<int>(400 * scale()); }
[[nodiscard]] int large() const { return static_cast<int>(600 * scale()); }
[[nodiscard]] int extraLarge() const {
return static_cast<int>(1000 * scale());
}
[[nodiscard]] int expressiveFastSpatial() const {
return static_cast<int>(350 * scale());
}
[[nodiscard]] int expressiveDefaultSpatial() const {
return static_cast<int>(500 * scale());
}
[[nodiscard]] int expressiveSlowSpatial() const {
return static_cast<int>(650 * scale());
}
[[nodiscard]] int expressiveFastEffects() const {
return static_cast<int>(150 * scale());
}
[[nodiscard]] int expressiveDefaultEffects() const {
return static_cast<int>(200 * scale());
}
[[nodiscard]] int expressiveSlowEffects() const {
return static_cast<int>(300 * scale());
}
};
class Anim : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CFG_PROPERTY(int, mediaGifSpeedAdjustment, 300)
CFG_PROPERTY(qreal, sessionGifSpeed, 0.7)
CONFIG_SUBOBJECT(AnimDurations, durations)
public:
explicit Anim(QObject* parent = nullptr) : ConfigObject(parent) {}
explicit Anim(QObject* parent = nullptr)
: ConfigObject(parent), m_durations(new AnimDurations(this)) {}
};
class Deform : public ConfigObject {
@@ -39,15 +98,161 @@ class FontFamily : public ConfigObject {
explicit FontFamily(QObject* parent = nullptr) : ConfigObject(parent) {}
};
class Font : public ConfigObject {
namespace detail {
struct FontConfig {
QString family = {};
int size;
int weight = QFont::Normal;
bool italic = false;
QVariantMap vaxes = {{QStringLiteral("ROND"), 25}};
};
} // namespace detail
class FontSizeNode : public ConfigObject {
public:
explicit FontSizeNode(QObject* parent = nullptr) : ConfigObject(parent) {}
};
class FontStyleNode : public ConfigObject {
public:
explicit FontStyleNode(QObject* parent = nullptr) : ConfigObject(parent) {}
virtual FontSizeNode* largeNode() const = 0;
virtual FontSizeNode* mediumNode() const = 0;
virtual FontSizeNode* smallNode() const = 0;
virtual FontSizeNode* extraLargeNode() const { return nullptr; }
};
#define ARG(...) __VA_ARGS__
#define FONT(...) ZShell::config::detail::FontConfig __VA_ARGS__
#define FONT_CONFIG(Style, Size, props) \
class FontConfig##Style##Size : public FontSizeNode { \
Q_OBJECT QML_ANONYMOUS \
\
CFG_PROPERTY(QString, family, ARG(props).family) \
CFG_PROPERTY(int, size, ARG(props).size) \
CFG_PROPERTY(int, weight, ARG(props).weight) \
CFG_PROPERTY(bool, italic, ARG(props).italic) \
CFG_PROPERTY(QVariantMap, vaxes, ARG(props).vaxes) \
\
public \
: explicit FontConfig##Style##Size(QObject* parent = nullptr) \
: FontSizeNode(parent) {} \
};
#define FONT_STYLE(Style, family_, large_, medium_, small_) \
FONT_CONFIG(Style, Large, ARG(large_)) \
FONT_CONFIG(Style, Medium, ARG(medium_)) \
FONT_CONFIG(Style, Small, ARG(small_)) \
\
class FontStyle##Style : public FontStyleNode { \
Q_OBJECT QML_ANONYMOUS \
\
CFG_PROPERTY(QString, family, family_) \
CONFIG_SUBOBJECT(FontConfig##Style##Large, large) \
CONFIG_SUBOBJECT(FontConfig##Style##Medium, medium) \
CONFIG_SUBOBJECT(FontConfig##Style##Small, small) \
\
public \
: explicit FontStyle##Style(QObject* parent = nullptr) \
: FontStyleNode(parent) \
, m_large(new FontConfig##Style##Large(this)) \
, m_medium(new FontConfig##Style##Medium(this)) \
, m_small(new FontConfig##Style##Small(this)) {} \
\
FontSizeNode* largeNode() const override { return m_large; } \
FontSizeNode* mediumNode() const override { return m_medium; } \
FontSizeNode* smallNode() const override { return m_small; } \
};
// clang-format off
FONT_STYLE(Headline, QStringLiteral("GoogleSansFlex"),
FONT({ .size = 32, .weight = QFont::Medium }),
FONT({ .size = 28, .weight = QFont::Medium }),
FONT({ .size = 24, .weight = QFont::Medium })
)
FONT_STYLE(Title, QStringLiteral("GoogleSansFlex"),
FONT({ .size = 22, .weight = QFont::Medium }),
FONT({ .size = 16, .weight = QFont::Medium }),
FONT({ .size = 14, .weight = QFont::Medium })
)
FONT_STYLE(Body, QStringLiteral("GoogleSansFlex"),
FONT({ .size = 16 }),
FONT({ .size = 14 }),
FONT({ .size = 12 })
)
FONT_STYLE(Label, QStringLiteral("GoogleSansFlex"),
FONT({ .size = 14, .weight = QFont::Medium }),
FONT({ .size = 12, .weight = QFont::Medium }),
FONT({ .size = 11 })
)
FONT_STYLE(Mono, QStringLiteral("CaskaydiaCove NF"),
FONT({ .size = 16, .vaxes = {} }),
FONT({ .size = 14, .vaxes = {} }),
FONT({ .size = 12, .vaxes = {} })
)
// clang-format on
FONT_CONFIG(Icon, ExtraLarge, FONT({.size = static_cast<int>(48 / 1.33)}))
FONT_CONFIG(Icon, Large, FONT({.size = static_cast<int>(32 / 1.33)}))
FONT_CONFIG(Icon, Medium, FONT({.size = static_cast<int>(24 / 1.33)}))
FONT_CONFIG(Icon, Small, FONT({.size = static_cast<int>(20 / 1.33)}))
class FontStyleIcon : public FontStyleNode {
Q_OBJECT
QML_ANONYMOUS
CONFIG_SUBOBJECT(FontFamily, family)
CFG_PROPERTY(QString, family, QStringLiteral("Material Symbols Rounded"))
CONFIG_SUBOBJECT(FontConfigIconExtraLarge, extraLarge)
CONFIG_SUBOBJECT(FontConfigIconLarge, large)
CONFIG_SUBOBJECT(FontConfigIconMedium, medium)
CONFIG_SUBOBJECT(FontConfigIconSmall, small)
public:
explicit Font(QObject* parent = nullptr)
: ConfigObject(parent), m_family(new FontFamily(this)) {}
explicit FontStyleIcon(QObject* parent = nullptr)
: FontStyleNode(parent)
, m_extraLarge(new FontConfigIconExtraLarge(this))
, m_large(new FontConfigIconLarge(this))
, m_medium(new FontConfigIconMedium(this))
, m_small(new FontConfigIconSmall(this)) {}
FontSizeNode* extraLargeNode() const override { return m_extraLarge; }
FontSizeNode* largeNode() const override { return m_large; }
FontSizeNode* mediumNode() const override { return m_medium; }
FontSizeNode* smallNode() const override { return m_small; }
};
#undef ARG
#undef FONT
#undef FONT_CONFIG
#undef FONT_STYLE
class AppearanceFont : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CFG_PROPERTY(qreal, scale, 1)
CONFIG_SUBOBJECT(FontFamily, family)
CONFIG_SUBOBJECT(FontStyleHeadline, headline)
CONFIG_SUBOBJECT(FontStyleTitle, title)
CONFIG_SUBOBJECT(FontStyleBody, body)
CONFIG_SUBOBJECT(FontStyleLabel, label)
CONFIG_SUBOBJECT(FontStyleMono, mono)
CONFIG_SUBOBJECT(FontStyleIcon, icon)
CFG_PROPERTY(QString, clock, QStringLiteral("Rubik"))
CFG_PROPERTY(QString, workspaces, QStringLiteral("Rubik"))
public:
explicit AppearanceFont(QObject* parent = nullptr)
: ConfigObject(parent)
, m_family(new FontFamily(this))
, m_headline(new FontStyleHeadline(this))
, m_title(new FontStyleTitle(this))
, m_body(new FontStyleBody(this))
, m_label(new FontStyleLabel(this))
, m_mono(new FontStyleMono(this))
, m_icon(new FontStyleIcon(this)) {}
};
class Transparency : public ConfigObject {
@@ -62,13 +267,140 @@ class Transparency : public ConfigObject {
explicit Transparency(QObject* parent = nullptr) : ConfigObject(parent) {}
};
class AppearanceRounding : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CFG_PROPERTY(qreal, scale, 1)
Q_PROPERTY(int extraSmall READ extraSmall NOTIFY scaleChanged)
Q_PROPERTY(int small READ small NOTIFY scaleChanged)
Q_PROPERTY(int medium READ medium NOTIFY scaleChanged)
Q_PROPERTY(int large READ large NOTIFY scaleChanged)
Q_PROPERTY(int largeIncreased READ largeIncreased NOTIFY scaleChanged)
Q_PROPERTY(int extraLarge READ extraLarge NOTIFY scaleChanged)
Q_PROPERTY(
int extraLargeIncreased READ extraLargeIncreased NOTIFY scaleChanged)
Q_PROPERTY(int extraExtraLarge READ extraExtraLarge NOTIFY scaleChanged)
Q_PROPERTY(int full READ full CONSTANT)
public:
explicit AppearanceRounding(QObject* parent = nullptr)
: ConfigObject(parent) {}
[[nodiscard]] int extraSmall() const {
return static_cast<int>(4 * scale());
}
[[nodiscard]] int small() const { return static_cast<int>(8 * scale()); }
[[nodiscard]] int medium() const { return static_cast<int>(12 * scale()); }
[[nodiscard]] int large() const { return static_cast<int>(16 * scale()); }
[[nodiscard]] int largeIncreased() const {
return static_cast<int>(20 * scale());
}
[[nodiscard]] int extraLarge() const {
return static_cast<int>(28 * scale());
}
[[nodiscard]] int extraLargeIncreased() const {
return static_cast<int>(32 * scale());
}
[[nodiscard]] int extraExtraLarge() const {
return static_cast<int>(48 * scale());
}
[[nodiscard]] int full() const { return std::numeric_limits<int>::max(); }
};
class AppearanceSpacing : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CFG_PROPERTY(qreal, scale, 1)
Q_PROPERTY(int extraSmall READ extraSmall NOTIFY scaleChanged)
Q_PROPERTY(int small READ small NOTIFY scaleChanged)
Q_PROPERTY(int medium READ medium NOTIFY scaleChanged)
Q_PROPERTY(int large READ large NOTIFY scaleChanged)
Q_PROPERTY(int largeIncreased READ largeIncreased NOTIFY scaleChanged)
Q_PROPERTY(int extraLarge READ extraLarge NOTIFY scaleChanged)
Q_PROPERTY(
int extraLargeIncreased READ extraLargeIncreased NOTIFY scaleChanged)
Q_PROPERTY(int extraExtraLarge READ extraExtraLarge NOTIFY scaleChanged)
public:
explicit AppearanceSpacing(QObject* parent = nullptr)
: ConfigObject(parent) {}
[[nodiscard]] int extraSmall() const {
return static_cast<int>(4 * scale());
}
[[nodiscard]] int small() const { return static_cast<int>(8 * scale()); }
[[nodiscard]] int medium() const { return static_cast<int>(12 * scale()); }
[[nodiscard]] int large() const { return static_cast<int>(16 * scale()); }
[[nodiscard]] int largeIncreased() const {
return static_cast<int>(20 * scale());
}
[[nodiscard]] int extraLarge() const {
return static_cast<int>(28 * scale());
}
[[nodiscard]] int extraLargeIncreased() const {
return static_cast<int>(32 * scale());
}
[[nodiscard]] int extraExtraLarge() const {
return static_cast<int>(48 * scale());
}
};
class AppearancePadding : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CFG_PROPERTY(qreal, scale, 1)
Q_PROPERTY(int extraSmall READ extraSmall NOTIFY scaleChanged)
Q_PROPERTY(int smaller READ smaller NOTIFY scaleChanged)
Q_PROPERTY(int small READ small NOTIFY scaleChanged)
Q_PROPERTY(int medium READ medium NOTIFY scaleChanged)
Q_PROPERTY(int large READ large NOTIFY scaleChanged)
Q_PROPERTY(int largeIncreased READ largeIncreased NOTIFY scaleChanged)
Q_PROPERTY(int extraLarge READ extraLarge NOTIFY scaleChanged)
Q_PROPERTY(
int extraLargeIncreased READ extraLargeIncreased NOTIFY scaleChanged)
Q_PROPERTY(int extraExtraLarge READ extraExtraLarge NOTIFY scaleChanged)
public:
explicit AppearancePadding(QObject* parent = nullptr)
: ConfigObject(parent) {}
[[nodiscard]] int extraSmall() const {
return static_cast<int>(4 * scale());
}
[[nodiscard]] int smaller() const { return static_cast<int>(5 * scale()); }
[[nodiscard]] int small() const { return static_cast<int>(8 * scale()); }
[[nodiscard]] int medium() const { return static_cast<int>(12 * scale()); }
[[nodiscard]] int large() const { return static_cast<int>(16 * scale()); }
[[nodiscard]] int largeIncreased() const {
return static_cast<int>(20 * scale());
}
[[nodiscard]] int extraLarge() const {
return static_cast<int>(28 * scale());
}
[[nodiscard]] int extraLargeIncreased() const {
return static_cast<int>(32 * scale());
}
[[nodiscard]] int extraExtraLarge() const {
return static_cast<int>(48 * scale());
}
};
class Appearance : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CONFIG_SUBOBJECT(Anim, anim)
CONFIG_SUBOBJECT(Deform, deform)
CONFIG_SUBOBJECT(Font, font)
CONFIG_SUBOBJECT(AppearanceFont, font)
CONFIG_SUBOBJECT(AppearanceRounding, rounding)
CONFIG_SUBOBJECT(AppearanceSpacing, spacing)
CONFIG_SUBOBJECT(AppearancePadding, padding)
CONFIG_SUBOBJECT(Transparency, transparency)
public:
@@ -76,8 +408,11 @@ class Appearance : public ConfigObject {
: ConfigObject(parent)
, m_anim(new Anim(this))
, m_deform(new Deform(this))
, m_font(new Font(this))
, m_transparency(new Transparency(this)) {}
, m_font(new AppearanceFont(this))
, m_rounding(new AppearanceRounding(this))
, m_spacing(new AppearanceSpacing(this))
, m_padding(new AppearancePadding(this))
, m_transparency(new Transparency(this)) {};
};
} // namespace ZShell::config
+4 -12
View File
@@ -65,13 +65,12 @@ void ConfigObject::loadFromJson(const QJsonValue& json) {
auto prop = meta->property(i);
const auto key = QString::fromUtf8(prop.name());
known.insert(key);
if (!obj.contains(key)) continue;
const auto jsonVal = obj.value(key);
if (prop.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
known.insert(key);
if (auto* const node = prop.read(this).value<ConfigNode*>()) {
node->loadFromJson(jsonVal);
continue;
@@ -80,6 +79,8 @@ void ConfigObject::loadFromJson(const QJsonValue& json) {
if (!prop.isWritable()) continue;
known.insert(key);
if (!jsonValueMatchesType(jsonVal, prop.metaType())) {
qWarning() << "Config: type mismatch for" << key << "in"
<< meta->className() << "- expected"
@@ -218,18 +219,9 @@ void ConfigObject::materializeDefaults() {
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
const auto prop = meta->property(i);
const auto key = QString::fromUtf8(prop.name());
if (auto* const node = prop.read(this).value<ConfigNode*>()) {
if (auto* const node = prop.read(this).value<ConfigNode*>())
node->materializeDefaults();
continue;
}
if (!prop.isWritable()) continue;
if (m_global) continue;
m_loadedKeys.insert(key);
}
}
+290
View File
@@ -0,0 +1,290 @@
#include "font.hpp"
#include "appearance.hpp"
#include "config.hpp"
namespace ZShell::config {
FontStyleBase::FontStyleBase(QObject* parent) : QObject(parent) {}
QFont FontStyleBase::large() const {
return m_large;
}
QFont FontStyleBase::medium() const {
return m_medium;
}
QFont FontStyleBase::small() const {
return m_small;
}
QFont FontStyleBase::buildFont(
const FontSizeNode* cfg, const QString& fallbackFamily, qreal scale) {
const auto family = cfg->property("family").toString();
const auto size = cfg->property("size").toInt();
const auto weight = cfg->property("weight").value<QFont::Weight>();
const auto italic = cfg->property("italic").toBool();
const auto vaxes = cfg->property("vaxes").toMap();
QFont font;
font.setFamily(family.isEmpty() ? fallbackFamily : family);
const int scaledSize = static_cast<int>(size * scale);
const int cappedSize = scaledSize > 0 ? scaledSize : 1;
font.setPointSize(cappedSize);
font.setVariableAxis("opsz", static_cast<float>(cappedSize));
font.setWeight(weight);
font.setVariableAxis("wght", static_cast<float>(weight));
font.setItalic(italic);
for (auto it = vaxes.constBegin(); it != vaxes.constEnd(); ++it) {
if (auto tag = QFont::Tag::fromString(it.key()))
font.setVariableAxis(*tag, it.value().toFloat());
}
return font;
}
void FontStyleBase::bind(FontStyleNode* cfg) {
if (m_cfg == cfg) return;
if (m_cfg) {
disconnect(m_cfg, nullptr, this, nullptr);
disconnect(m_cfg->largeNode(), nullptr, this, nullptr);
disconnect(m_cfg->mediumNode(), nullptr, this, nullptr);
disconnect(m_cfg->smallNode(), nullptr, this, nullptr);
}
m_cfg = cfg;
if (cfg) {
connect(
cfg, &ConfigNode::propertiesChanged, this, &FontStyleBase::rebuild);
connect(
cfg->largeNode(),
&ConfigNode::propertiesChanged,
this,
&FontStyleBase::rebuild);
connect(
cfg->mediumNode(),
&ConfigNode::propertiesChanged,
this,
&FontStyleBase::rebuild);
connect(
cfg->smallNode(),
&ConfigNode::propertiesChanged,
this,
&FontStyleBase::rebuild);
}
rebuild();
}
void FontStyleBase::rebuild() {
if (m_cfg) {
const auto family = m_cfg->property("family").toString();
m_large = buildFont(m_cfg->largeNode(), family, m_scale);
m_medium = buildFont(m_cfg->mediumNode(), family, m_scale);
m_small = buildFont(m_cfg->smallNode(), family, m_scale);
} else {
m_large = QFont();
m_medium = QFont();
m_small = QFont();
}
emit fontsChanged();
}
void FontStyleBase::setScale(qreal scale) {
if (qFuzzyCompare(m_scale + 1.0, scale + 1.0)) return;
m_scale = scale;
rebuild();
}
FontStyle::FontStyle(QObject* parent)
: FontStyleBase(parent), m_builders(new FontBuilders(this, this)) {}
FontBuilders* FontStyle::builders() const {
return m_builders;
}
IconFontStyle::IconFontStyle(QObject* parent)
: FontStyleBase(parent), m_builders(new IconFontBuilders(this, this)) {}
FontBuilder IconFontStyle::size(int pointSize) {
return FontBuilder(m_small).size(pointSize);
}
FontBuilder IconFontStyle::pixelSize(int pixelSize) {
return FontBuilder(m_small).pixelSize(pixelSize);
}
void IconFontStyle::bind(FontStyleNode* cfg) {
if (m_cfg == cfg) return;
if (m_cfg) disconnect(m_cfg->extraLargeNode(), nullptr, this, nullptr);
FontStyleBase::bind(cfg);
if (cfg)
connect(
cfg->extraLargeNode(),
&ConfigNode::propertiesChanged,
this,
&IconFontStyle::rebuild);
}
QFont IconFontStyle::extraLarge() const {
return m_extraLarge;
}
IconFontBuilders* IconFontStyle::builders() const {
return m_builders;
}
void IconFontStyle::rebuild() {
if (m_cfg) {
const auto family = m_cfg->property("family").toString();
m_extraLarge = buildFont(m_cfg->extraLargeNode(), family, m_scale);
} else {
m_extraLarge = QFont();
}
FontStyleBase::rebuild();
}
FontBuilders::FontBuilders(const FontStyleBase* style, QObject* parent)
: QObject(parent), m_style(style) {
connect(
style,
&FontStyleBase::fontsChanged,
this,
&FontBuilders::buildersChanged);
}
FontBuilder FontBuilders::large() const {
return FontBuilder(m_style->large());
}
FontBuilder FontBuilders::medium() const {
return FontBuilder(m_style->medium());
}
FontBuilder FontBuilders::small() const {
return FontBuilder(m_style->small());
}
IconFontBuilders::IconFontBuilders(const IconFontStyle* style, QObject* parent)
: FontBuilders(style, parent) {}
FontBuilder IconFontBuilders::extraLarge() const {
return FontBuilder(
static_cast<const IconFontStyle*>(m_style)->extraLarge());
}
FontTokens::FontTokens(QObject* parent)
: ConfigObject(parent)
, m_size(new FontSizeTokens(this))
, m_headline(new FontStyle(this))
, m_title(new FontStyle(this))
, m_body(new FontStyle(this))
, m_label(new FontStyle(this))
, m_mono(new FontStyle(this))
, m_icon(new IconFontStyle(this)) {
if (!Config::instance()) new Config();
bindFont(Config::instance()->appearance()->font());
}
FontStyle* FontTokens::headline() const {
return m_headline;
}
FontStyle* FontTokens::title() const {
return m_title;
}
FontStyle* FontTokens::body() const {
return m_body;
}
FontStyle* FontTokens::label() const {
return m_label;
}
FontStyle* FontTokens::mono() const {
return m_mono;
}
IconFontStyle* FontTokens::icon() const {
return m_icon;
}
FontBuilder FontTokens::clock() const {
return FontBuilder(m_clock);
}
QString FontTokens::workspaces() const {
return m_font ? m_font->workspaces() : QString();
}
void FontTokens::bindFont(AppearanceFont* font) {
if (m_font == font) return;
if (m_font) disconnect(m_font, nullptr, this, nullptr);
m_font = font;
if (font) {
rebuildScale();
m_headline->bind(font->headline());
m_title->bind(font->title());
m_body->bind(font->body());
m_label->bind(font->label());
m_mono->bind(font->mono());
m_icon->bind(font->icon());
connect(
font,
&AppearanceFont::clockChanged,
this,
&FontTokens::rebuildClock);
connect(
font,
&AppearanceFont::scaleChanged,
this,
&FontTokens::rebuildScale);
connect(
font,
&AppearanceFont::workspacesChanged,
this,
&FontTokens::workspacesChanged);
} else {
rebuildScale();
m_headline->bind(nullptr);
m_title->bind(nullptr);
m_body->bind(nullptr);
m_label->bind(nullptr);
m_mono->bind(nullptr);
m_icon->bind(nullptr);
}
rebuildClock();
emit workspacesChanged();
}
void FontTokens::rebuildScale() {
const qreal s = m_font ? m_font->scale() : 1;
m_headline->setScale(s);
m_title->setScale(s);
m_body->setScale(s);
m_label->setScale(s);
m_mono->setScale(s);
m_icon->setScale(s);
}
void FontTokens::rebuildClock() {
QFont f;
if (m_font) f.setFamily(m_font->clock());
m_clock = f;
emit clockChanged();
}
} // namespace ZShell::config
+200
View File
@@ -0,0 +1,200 @@
#pragma once
#include "configobject.hpp"
#include "fontbuilder.hpp"
#include <qfont.h>
#include <qobject.h>
#include <qqmlintegration.h>
#include <qvariantmap.h>
namespace ZShell::config {
class AppearanceFont;
class FontBuilders;
class FontStyleBase;
class FontSizeNode;
class FontStyleNode;
class IconFontStyle;
class FontSizeTokens : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CFG_PROPERTY(int, small, 11)
CFG_PROPERTY(int, smaller, 12)
CFG_PROPERTY(int, normal, 13)
CFG_PROPERTY(int, larger, 15)
CFG_PROPERTY(int, large, 18)
CFG_PROPERTY(int, extraLarge, 28)
public:
explicit FontSizeTokens(QObject* parent = nullptr) : ConfigObject(parent) {}
};
class FontBuilders : public QObject {
Q_OBJECT
QML_ANONYMOUS
Q_PROPERTY(
ZShell::config::FontBuilder large READ large NOTIFY buildersChanged
FINAL)
Q_PROPERTY(
ZShell::config::FontBuilder medium READ medium NOTIFY buildersChanged
FINAL)
Q_PROPERTY(
ZShell::config::FontBuilder small READ small NOTIFY buildersChanged
FINAL)
public:
explicit FontBuilders(const FontStyleBase* style, QObject* parent = nullptr);
[[nodiscard]] FontBuilder large() const;
[[nodiscard]] FontBuilder medium() const;
[[nodiscard]] FontBuilder small() const;
signals:
void buildersChanged();
protected:
const FontStyleBase* m_style;
};
class IconFontBuilders : public FontBuilders {
Q_OBJECT
QML_ANONYMOUS
Q_PROPERTY(
ZShell::config::FontBuilder extraLarge READ extraLarge NOTIFY
buildersChanged FINAL)
public:
explicit IconFontBuilders(
const IconFontStyle* style, QObject* parent = nullptr);
[[nodiscard]] FontBuilder extraLarge() const;
};
class FontStyleBase : public QObject {
Q_OBJECT
Q_PROPERTY(QFont large READ large NOTIFY fontsChanged FINAL)
Q_PROPERTY(QFont medium READ medium NOTIFY fontsChanged FINAL)
Q_PROPERTY(QFont small READ small NOTIFY fontsChanged FINAL)
public:
explicit FontStyleBase(QObject* parent = nullptr);
virtual void bind(FontStyleNode* cfg);
void setScale(qreal scale);
[[nodiscard]] QFont large() const;
[[nodiscard]] QFont medium() const;
[[nodiscard]] QFont small() const;
signals:
void fontsChanged();
protected:
virtual void rebuild();
static QFont buildFont(
const FontSizeNode* cfg, const QString& fallbackFamily, qreal scale);
FontStyleNode* m_cfg = nullptr;
qreal m_scale = 1;
QFont m_large;
QFont m_medium;
QFont m_small;
};
class FontStyle : public FontStyleBase {
Q_OBJECT
QML_ANONYMOUS
Q_PROPERTY(FontBuilders* builders READ builders CONSTANT FINAL)
public:
explicit FontStyle(QObject* parent = nullptr);
[[nodiscard]] FontBuilders* builders() const;
private:
FontBuilders* m_builders;
};
class IconFontStyle : public FontStyleBase {
Q_OBJECT
QML_ANONYMOUS
Q_PROPERTY(QFont extraLarge READ extraLarge NOTIFY fontsChanged FINAL)
Q_PROPERTY(IconFontBuilders* builders READ builders CONSTANT FINAL)
public:
explicit IconFontStyle(QObject* parent = nullptr);
Q_INVOKABLE FontBuilder size(int pointSize);
Q_INVOKABLE FontBuilder pixelSize(int pixelSize);
void bind(FontStyleNode* cfg) override;
[[nodiscard]] QFont extraLarge() const;
[[nodiscard]] IconFontBuilders* builders() const;
protected:
void rebuild() override;
private:
QFont m_extraLarge;
IconFontBuilders* m_builders;
};
class FontTokens : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CONFIG_SUBOBJECT(FontSizeTokens, size)
Q_PROPERTY(FontStyle* headline READ headline CONSTANT FINAL)
Q_PROPERTY(FontStyle* title READ title CONSTANT FINAL)
Q_PROPERTY(FontStyle* body READ body CONSTANT FINAL)
Q_PROPERTY(FontStyle* label READ label CONSTANT FINAL)
Q_PROPERTY(FontStyle* mono READ mono CONSTANT FINAL)
Q_PROPERTY(IconFontStyle* icon READ icon CONSTANT FINAL)
Q_PROPERTY(
ZShell::config::FontBuilder clock READ clock NOTIFY clockChanged FINAL)
Q_PROPERTY(QString workspaces READ workspaces NOTIFY workspacesChanged FINAL)
public:
explicit FontTokens(QObject* parent = nullptr);
void bindFont(AppearanceFont* font);
[[nodiscard]] FontStyle* headline() const;
[[nodiscard]] FontStyle* title() const;
[[nodiscard]] FontStyle* body() const;
[[nodiscard]] FontStyle* label() const;
[[nodiscard]] FontStyle* mono() const;
[[nodiscard]] IconFontStyle* icon() const;
[[nodiscard]] FontBuilder clock() const;
[[nodiscard]] QString workspaces() const;
signals:
void clockChanged();
void workspacesChanged();
private:
void rebuildClock();
void rebuildScale();
AppearanceFont* m_font = nullptr;
FontStyle* m_headline;
FontStyle* m_title;
FontStyle* m_body;
FontStyle* m_label;
FontStyle* m_mono;
IconFontStyle* m_icon;
QFont m_clock;
};
} // namespace ZShell::config
+105
View File
@@ -0,0 +1,105 @@
#include "fontbuilder.hpp"
#include <qloggingcategory.h>
namespace {
Q_LOGGING_CATEGORY(lcFontBuilder, "zshell.fontbuilder", QtInfoMsg)
} // namespace
namespace ZShell::config {
FontBuilder::FontBuilder(const QFont& font) : m_font(font) {}
FontBuilder FontBuilder::family(const QString& family) {
m_font.setFamily(family);
return *this;
}
FontBuilder FontBuilder::size(int pointSize) {
if (pointSize <= 0) pointSize = 1;
m_font.setPointSize(pointSize);
m_font.setVariableAxis("opsz", static_cast<float>(pointSize));
return *this;
}
FontBuilder FontBuilder::pixelSize(int pixelSize) {
if (pixelSize <= 0) pixelSize = 1;
m_font.setPixelSize(pixelSize);
return *this;
}
FontBuilder FontBuilder::weight(QFont::Weight weight) {
m_font.setWeight(weight);
m_font.setVariableAxis("wght", static_cast<float>(weight));
return *this;
}
FontBuilder FontBuilder::italic(bool on) {
m_font.setItalic(on);
return *this;
}
FontBuilder FontBuilder::stretch(int stretch) {
m_font.setStretch(stretch);
return *this;
}
FontBuilder FontBuilder::letterSpacing(qreal spacing, bool absolute) {
m_font.setLetterSpacing(
absolute ? QFont::AbsoluteSpacing : QFont::PercentageSpacing, spacing);
return *this;
}
FontBuilder FontBuilder::capitalisation(QFont::Capitalization cap) {
m_font.setCapitalization(cap);
return *this;
}
FontBuilder FontBuilder::vaxis(const QString& tag, float value) {
if (auto t = QFont::Tag::fromString(tag))
m_font.setVariableAxis(*t, value);
else
qCWarning(lcFontBuilder)
<< "Unable to convert tag" << tag << "to QFont::Tag";
return *this;
}
FontBuilder FontBuilder::vaxes(const QVariantMap& axes) {
for (auto it = axes.constBegin(); it != axes.constEnd(); ++it) {
if (it.value().canConvert<float>()) {
if (auto tag = QFont::Tag::fromString(it.key()))
m_font.setVariableAxis(*tag, it.value().toFloat());
else
qCWarning(lcFontBuilder)
<< "Unable to convert tag" << it.key() << "to QFont::Tag";
} else {
qCWarning(lcFontBuilder)
<< "Unable to convert value" << it.value() << "to float";
}
}
return *this;
}
QFont FontBuilder::build() const {
return m_font;
}
FontBuilder FontBuilder::fill(float value) {
return vaxis(QStringLiteral("FILL"), value);
}
FontBuilder FontBuilder::grade(float value) {
return vaxis(QStringLiteral("GRAD"), value);
}
FontBuilder FontBuilder::width(float value) {
return vaxis(QStringLiteral("wdth"), value);
}
FontBuilder FontBuilder::scale(qreal factor) {
return size(static_cast<int>(m_font.pointSize() * factor));
}
} // namespace ZShell::config
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <qfont.h>
#include <qqmlintegration.h>
#include <qvariant.h>
namespace ZShell::config {
class FontBuilder {
Q_GADGET
QML_ANONYMOUS
public:
FontBuilder() = default;
explicit FontBuilder(const QFont& font);
[[nodiscard]] Q_INVOKABLE FontBuilder family(const QString& family);
[[nodiscard]] Q_INVOKABLE FontBuilder size(int pointSize);
[[nodiscard]] Q_INVOKABLE FontBuilder pixelSize(int pixelSize);
[[nodiscard]] Q_INVOKABLE FontBuilder weight(QFont::Weight weight);
[[nodiscard]] Q_INVOKABLE FontBuilder italic(bool on = true);
[[nodiscard]] Q_INVOKABLE FontBuilder stretch(int stretch);
[[nodiscard]] Q_INVOKABLE FontBuilder
letterSpacing(qreal spacing, bool absolute = true);
[[nodiscard]] Q_INVOKABLE FontBuilder
capitalisation(QFont::Capitalization cap);
[[nodiscard]] Q_INVOKABLE FontBuilder vaxis(const QString& tag, float value);
[[nodiscard]] Q_INVOKABLE FontBuilder vaxes(const QVariantMap& axes);
[[nodiscard]] Q_INVOKABLE QFont build() const;
[[nodiscard]] Q_INVOKABLE FontBuilder fill(float value);
[[nodiscard]] Q_INVOKABLE FontBuilder grade(float value);
[[nodiscard]] Q_INVOKABLE FontBuilder width(float value);
[[nodiscard]] Q_INVOKABLE FontBuilder scale(qreal factor);
private:
QFont m_font;
};
} // namespace ZShell::config
+25 -112
View File
@@ -2,6 +2,9 @@
#include "configobject.hpp"
#include "anim.hpp"
#include "appearance.hpp"
#include "config.hpp"
#include "font.hpp"
#include <QVariantList>
#include <limits>
@@ -13,89 +16,6 @@
namespace ZShell::config {
class AppearanceRounding : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CFG_PROPERTY(qreal, extraSmall, 4)
CFG_PROPERTY(qreal, full, std::numeric_limits<int>::max())
CFG_PROPERTY(qreal, large, 16)
CFG_PROPERTY(qreal, largeIncreased, 20)
CFG_PROPERTY(qreal, extraLarge, 28)
CFG_PROPERTY(qreal, extraLargeIncreased, 32)
CFG_PROPERTY(qreal, extraExtraLarge, 48)
CFG_PROPERTY(qreal, medium, 12)
CFG_PROPERTY(qreal, scale, 1)
CFG_PROPERTY(qreal, small, 8)
public:
explicit AppearanceRounding(QObject* parent = nullptr)
: ConfigObject(parent) {}
};
class AppearancePadding : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CFG_PROPERTY(int, extraSmall, 4)
CFG_PROPERTY(int, smaller, 5)
CFG_PROPERTY(int, small, 8)
CFG_PROPERTY(int, medium, 12)
CFG_PROPERTY(int, large, 16)
CFG_PROPERTY(int, largeIncreased, 20)
CFG_PROPERTY(int, extraLarge, 28)
CFG_PROPERTY(int, extraLargeIncreased, 32)
CFG_PROPERTY(int, extraExtraLarge, 48)
public:
explicit AppearancePadding(QObject* parent = nullptr)
: ConfigObject(parent) {}
};
class AppearanceSpacing : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CFG_PROPERTY(int, extraSmall, 4)
CFG_PROPERTY(int, small, 8)
CFG_PROPERTY(int, medium, 12)
CFG_PROPERTY(int, large, 16)
CFG_PROPERTY(int, largeIncreased, 20)
CFG_PROPERTY(int, extraLarge, 28)
CFG_PROPERTY(int, extraLargeIncreased, 32)
CFG_PROPERTY(int, extraExtraLarge, 48)
public:
explicit AppearanceSpacing(QObject* parent = nullptr)
: ConfigObject(parent) {}
};
class FontSizeTokens : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CFG_PROPERTY(int, small, 11)
CFG_PROPERTY(int, smaller, 12)
CFG_PROPERTY(int, normal, 13)
CFG_PROPERTY(int, larger, 15)
CFG_PROPERTY(int, large, 18)
CFG_PROPERTY(int, extraLarge, 28)
public:
explicit FontSizeTokens(QObject* parent = nullptr) : ConfigObject(parent) {}
};
class FontTokens : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CONFIG_SUBOBJECT(FontSizeTokens, size)
public:
explicit FontTokens(QObject* parent = nullptr)
: ConfigObject(parent), m_size(new FontSizeTokens(this)) {}
};
class AnimCurves : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
@@ -142,31 +62,11 @@ class AnimCurves : public ConfigObject {
, m_expressiveSlowEffects({0.34, 0.88, 0.34, 1, 1, 1}) {}
};
class AnimDurations : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CFG_PROPERTY(int, small, 200)
CFG_PROPERTY(int, scale, 1)
CFG_PROPERTY(int, normal, 400)
CFG_PROPERTY(int, large, 600)
CFG_PROPERTY(int, extraLarge, 1000)
CFG_PROPERTY(int, expressiveFastSpatial, 350)
CFG_PROPERTY(int, expressiveDefaultSpatial, 500)
CFG_PROPERTY(int, expressiveSlowSpatial, 650)
CFG_PROPERTY(int, expressiveFastEffects, 150)
CFG_PROPERTY(int, expressiveDefaultEffects, 200)
CFG_PROPERTY(int, expressiveSlowEffects, 300)
public:
explicit AnimDurations(QObject* parent = nullptr) : ConfigObject(parent) {}
};
class AnimTokens : public ConfigObject {
Q_OBJECT
QML_ANONYMOUS
CONFIG_SUBOBJECT(AnimDurations, durations)
Q_PROPERTY(ZShell::config::AnimDurations* durations READ durations CONSTANT)
Q_PROPERTY(QEasingCurve emphasized READ emphasized NOTIFY curvesChanged)
Q_PROPERTY(
@@ -201,7 +101,6 @@ class AnimTokens : public ConfigObject {
explicit AnimTokens(QObject* parent = nullptr)
: ConfigObject(parent)
, m_curves(new AnimCurves(this))
, m_durations(new AnimDurations(this))
, m_easing(new AnimEasing(this)) {
connect(
m_easing,
@@ -211,6 +110,10 @@ class AnimTokens : public ConfigObject {
m_easing->bindCurves(m_curves);
}
[[nodiscard]] AnimDurations* durations() const {
return Config::instance()->appearance()->anim()->durations();
}
[[nodiscard]] QEasingCurve emphasized() const {
return m_easing->emphasized();
}
@@ -269,9 +172,10 @@ class Tokens : public ConfigObject {
QML_ELEMENT
QML_SINGLETON
CONFIG_SUBOBJECT(AppearanceRounding, rounding)
CONFIG_SUBOBJECT(AppearancePadding, padding)
CONFIG_SUBOBJECT(AppearanceSpacing, spacing)
Q_PROPERTY(
ZShell::config::AppearanceRounding* rounding READ rounding CONSTANT)
Q_PROPERTY(ZShell::config::AppearancePadding* padding READ padding CONSTANT)
Q_PROPERTY(ZShell::config::AppearanceSpacing* spacing READ spacing CONSTANT)
CONFIG_SUBOBJECT(FontTokens, font)
CONFIG_SUBOBJECT(AnimTokens, anim)
CONFIG_SUBOBJECT(BarTokens, bar)
@@ -279,12 +183,21 @@ class Tokens : public ConfigObject {
public:
explicit Tokens(QObject* parent = nullptr)
: ConfigObject(parent)
, m_rounding(new AppearanceRounding(this))
, m_padding(new AppearancePadding(this))
, m_spacing(new AppearanceSpacing(this))
, m_font(new FontTokens(this))
, m_anim(new AnimTokens(this))
, m_bar(new BarTokens(this)) {}
, m_bar(new BarTokens(this)) {
if (!Config::instance()) new Config();
}
[[nodiscard]] AppearanceRounding* rounding() const {
return Config::instance()->appearance()->rounding();
}
[[nodiscard]] AppearancePadding* padding() const {
return Config::instance()->appearance()->padding();
}
[[nodiscard]] AppearanceSpacing* spacing() const {
return Config::instance()->appearance()->spacing();
}
static Tokens* create(QQmlEngine*, QJSEngine*) { return new Tokens(); }
};