Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 3s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 10s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Successful in 1m40s
Rust / fmt (pull_request) Successful in 27s
Rust / clippy (pull_request) Successful in 46s
C++ / fmt (pull_request) Failing after 3s
C++ / build (pull_request) Failing after 9s
C++ / clang-tidy (pull_request) Failing after 9s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 10s
Python / static (pull_request) Successful in 28s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Successful in 1m40s
Rust / fmt (pull_request) Successful in 27s
Rust / clippy (pull_request) Successful in 46s
This commit is contained in:
@@ -1,19 +1,27 @@
|
||||
qml_module(ZShell-config
|
||||
URI ZShell.Config
|
||||
INCLUDE_PREFIX config
|
||||
DEPENDENCIES
|
||||
ZShell.Settings
|
||||
SOURCES
|
||||
confignode.hpp confignode.cpp
|
||||
configobject.hpp configobject.cpp
|
||||
configlist.hpp configlist.cpp
|
||||
config.hpp config.cpp
|
||||
migration.hpp migration.cpp
|
||||
anim.hpp anim.cpp
|
||||
fontbuilder.hpp fontbuilder.cpp
|
||||
font.hpp font.cpp
|
||||
tokens.hpp
|
||||
appearance.hpp
|
||||
appearance.hpp appearance.cpp
|
||||
background.hpp
|
||||
bar.hpp
|
||||
clipboard.hpp
|
||||
colors.hpp
|
||||
dashboard.hpp
|
||||
display.hpp
|
||||
dock.hpp
|
||||
enums.hpp
|
||||
general.hpp
|
||||
launcher.hpp
|
||||
llm.hpp
|
||||
@@ -25,3 +33,10 @@ qml_module(ZShell-config
|
||||
sidebar.hpp
|
||||
utilities.hpp
|
||||
)
|
||||
|
||||
target_link_libraries(ZShell-config PUBLIC
|
||||
Qt::Gui
|
||||
zshell-util
|
||||
ZShell
|
||||
ZShell-settings
|
||||
)
|
||||
|
||||
@@ -1,32 +1,82 @@
|
||||
#include "anim.hpp"
|
||||
#include "tokens.hpp"
|
||||
|
||||
#include <qpoint.h>
|
||||
|
||||
#include "appearance.hpp"
|
||||
#include "tokens.hpp"
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
QEasingCurve AnimEasing::buildCurve(const QList<qreal>& points) {
|
||||
AnimTokens::AnimTokens(QObject* parent) : QObject(parent) {}
|
||||
|
||||
QEasingCurve AnimTokens::emphasized() const {
|
||||
return m_emphasized;
|
||||
}
|
||||
|
||||
QEasingCurve AnimTokens::emphasizedAccel() const {
|
||||
return m_emphasizedAccel;
|
||||
}
|
||||
|
||||
QEasingCurve AnimTokens::emphasizedDecel() const {
|
||||
return m_emphasizedDecel;
|
||||
}
|
||||
|
||||
QEasingCurve AnimTokens::standard() const {
|
||||
return m_standard;
|
||||
}
|
||||
|
||||
QEasingCurve AnimTokens::standardAccel() const {
|
||||
return m_standardAccel;
|
||||
}
|
||||
|
||||
QEasingCurve AnimTokens::standardDecel() const {
|
||||
return m_standardDecel;
|
||||
}
|
||||
|
||||
QEasingCurve AnimTokens::expressiveFastSpatial() const {
|
||||
return m_expressiveFastSpatial;
|
||||
}
|
||||
|
||||
QEasingCurve AnimTokens::expressiveDefaultSpatial() const {
|
||||
return m_expressiveDefaultSpatial;
|
||||
}
|
||||
|
||||
QEasingCurve AnimTokens::expressiveSlowSpatial() const {
|
||||
return m_expressiveSlowSpatial;
|
||||
}
|
||||
|
||||
QEasingCurve AnimTokens::expressiveFastEffects() const {
|
||||
return m_expressiveFastEffects;
|
||||
}
|
||||
|
||||
QEasingCurve AnimTokens::expressiveDefaultEffects() const {
|
||||
return m_expressiveDefaultEffects;
|
||||
}
|
||||
|
||||
QEasingCurve AnimTokens::expressiveSlowEffects() const {
|
||||
return m_expressiveSlowEffects;
|
||||
}
|
||||
|
||||
AnimDurations* AnimTokens::durations() const {
|
||||
return m_durations;
|
||||
}
|
||||
|
||||
QEasingCurve AnimTokens::buildCurve(const QList<qreal>& points) {
|
||||
QEasingCurve curve(QEasingCurve::BezierSpline);
|
||||
|
||||
for (int i = 0; i + 5 < points.size(); i += 6) {
|
||||
QPointF c1(points[i], points[i + 1]);
|
||||
QPointF c2(points[i + 2], points[i + 3]);
|
||||
QPointF end(points[i + 4], points[i + 5]);
|
||||
const QPointF c1(points[i], points[i + 1]);
|
||||
const QPointF c2(points[i + 2], points[i + 3]);
|
||||
const QPointF end(points[i + 4], points[i + 5]);
|
||||
curve.addCubicBezierSegment(c1, c2, end);
|
||||
}
|
||||
|
||||
return curve;
|
||||
}
|
||||
|
||||
void AnimEasing::bindCurves(AnimCurves* curves) {
|
||||
m_curves = curves;
|
||||
connect(
|
||||
curves,
|
||||
&AnimCurves::propertiesChanged,
|
||||
this,
|
||||
&AnimEasing::rebuildCurves);
|
||||
rebuildCurves();
|
||||
}
|
||||
|
||||
void AnimEasing::rebuildCurves() {
|
||||
void AnimTokens::rebuildCurves() {
|
||||
if (!m_curves) return;
|
||||
|
||||
m_emphasized = buildCurve(m_curves->emphasized());
|
||||
m_emphasizedAccel = buildCurve(m_curves->emphasizedAccel());
|
||||
m_emphasizedDecel = buildCurve(m_curves->emphasizedDecel());
|
||||
@@ -41,7 +91,24 @@ void AnimEasing::rebuildCurves() {
|
||||
m_expressiveDefaultEffects =
|
||||
buildCurve(m_curves->expressiveDefaultEffects());
|
||||
m_expressiveSlowEffects = buildCurve(m_curves->expressiveSlowEffects());
|
||||
|
||||
emit curvesChanged();
|
||||
}
|
||||
|
||||
void AnimTokens::bindCurves(AnimCurves* curves) {
|
||||
m_curves = curves;
|
||||
|
||||
connect(
|
||||
curves, &AnimCurves::optionChanged, this, &AnimTokens::rebuildCurves);
|
||||
|
||||
rebuildCurves();
|
||||
}
|
||||
|
||||
void AnimTokens::bindDurations(AnimDurations* durations) {
|
||||
if (m_durations == durations) return;
|
||||
|
||||
m_durations = durations;
|
||||
emit durationsChanged();
|
||||
}
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -9,8 +9,10 @@ namespace ZShell::config {
|
||||
class AnimCurves;
|
||||
class AnimDurations;
|
||||
|
||||
class AnimEasing : public QObject {
|
||||
class AnimTokens : public QObject {
|
||||
Q_OBJECT
|
||||
Q_MOC_INCLUDE("tokens.hpp")
|
||||
Q_MOC_INCLUDE("appearance.hpp")
|
||||
QML_ANONYMOUS
|
||||
|
||||
Q_PROPERTY(QEasingCurve emphasized READ emphasized NOTIFY curvesChanged)
|
||||
@@ -42,54 +44,53 @@ class AnimEasing : public QObject {
|
||||
QEasingCurve expressiveSlowEffects READ expressiveSlowEffects NOTIFY
|
||||
curvesChanged)
|
||||
|
||||
Q_PROPERTY(
|
||||
ZShell::config::AnimDurations* durations READ durations NOTIFY
|
||||
durationsChanged)
|
||||
|
||||
public:
|
||||
explicit AnimEasing(QObject* parent = nullptr) : QObject(parent) {}
|
||||
explicit AnimTokens(QObject* parent = nullptr);
|
||||
|
||||
void bindCurves(AnimCurves* curves);
|
||||
void bindDurations(AnimDurations* durations);
|
||||
|
||||
[[nodiscard]] QEasingCurve emphasized() const { return m_emphasized; }
|
||||
[[nodiscard]] QEasingCurve emphasizedAccel() const {
|
||||
return m_emphasizedAccel;
|
||||
}
|
||||
[[nodiscard]] QEasingCurve emphasizedDecel() const {
|
||||
return m_emphasizedDecel;
|
||||
}
|
||||
[[nodiscard]] QEasingCurve standard() const { return m_standard; }
|
||||
[[nodiscard]] QEasingCurve standardAccel() const { return m_standardAccel; }
|
||||
[[nodiscard]] QEasingCurve standardDecel() const { return m_standardDecel; }
|
||||
[[nodiscard]] QEasingCurve expressiveFastSpatial() const {
|
||||
return m_expressiveFastSpatial;
|
||||
}
|
||||
[[nodiscard]] QEasingCurve expressiveDefaultSpatial() const {
|
||||
return m_expressiveDefaultSpatial;
|
||||
}
|
||||
[[nodiscard]] QEasingCurve expressiveSlowSpatial() const {
|
||||
return m_expressiveSlowSpatial;
|
||||
}
|
||||
[[nodiscard]] QEasingCurve expressiveFastEffects() const {
|
||||
return m_expressiveFastEffects;
|
||||
}
|
||||
[[nodiscard]] QEasingCurve expressiveDefaultEffects() const {
|
||||
return m_expressiveDefaultEffects;
|
||||
}
|
||||
[[nodiscard]] QEasingCurve expressiveSlowEffects() const {
|
||||
return m_expressiveSlowEffects;
|
||||
}
|
||||
[[nodiscard]] QEasingCurve emphasized() const;
|
||||
[[nodiscard]] QEasingCurve emphasizedAccel() const;
|
||||
[[nodiscard]] QEasingCurve emphasizedDecel() const;
|
||||
[[nodiscard]] QEasingCurve standard() const;
|
||||
[[nodiscard]] QEasingCurve standardAccel() const;
|
||||
[[nodiscard]] QEasingCurve standardDecel() const;
|
||||
[[nodiscard]] QEasingCurve expressiveFastSpatial() const;
|
||||
[[nodiscard]] QEasingCurve expressiveDefaultSpatial() const;
|
||||
[[nodiscard]] QEasingCurve expressiveSlowSpatial() const;
|
||||
[[nodiscard]] QEasingCurve expressiveFastEffects() const;
|
||||
[[nodiscard]] QEasingCurve expressiveDefaultEffects() const;
|
||||
[[nodiscard]] QEasingCurve expressiveSlowEffects() const;
|
||||
[[nodiscard]] AnimDurations* durations() const;
|
||||
|
||||
signals:
|
||||
void curvesChanged();
|
||||
void durationsChanged();
|
||||
|
||||
private:
|
||||
static QEasingCurve buildCurve(const QList<qreal>& points);
|
||||
void rebuildCurves();
|
||||
static QEasingCurve buildCurve(const QList<qreal>& points);
|
||||
|
||||
AnimCurves* m_curves = nullptr;
|
||||
QEasingCurve m_emphasized, m_emphasizedAccel, m_emphasizedDecel;
|
||||
QEasingCurve m_standard, m_standardAccel, m_standardDecel;
|
||||
QEasingCurve m_expressiveFastSpatial, m_expressiveDefaultSpatial,
|
||||
m_expressiveSlowSpatial;
|
||||
QEasingCurve m_expressiveFastEffects, m_expressiveDefaultEffects,
|
||||
m_expressiveSlowEffects;
|
||||
AnimDurations* m_durations = nullptr;
|
||||
|
||||
QEasingCurve m_emphasized;
|
||||
QEasingCurve m_emphasizedAccel;
|
||||
QEasingCurve m_emphasizedDecel;
|
||||
QEasingCurve m_standard;
|
||||
QEasingCurve m_standardAccel;
|
||||
QEasingCurve m_standardDecel;
|
||||
QEasingCurve m_expressiveFastSpatial;
|
||||
QEasingCurve m_expressiveDefaultSpatial;
|
||||
QEasingCurve m_expressiveSlowSpatial;
|
||||
QEasingCurve m_expressiveFastEffects;
|
||||
QEasingCurve m_expressiveDefaultEffects;
|
||||
QEasingCurve m_expressiveSlowEffects;
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
#include "appearance.hpp"
|
||||
|
||||
#include "tokens.hpp"
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename Source, typename Target>
|
||||
void connectTokenSignals(Source* source, Target* target) {
|
||||
const auto* meta = source->metaObject();
|
||||
|
||||
for (int i = meta->propertyOffset(); i < meta->propertyCount(); ++i) {
|
||||
auto prop = meta->property(i);
|
||||
|
||||
if (prop.hasNotifySignal())
|
||||
QObject::connect(
|
||||
source,
|
||||
prop.notifySignal(),
|
||||
target,
|
||||
target->metaObject()->method(
|
||||
target->metaObject()->indexOfSignal("valuesChanged()")));
|
||||
}
|
||||
|
||||
QObject::connect(
|
||||
target, &Target::scaleChanged, target, &Target::valuesChanged);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void AppearanceRounding::bindTokens(RoundingTokens* tokens) {
|
||||
m_tokens = tokens;
|
||||
connectTokenSignals(tokens, this);
|
||||
}
|
||||
|
||||
int AppearanceRounding::extraSmall() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->extraSmall() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearanceRounding::small() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->small() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearanceRounding::medium() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->medium() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearanceRounding::large() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->large() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearanceRounding::largeIncreased() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->largeIncreased() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int AppearanceRounding::extraLarge() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->extraLarge() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearanceRounding::extraLargeIncreased() const {
|
||||
return m_tokens
|
||||
? static_cast<int>(m_tokens->extraLargeIncreased() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int AppearanceRounding::extraExtraLarge() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->extraExtraLarge() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int AppearanceRounding::full() const {
|
||||
return m_tokens ? m_tokens->full() : 0;
|
||||
}
|
||||
|
||||
void AppearanceSpacing::bindTokens(SpacingTokens* tokens) {
|
||||
m_tokens = tokens;
|
||||
connectTokenSignals(tokens, this);
|
||||
}
|
||||
|
||||
int AppearanceSpacing::extraSmall() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->extraSmall() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearanceSpacing::small() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->small() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearanceSpacing::medium() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->medium() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearanceSpacing::large() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->large() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearanceSpacing::largeIncreased() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->largeIncreased() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int AppearanceSpacing::extraLarge() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->extraLarge() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearanceSpacing::extraLargeIncreased() const {
|
||||
return m_tokens
|
||||
? static_cast<int>(m_tokens->extraLargeIncreased() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int AppearanceSpacing::extraExtraLarge() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->extraExtraLarge() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
void AppearancePadding::bindTokens(PaddingTokens* tokens) {
|
||||
m_tokens = tokens;
|
||||
connectTokenSignals(tokens, this);
|
||||
}
|
||||
|
||||
int AppearancePadding::extraSmall() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->extraSmall() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearancePadding::smaller() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->smaller() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearancePadding::small() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->small() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearancePadding::medium() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->medium() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearancePadding::large() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->large() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearancePadding::largeIncreased() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->largeIncreased() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int AppearancePadding::extraLarge() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->extraLarge() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AppearancePadding::extraLargeIncreased() const {
|
||||
return m_tokens
|
||||
? static_cast<int>(m_tokens->extraLargeIncreased() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int AppearancePadding::extraExtraLarge() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->extraExtraLarge() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
void AnimDurations::bindTokens(AnimDurationTokens* tokens) {
|
||||
m_tokens = tokens;
|
||||
connectTokenSignals(tokens, this);
|
||||
}
|
||||
|
||||
int AnimDurations::small() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->small() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AnimDurations::normal() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->normal() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AnimDurations::large() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->large() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AnimDurations::extraLarge() const {
|
||||
return m_tokens ? static_cast<int>(m_tokens->extraLarge() * m_scale) : 0;
|
||||
}
|
||||
|
||||
int AnimDurations::expressiveFastSpatial() const {
|
||||
return m_tokens
|
||||
? static_cast<int>(m_tokens->expressiveFastSpatial() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int AnimDurations::expressiveDefaultSpatial() const {
|
||||
return m_tokens ? static_cast<int>(
|
||||
m_tokens->expressiveDefaultSpatial() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int AnimDurations::expressiveSlowSpatial() const {
|
||||
return m_tokens
|
||||
? static_cast<int>(m_tokens->expressiveSlowSpatial() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int AnimDurations::expressiveFastEffects() const {
|
||||
return m_tokens
|
||||
? static_cast<int>(m_tokens->expressiveFastEffects() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int AnimDurations::expressiveDefaultEffects() const {
|
||||
return m_tokens ? static_cast<int>(
|
||||
m_tokens->expressiveDefaultEffects() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int AnimDurations::expressiveSlowEffects() const {
|
||||
return m_tokens
|
||||
? static_cast<int>(m_tokens->expressiveSlowEffects() * m_scale)
|
||||
: 0;
|
||||
}
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -1,83 +1,347 @@
|
||||
#pragma once
|
||||
|
||||
#include "configobject.hpp"
|
||||
#include <qqmlregistration.h>
|
||||
#include <qfont.h>
|
||||
#include <qstring.h>
|
||||
#include <qvariantmap.h>
|
||||
|
||||
#include "settings/objectnode.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class Anim : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
using Qt::StringLiterals::operator""_s;
|
||||
|
||||
CFG_PROPERTY(int, mediaGifSpeedAdjustment, 300)
|
||||
CFG_PROPERTY(qreal, sessionGifSpeed, 0.7)
|
||||
class RoundingTokens;
|
||||
class SpacingTokens;
|
||||
class PaddingTokens;
|
||||
class AnimDurationTokens;
|
||||
|
||||
class AppearanceRounding : public settings::ObjectNode {
|
||||
CONFIG_NODE(AppearanceRounding, settings::ObjectNode)
|
||||
|
||||
CONFIG_PROPERTY(qreal, scale, 1)
|
||||
|
||||
Q_PROPERTY(int extraSmall READ extraSmall NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int small READ small NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int medium READ medium NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int large READ large NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int largeIncreased READ largeIncreased NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int extraLarge READ extraLarge NOTIFY valuesChanged)
|
||||
Q_PROPERTY(
|
||||
int extraLargeIncreased READ extraLargeIncreased NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int extraExtraLarge READ extraExtraLarge NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int full READ full NOTIFY valuesChanged)
|
||||
|
||||
public:
|
||||
explicit Anim(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
void bindTokens(RoundingTokens* tokens);
|
||||
|
||||
[[nodiscard]] int extraSmall() const;
|
||||
[[nodiscard]] int small() const;
|
||||
[[nodiscard]] int medium() const;
|
||||
[[nodiscard]] int large() const;
|
||||
[[nodiscard]] int largeIncreased() const;
|
||||
[[nodiscard]] int extraLarge() const;
|
||||
[[nodiscard]] int extraLargeIncreased() const;
|
||||
[[nodiscard]] int extraExtraLarge() const;
|
||||
[[nodiscard]] int full() const;
|
||||
|
||||
signals:
|
||||
void valuesChanged();
|
||||
|
||||
private:
|
||||
RoundingTokens* m_tokens = nullptr;
|
||||
};
|
||||
|
||||
class Deform : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class AppearanceSpacing : public settings::ObjectNode {
|
||||
CONFIG_NODE(AppearanceSpacing, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(qreal, scale, 1)
|
||||
CONFIG_PROPERTY(qreal, scale, 1)
|
||||
|
||||
Q_PROPERTY(int extraSmall READ extraSmall NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int small READ small NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int medium READ medium NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int large READ large NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int largeIncreased READ largeIncreased NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int extraLarge READ extraLarge NOTIFY valuesChanged)
|
||||
Q_PROPERTY(
|
||||
int extraLargeIncreased READ extraLargeIncreased NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int extraExtraLarge READ extraExtraLarge NOTIFY valuesChanged)
|
||||
|
||||
public:
|
||||
explicit Deform(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
void bindTokens(SpacingTokens* tokens);
|
||||
|
||||
[[nodiscard]] int extraSmall() const;
|
||||
[[nodiscard]] int small() const;
|
||||
[[nodiscard]] int medium() const;
|
||||
[[nodiscard]] int large() const;
|
||||
[[nodiscard]] int largeIncreased() const;
|
||||
[[nodiscard]] int extraLarge() const;
|
||||
[[nodiscard]] int extraLargeIncreased() const;
|
||||
[[nodiscard]] int extraExtraLarge() const;
|
||||
|
||||
signals:
|
||||
void valuesChanged();
|
||||
|
||||
private:
|
||||
SpacingTokens* m_tokens = nullptr;
|
||||
};
|
||||
|
||||
class FontFamily : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class AppearancePadding : public settings::ObjectNode {
|
||||
CONFIG_NODE(AppearancePadding, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(QString, clock, QStringLiteral("Rubik"))
|
||||
CFG_PROPERTY(QString, material, QStringLiteral("Material Symbols Rounded"))
|
||||
CFG_PROPERTY(QString, mono, QStringLiteral("CaskaydiaCove NF"))
|
||||
CFG_PROPERTY(QString, sans, QStringLiteral("Segoe UI Variable Text"))
|
||||
CONFIG_PROPERTY(qreal, scale, 1)
|
||||
|
||||
Q_PROPERTY(int extraSmall READ extraSmall NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int smaller READ smaller NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int small READ small NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int medium READ medium NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int large READ large NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int largeIncreased READ largeIncreased NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int extraLarge READ extraLarge NOTIFY valuesChanged)
|
||||
Q_PROPERTY(
|
||||
int extraLargeIncreased READ extraLargeIncreased NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int extraExtraLarge READ extraExtraLarge NOTIFY valuesChanged)
|
||||
|
||||
public:
|
||||
explicit FontFamily(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
void bindTokens(PaddingTokens* tokens);
|
||||
|
||||
[[nodiscard]] int extraSmall() const;
|
||||
[[nodiscard]] int smaller() const;
|
||||
[[nodiscard]] int small() const;
|
||||
[[nodiscard]] int medium() const;
|
||||
[[nodiscard]] int large() const;
|
||||
[[nodiscard]] int largeIncreased() const;
|
||||
[[nodiscard]] int extraLarge() const;
|
||||
[[nodiscard]] int extraLargeIncreased() const;
|
||||
[[nodiscard]] int extraExtraLarge() const;
|
||||
|
||||
signals:
|
||||
void valuesChanged();
|
||||
|
||||
private:
|
||||
PaddingTokens* m_tokens = nullptr;
|
||||
};
|
||||
|
||||
class Font : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
namespace detail {
|
||||
|
||||
struct FontConfig {
|
||||
QString family = {};
|
||||
int size;
|
||||
int weight = QFont::Normal;
|
||||
bool italic = false;
|
||||
QVariantMap vaxes = {{u"ROND"_s, 25}};
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class FontSizeNode : public settings::ObjectNode {
|
||||
CONFIG_NODE(FontSizeNode, settings::ObjectNode)
|
||||
};
|
||||
|
||||
class FontStyleNode : public settings::ObjectNode {
|
||||
CONFIG_NODE(FontStyleNode, settings::ObjectNode)
|
||||
|
||||
public:
|
||||
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 { \
|
||||
CONFIG_NODE(FontConfig##Style##Size, FontSizeNode) \
|
||||
\
|
||||
CONFIG_PROPERTY( \
|
||||
QString, \
|
||||
family, \
|
||||
ARG(props).family) /* Empty inherits the style family */ \
|
||||
CONFIG_PROPERTY(int, size, ARG(props).size) \
|
||||
CONFIG_PROPERTY(int, weight, ARG(props).weight) \
|
||||
CONFIG_PROPERTY(bool, italic, ARG(props).italic) \
|
||||
CONFIG_PROPERTY(QVariantMap, vaxes, ARG(props).vaxes) \
|
||||
};
|
||||
#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 { \
|
||||
CONFIG_NODE(FontStyle##Style, FontStyleNode) \
|
||||
\
|
||||
CONFIG_PROPERTY(QString, family, family_) \
|
||||
CONFIG_SUBOBJECT(FontConfig##Style##Large, large) \
|
||||
CONFIG_SUBOBJECT(FontConfig##Style##Medium, medium) \
|
||||
CONFIG_SUBOBJECT(FontConfig##Style##Small, small) \
|
||||
\
|
||||
public: \
|
||||
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, u"GoogleSansFlex"_s,
|
||||
FONT({ .size = 32, .weight = QFont::Medium }),
|
||||
FONT({ .size = 28, .weight = QFont::Medium }),
|
||||
FONT({ .size = 24, .weight = QFont::Medium })
|
||||
)
|
||||
FONT_STYLE(Title, u"GoogleSansFlex"_s,
|
||||
FONT({ .size = 22, .weight = QFont::Medium }),
|
||||
FONT({ .size = 16, .weight = QFont::Medium }),
|
||||
FONT({ .size = 14, .weight = QFont::Medium })
|
||||
)
|
||||
FONT_STYLE(Body, u"GoogleSansFlex"_s,
|
||||
FONT({ .size = 16 }),
|
||||
FONT({ .size = 14 }),
|
||||
FONT({ .size = 12 })
|
||||
)
|
||||
FONT_STYLE(Label, u"GoogleSansFlex"_s,
|
||||
FONT({ .size = 14, .weight = QFont::Medium }),
|
||||
FONT({ .size = 12, .weight = QFont::Medium }),
|
||||
FONT({ .size = 11 })
|
||||
)
|
||||
FONT_STYLE(Mono, u"CaskaydiaCove NF"_s,
|
||||
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 {
|
||||
CONFIG_NODE(FontStyleIcon, FontStyleNode)
|
||||
|
||||
CONFIG_PROPERTY(QString, family, u"Material Symbols Rounded"_s)
|
||||
CONFIG_SUBOBJECT(FontConfigIconExtraLarge, extraLarge)
|
||||
CONFIG_SUBOBJECT(FontConfigIconLarge, large)
|
||||
CONFIG_SUBOBJECT(FontConfigIconMedium, medium)
|
||||
CONFIG_SUBOBJECT(FontConfigIconSmall, small)
|
||||
|
||||
public:
|
||||
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 FontFamily : public settings::ObjectNode {
|
||||
CONFIG_NODE(FontFamily, settings::ObjectNode)
|
||||
|
||||
CONFIG_PROPERTY(QString, clock, QStringLiteral("Rubik"))
|
||||
CONFIG_PROPERTY(
|
||||
QString, material, QStringLiteral("Material Symbols Rounded"))
|
||||
CONFIG_PROPERTY(QString, mono, QStringLiteral("CaskaydiaCove NF"))
|
||||
CONFIG_PROPERTY(QString, sans, QStringLiteral("Segoe UI Variable Text"))
|
||||
};
|
||||
|
||||
class AppearanceFont : public settings::ObjectNode {
|
||||
CONFIG_NODE(AppearanceFont, settings::ObjectNode)
|
||||
|
||||
CONFIG_PROPERTY(qreal, scale, 1)
|
||||
CONFIG_SUBOBJECT(FontFamily, family)
|
||||
|
||||
public:
|
||||
explicit Font(QObject* parent = nullptr)
|
||||
: ConfigObject(parent), m_family(new FontFamily(this)) {}
|
||||
CONFIG_SUBOBJECT(FontStyleHeadline, headline)
|
||||
CONFIG_SUBOBJECT(FontStyleTitle, title)
|
||||
CONFIG_SUBOBJECT(FontStyleBody, body)
|
||||
CONFIG_SUBOBJECT(FontStyleLabel, label)
|
||||
CONFIG_SUBOBJECT(FontStyleMono, mono)
|
||||
CONFIG_SUBOBJECT(FontStyleIcon, icon)
|
||||
CONFIG_PROPERTY(QString, clock, QStringLiteral("Rubik"))
|
||||
CONFIG_PROPERTY(QString, workspaces, QStringLiteral("Rubik"))
|
||||
};
|
||||
|
||||
class Transparency : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class AnimDurations : public settings::ObjectNode {
|
||||
CONFIG_NODE(AnimDurations, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(qreal, base, 0.85)
|
||||
CFG_PROPERTY(bool, enabled, false)
|
||||
CFG_PROPERTY(qreal, layers, 0.4)
|
||||
CONFIG_GLOBAL_PROPERTY(qreal, scale, 1)
|
||||
|
||||
Q_PROPERTY(int small READ small NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int normal READ normal NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int large READ large NOTIFY valuesChanged)
|
||||
Q_PROPERTY(int extraLarge READ extraLarge NOTIFY valuesChanged)
|
||||
Q_PROPERTY(
|
||||
int expressiveFastSpatial READ expressiveFastSpatial NOTIFY
|
||||
valuesChanged)
|
||||
Q_PROPERTY(
|
||||
int expressiveDefaultSpatial READ expressiveDefaultSpatial NOTIFY
|
||||
valuesChanged)
|
||||
Q_PROPERTY(
|
||||
int expressiveSlowSpatial READ expressiveSlowSpatial NOTIFY
|
||||
valuesChanged)
|
||||
Q_PROPERTY(
|
||||
int expressiveFastEffects READ expressiveFastEffects NOTIFY
|
||||
valuesChanged)
|
||||
Q_PROPERTY(
|
||||
int expressiveDefaultEffects READ expressiveDefaultEffects NOTIFY
|
||||
valuesChanged)
|
||||
Q_PROPERTY(
|
||||
int expressiveSlowEffects READ expressiveSlowEffects NOTIFY
|
||||
valuesChanged)
|
||||
|
||||
public:
|
||||
explicit Transparency(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
void bindTokens(AnimDurationTokens* tokens);
|
||||
|
||||
[[nodiscard]] int small() const;
|
||||
[[nodiscard]] int normal() const;
|
||||
[[nodiscard]] int large() const;
|
||||
[[nodiscard]] int extraLarge() const;
|
||||
[[nodiscard]] int expressiveFastSpatial() const;
|
||||
[[nodiscard]] int expressiveDefaultSpatial() const;
|
||||
[[nodiscard]] int expressiveSlowSpatial() const;
|
||||
[[nodiscard]] int expressiveFastEffects() const;
|
||||
[[nodiscard]] int expressiveDefaultEffects() const;
|
||||
[[nodiscard]] int expressiveSlowEffects() const;
|
||||
|
||||
signals:
|
||||
void valuesChanged();
|
||||
|
||||
private:
|
||||
AnimDurationTokens* m_tokens = nullptr;
|
||||
};
|
||||
|
||||
class Appearance : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Anim : public settings::ObjectNode {
|
||||
CONFIG_NODE(Anim, settings::ObjectNode)
|
||||
|
||||
CONFIG_PROPERTY(int, mediaGifSpeedAdjustment, 300)
|
||||
CONFIG_PROPERTY(qreal, sessionGifSpeed, 0.7)
|
||||
CONFIG_SUBOBJECT(AnimDurations, durations)
|
||||
};
|
||||
|
||||
class Deform : public settings::ObjectNode {
|
||||
CONFIG_NODE(Deform, settings::ObjectNode)
|
||||
|
||||
CONFIG_PROPERTY(qreal, scale, 1)
|
||||
};
|
||||
|
||||
class AppearanceTransparency : public settings::ObjectNode {
|
||||
CONFIG_NODE(AppearanceTransparency, settings::ObjectNode)
|
||||
|
||||
CONFIG_GLOBAL_PROPERTY(bool, enabled, false)
|
||||
CONFIG_GLOBAL_PROPERTY(qreal, base, 0.85)
|
||||
CONFIG_GLOBAL_PROPERTY(qreal, layers, 0.4)
|
||||
};
|
||||
|
||||
class Appearance : public settings::ObjectNode {
|
||||
CONFIG_NODE(Appearance, settings::ObjectNode)
|
||||
|
||||
CONFIG_SUBOBJECT(Anim, anim)
|
||||
CONFIG_SUBOBJECT(Deform, deform)
|
||||
CONFIG_SUBOBJECT(Font, font)
|
||||
CONFIG_SUBOBJECT(Transparency, transparency)
|
||||
|
||||
public:
|
||||
explicit Appearance(QObject* parent = nullptr)
|
||||
: ConfigObject(parent)
|
||||
, m_anim(new Anim(this))
|
||||
, m_deform(new Deform(this))
|
||||
, m_font(new Font(this))
|
||||
, m_transparency(new Transparency(this)) {}
|
||||
CONFIG_SUBOBJECT(AppearanceFont, font)
|
||||
CONFIG_SUBOBJECT(AppearanceRounding, rounding)
|
||||
CONFIG_SUBOBJECT(AppearanceSpacing, spacing)
|
||||
CONFIG_SUBOBJECT(AppearancePadding, padding)
|
||||
CONFIG_SUBOBJECT(AppearanceTransparency, transparency)
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
#pragma once
|
||||
#include "configobject.hpp"
|
||||
#include "common.hpp"
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class Background : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Background : public settings::ObjectNode {
|
||||
CONFIG_NODE(Background, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(bool, enabled, true)
|
||||
CFG_PROPERTY(int, wallFadeDuration, 300)
|
||||
|
||||
public:
|
||||
explicit Background(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(bool, enabled, true)
|
||||
CONFIG_PROPERTY(int, wallFadeDuration, 300)
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "configlist.hpp"
|
||||
#include "configobject.hpp"
|
||||
#include "common.hpp"
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qhashfunctions.h>
|
||||
#include <qqmlregistration.h>
|
||||
@@ -9,66 +8,53 @@
|
||||
namespace ZShell::config {
|
||||
|
||||
class BarEntry : public ListEntry {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
|
||||
public:
|
||||
explicit BarEntry(QObject* parent = nullptr) : ListEntry(parent) {}
|
||||
CONFIG_NODE(BarEntry, ListEntry)
|
||||
};
|
||||
|
||||
class Popouts : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Popouts : public settings::ObjectNode {
|
||||
CONFIG_NODE(Popouts, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(bool, statusIcons, true)
|
||||
CFG_PROPERTY(bool, tray, true)
|
||||
|
||||
public:
|
||||
explicit Popouts(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(bool, statusIcons, true)
|
||||
CONFIG_PROPERTY(bool, tray, true)
|
||||
};
|
||||
|
||||
class Tray : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Tray : public settings::ObjectNode {
|
||||
CONFIG_NODE(Tray, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(bool, recolorIcons, false)
|
||||
CFG_PROPERTY(bool, showOnHover, true)
|
||||
CFG_PROPERTY(int, trayIconSize, 24)
|
||||
CONFIG_PROPERTY(bool, recolorIcons, false)
|
||||
CONFIG_PROPERTY(bool, showOnHover, true)
|
||||
CONFIG_PROPERTY(int, trayIconSize, 24)
|
||||
CONFIG_LIST(
|
||||
EntryList,
|
||||
statusIcons,
|
||||
{
|
||||
DEFAULT_ARG({
|
||||
LIST_ENTRY(audio, true),
|
||||
LIST_ENTRY(microphone, true),
|
||||
LIST_ENTRY(bluetooth, false),
|
||||
LIST_ENTRY(network, false),
|
||||
LIST_ENTRY(wifi, false),
|
||||
LIST_ENTRY(power, true),
|
||||
})
|
||||
|
||||
public:
|
||||
explicit Tray(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
}))
|
||||
};
|
||||
|
||||
class Bar : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Bar : public settings::ObjectNode {
|
||||
CONFIG_NODE(Bar, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(bool, autoHide, false)
|
||||
CFG_PROPERTY(int, border, 4)
|
||||
CFG_PROPERTY(bool, fullscreenReveal, true)
|
||||
CFG_PROPERTY(int, height, 24)
|
||||
CFG_PROPERTY(bool, hideWhenNotif, true)
|
||||
CONFIG_PROPERTY(bool, autoHide, false)
|
||||
CONFIG_PROPERTY(int, border, 4)
|
||||
CONFIG_PROPERTY(bool, fullscreenReveal, true)
|
||||
CONFIG_PROPERTY(int, height, 24)
|
||||
CONFIG_PROPERTY(bool, hideWhenNotif, true)
|
||||
CONFIG_SUBOBJECT(Popouts, popouts)
|
||||
CFG_PROPERTY(int, revealDelay, 100)
|
||||
CFG_PROPERTY(int, rounding, 14)
|
||||
CFG_PROPERTY(int, smoothing, 32)
|
||||
CFG_PROPERTY(QString, position, QStringLiteral("top"))
|
||||
CONFIG_PROPERTY(int, revealDelay, 100)
|
||||
CONFIG_PROPERTY(int, rounding, 14)
|
||||
CONFIG_PROPERTY(int, smoothing, 32)
|
||||
CONFIG_PROPERTY(QString, position, QStringLiteral("top"))
|
||||
CONFIG_SUBOBJECT(Tray, tray)
|
||||
CONFIG_LIST(
|
||||
EntryList,
|
||||
entries,
|
||||
{
|
||||
DEFAULT_ARG({
|
||||
LIST_ENTRY(workspaces, true),
|
||||
LIST_ENTRY(audio, true),
|
||||
LIST_ENTRY(media, true),
|
||||
@@ -78,20 +64,15 @@ class Bar : public ConfigObject {
|
||||
LIST_ENTRY(spacer, true),
|
||||
LIST_ENTRY(activeWindow, true),
|
||||
LIST_ENTRY(spacer, true),
|
||||
LIST_ENTRY(hyprsunset, true),
|
||||
LIST_ENTRY(nightlight, true),
|
||||
LIST_ENTRY(tray, true),
|
||||
LIST_ENTRY(statusIcons, true),
|
||||
LIST_ENTRY(network, false),
|
||||
LIST_ENTRY(upower, true),
|
||||
LIST_ENTRY(clock, true),
|
||||
LIST_ENTRY(notifBell, true),
|
||||
})
|
||||
|
||||
public:
|
||||
explicit Bar(QObject* parent = nullptr)
|
||||
: ConfigObject(parent)
|
||||
, m_popouts(new Popouts(this))
|
||||
, m_tray(new Tray(this)) {}
|
||||
}),
|
||||
.identityKey = QStringLiteral("id"))
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -1,33 +1,24 @@
|
||||
#pragma once
|
||||
#include "configobject.hpp"
|
||||
#include "common.hpp"
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class ClipboardSizes : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class ClipboardSizes : public settings::ObjectNode {
|
||||
CONFIG_NODE(ClipboardSizes, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(int, itemHeight, 60)
|
||||
CFG_PROPERTY(int, minPreviewWidth, 200)
|
||||
CFG_PROPERTY(int, previewWidth, 800)
|
||||
CFG_PROPERTY(int, width, 500)
|
||||
|
||||
public:
|
||||
explicit ClipboardSizes(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(int, itemHeight, 60)
|
||||
CONFIG_PROPERTY(int, minPreviewWidth, 200)
|
||||
CONFIG_PROPERTY(int, previewWidth, 800)
|
||||
CONFIG_PROPERTY(int, width, 500)
|
||||
};
|
||||
|
||||
class Clipboard : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Clipboard : public settings::ObjectNode {
|
||||
CONFIG_NODE(Clipboard, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(bool, enabled, true)
|
||||
CFG_PROPERTY(int, maxEntriesShown, 10)
|
||||
CONFIG_PROPERTY(bool, enabled, true)
|
||||
CONFIG_PROPERTY(int, maxEntriesShown, 10)
|
||||
CONFIG_SUBOBJECT(ClipboardSizes, sizes)
|
||||
|
||||
public:
|
||||
explicit Clipboard(QObject* parent = nullptr)
|
||||
: ConfigObject(parent), m_sizes(new ClipboardSizes(this)) {}
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
#pragma once
|
||||
#include "configobject.hpp"
|
||||
#include "common.hpp"
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class Presets : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Presets : public settings::ObjectNode {
|
||||
CONFIG_NODE(Presets, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(QString, accent, QString())
|
||||
CFG_PROPERTY(QString, name, QStringLiteral("Catppuccin"))
|
||||
CFG_PROPERTY(QString, variant, QStringLiteral("latte"))
|
||||
|
||||
public:
|
||||
explicit Presets(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(QString, accent, QString())
|
||||
CONFIG_PROPERTY(QString, name, QStringLiteral("Catppuccin"))
|
||||
CONFIG_PROPERTY(QString, variant, QStringLiteral("latte"))
|
||||
};
|
||||
|
||||
class Colors : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Colors : public settings::ObjectNode {
|
||||
CONFIG_NODE(Colors, settings::ObjectNode)
|
||||
|
||||
CONFIG_SUBOBJECT(Presets, presets)
|
||||
CFG_PROPERTY(QString, schemeType, QStringLiteral("fidelity"))
|
||||
|
||||
public:
|
||||
explicit Colors(QObject* parent = nullptr)
|
||||
: ConfigObject(parent), m_presets(new Presets(this)) {}
|
||||
CONFIG_PROPERTY(QString, schemeType, QStringLiteral("fidelity"))
|
||||
CONFIG_PROPERTY(bool, scheduleDark, false)
|
||||
CONFIG_PROPERTY(int, scheduleDarkEnd, 600)
|
||||
CONFIG_PROPERTY(int, scheduleDarkStart, 1140)
|
||||
CONFIG_PROPERTY(bool, schemeGen, true)
|
||||
CONFIG_PROPERTY(bool, neovimColors, false)
|
||||
CONFIG_PROPERTY(QString, mode, QStringLiteral("dark"))
|
||||
CONFIG_PROPERTY(bool, smart, false)
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "common.hpp"
|
||||
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
Q_LOGGING_CATEGORY(lcConfig, "zshell.config", QtInfoMsg)
|
||||
|
||||
QString configDir() {
|
||||
const QString dir =
|
||||
QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
|
||||
QStringLiteral("/zshell");
|
||||
QDir().mkpath(dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
QString monitorConfigDir() {
|
||||
return configDir() + QStringLiteral("/monitors");
|
||||
}
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <qloggingcategory.h>
|
||||
#include <qstring.h>
|
||||
|
||||
#include "settings/macros.hpp"
|
||||
#include "settings/objectnode.hpp"
|
||||
|
||||
#define CONFIG_NODE_NO_CTOR SETTINGS_NODE_NO_CTOR
|
||||
#define CONFIG_NODE SETTINGS_NODE
|
||||
#define CONFIG_PROPERTY SETTINGS_PROPERTY
|
||||
#define CONFIG_GLOBAL_PROPERTY SETTINGS_GLOBAL_PROPERTY
|
||||
|
||||
#define CONFIG_ENUM_PROPERTY(Type, name, defaultVal) \
|
||||
CONFIG_PROPERTY(ZShell::config::Type::Enum, name, defaultVal)
|
||||
#define CONFIG_GLOBAL_ENUM_PROPERTY(Type, name, defaultVal) \
|
||||
CONFIG_GLOBAL_PROPERTY(ZShell::config::Type::Enum, name, defaultVal)
|
||||
|
||||
#define CONFIG_SUBOBJECT(Type, name) \
|
||||
SETTINGS_SUBOBJECT(ZShell::config::Type, name)
|
||||
#define CONFIG_GLOBAL_SUBOBJECT(Type, name) \
|
||||
SETTINGS_GLOBAL_SUBOBJECT(ZShell::config::Type, name)
|
||||
|
||||
#define CONFIG_LIST_TYPE SETTINGS_LIST_TYPE
|
||||
#define CONFIG_LIST(Type, name, defaultVal, ...) \
|
||||
SETTINGS_LIST( \
|
||||
ZShell::config::Type, name, DEFAULT_ARG(defaultVal), __VA_ARGS__)
|
||||
#define CONFIG_GLOBAL_LIST(Type, name, defaultVal, ...) \
|
||||
SETTINGS_GLOBAL_LIST( \
|
||||
ZShell::config::Type, name, DEFAULT_ARG(defaultVal), __VA_ARGS__)
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(lcConfig)
|
||||
|
||||
QString configDir();
|
||||
QString monitorConfigDir();
|
||||
|
||||
class ListEntry : public settings::ObjectNode {
|
||||
CONFIG_NODE(ListEntry, settings::ObjectNode)
|
||||
|
||||
CONFIG_PROPERTY(QString, id, {})
|
||||
CONFIG_PROPERTY(bool, enabled, true)
|
||||
};
|
||||
CONFIG_LIST_TYPE(ListEntry, EntryList)
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
// Shorthand for declaring an ID'd entry (bar entries/status icons, quick toggles, etc)
|
||||
#define LIST_ENTRY(id, enabled) \
|
||||
settings::vmap({ \
|
||||
{QStringLiteral("id"), QStringLiteral(#id)}, \
|
||||
{QStringLiteral("enabled"), enabled}, \
|
||||
})
|
||||
@@ -1,288 +0,0 @@
|
||||
#include "config.hpp"
|
||||
|
||||
#include "appearance.hpp"
|
||||
#include "background.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "clipboard.hpp"
|
||||
#include "colors.hpp"
|
||||
#include "dashboard.hpp"
|
||||
#include "dock.hpp"
|
||||
#include "general.hpp"
|
||||
#include "launcher.hpp"
|
||||
#include "llm.hpp"
|
||||
#include "lock.hpp"
|
||||
#include "notifs.hpp"
|
||||
#include "osd.hpp"
|
||||
#include "screenshot.hpp"
|
||||
#include "services.hpp"
|
||||
#include "sidebar.hpp"
|
||||
#include "utilities.hpp"
|
||||
|
||||
#include <QFile>
|
||||
#include <QSaveFile>
|
||||
#include <QJsonDocument>
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QFutureWatcher>
|
||||
#include <QFileInfo>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qfileinfo.h>
|
||||
#include <qfilesystemwatcher.h>
|
||||
#include <qthreadpool.h>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
Config* Config::s_instance = nullptr;
|
||||
|
||||
Config::Config(QObject* parent)
|
||||
: ConfigObject(parent)
|
||||
, m_appearance(new Appearance(this))
|
||||
, m_background(new Background(this))
|
||||
, m_bar(new Bar(this))
|
||||
, m_clipboard(new Clipboard(this))
|
||||
, m_colors(new Colors(this))
|
||||
, m_dashboard(new Dashboard(this))
|
||||
, m_dock(new Dock(this))
|
||||
, m_general(new General(this))
|
||||
, m_launcher(new Launcher(this))
|
||||
, m_llm(new Llm(this))
|
||||
, m_lock(new Lock(this))
|
||||
, m_notifs(new Notifs(this))
|
||||
, m_osd(new Osd(this))
|
||||
, m_screenshot(new Screenshot(this))
|
||||
, m_services(new Services(this))
|
||||
, m_sidebar(new Sidebar(this))
|
||||
, m_utilities(new Utilities(this)) {
|
||||
s_instance = this;
|
||||
connect(this, &ConfigObject::propertiesChanged, this, &Config::scheduleSave);
|
||||
|
||||
m_saveTimer.setSingleShot(true);
|
||||
m_saveTimer.setInterval(300);
|
||||
connect(&m_saveTimer, &QTimer::timeout, this, &Config::flushAsync);
|
||||
|
||||
m_reloadTimer.setSingleShot(true);
|
||||
m_reloadTimer.setInterval(50);
|
||||
connect(&m_reloadTimer, &QTimer::timeout, this, &Config::reloadAsync);
|
||||
|
||||
connectAutoSave(this);
|
||||
|
||||
connect(
|
||||
&m_watcher,
|
||||
&QFileSystemWatcher::directoryChanged,
|
||||
this,
|
||||
&Config::onWatcherEvent);
|
||||
|
||||
connect(
|
||||
&m_watcher,
|
||||
&QFileSystemWatcher::fileChanged,
|
||||
this,
|
||||
&Config::onWatcherEvent);
|
||||
|
||||
loadSync();
|
||||
updateWatch();
|
||||
|
||||
m_firstLoadDone = true;
|
||||
}
|
||||
|
||||
Config* Config::instance() {
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
Config* Config::create(QQmlEngine*, QJSEngine*) {
|
||||
if (!s_instance) s_instance = new Config();
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
QString Config::filePath() const {
|
||||
const QString dir =
|
||||
QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
|
||||
QStringLiteral("/zshell");
|
||||
QDir().mkpath(dir);
|
||||
return dir + QStringLiteral("/config.json");
|
||||
}
|
||||
|
||||
void Config::loadSync() {
|
||||
m_loading = true;
|
||||
|
||||
QFile f(filePath());
|
||||
QJsonObject before;
|
||||
|
||||
bool existed = false;
|
||||
|
||||
if (f.open(QIODevice::ReadOnly)) {
|
||||
const auto doc = QJsonDocument::fromJson(f.readAll());
|
||||
if (doc.isObject()) {
|
||||
before = doc.object();
|
||||
existed = true;
|
||||
} else {
|
||||
qInfo() << "Config: existing config at" << filePath()
|
||||
<< "is empty or not a valid JSON object - using defaults";
|
||||
}
|
||||
} else {
|
||||
qInfo() << "Config: no existing config at" << filePath()
|
||||
<< "- using defaults";
|
||||
}
|
||||
|
||||
loadFromJson(QJsonValue(before));
|
||||
m_loading = false;
|
||||
|
||||
const auto after = toJson().toObject();
|
||||
if (!existed || after != before) saveNow();
|
||||
}
|
||||
|
||||
void Config::updateWatch() {
|
||||
const QFileInfo info(filePath());
|
||||
|
||||
const QString dir = info.absolutePath();
|
||||
const QString file = info.absoluteFilePath();
|
||||
|
||||
if (!m_watcher.directories().contains(dir)) m_watcher.addPath(dir);
|
||||
if (info.exists() && !m_watcher.files().contains(file))
|
||||
m_watcher.addPath(file);
|
||||
}
|
||||
|
||||
void Config::onWatcherEvent(const QString&) {
|
||||
updateWatch();
|
||||
|
||||
const QFileInfo info(filePath());
|
||||
if (!info.exists()) return;
|
||||
|
||||
QFile file(info.absoluteFilePath());
|
||||
if (!file.open(QIODevice::ReadOnly)) return;
|
||||
|
||||
const QByteArray hash =
|
||||
QCryptographicHash::hash(file.readAll(), QCryptographicHash::Sha256);
|
||||
|
||||
{
|
||||
QMutexLocker lock(&m_writeMutex);
|
||||
|
||||
if (hash == m_lastWriteHash) return;
|
||||
}
|
||||
|
||||
m_reloadPending = true;
|
||||
|
||||
if (!m_loading) m_reloadTimer.start();
|
||||
}
|
||||
|
||||
void Config::loadAsync() {
|
||||
m_loading = true;
|
||||
|
||||
auto future = QtConcurrent::run([this]() {
|
||||
QFile f(filePath());
|
||||
|
||||
if (f.open(QIODevice::ReadOnly)) {
|
||||
const auto doc = QJsonDocument::fromJson(f.readAll());
|
||||
const bool valid = doc.isObject();
|
||||
const QJsonObject before = valid ? doc.object() : QJsonObject();
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
this,
|
||||
[this, valid, before]() {
|
||||
loadFromJson(QJsonValue(before));
|
||||
m_loading = false;
|
||||
|
||||
const auto after = toJson().toObject();
|
||||
if (!valid || after != before) saveNow();
|
||||
|
||||
if (m_reloadPending) m_reloadTimer.start();
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
} else {
|
||||
qInfo() << "Config: failed to reload from" << filePath()
|
||||
<< "- using in-memory values";
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
this,
|
||||
[this]() {
|
||||
m_loading = false;
|
||||
|
||||
if (m_reloadPending) m_reloadTimer.start();
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
});
|
||||
|
||||
m_loadFuture = future;
|
||||
}
|
||||
|
||||
void Config::load() {
|
||||
if (!m_firstLoadDone) {
|
||||
loadSync();
|
||||
} else {
|
||||
loadAsync();
|
||||
}
|
||||
}
|
||||
|
||||
void Config::reloadAsync() {
|
||||
if (m_loading) return;
|
||||
|
||||
m_reloadPending = false;
|
||||
loadAsync();
|
||||
}
|
||||
|
||||
void Config::scheduleSave() {
|
||||
if (m_loading) return;
|
||||
m_saveTimer.start();
|
||||
}
|
||||
|
||||
void Config::flushAsync() {
|
||||
const QByteArray data =
|
||||
QJsonDocument(toJson().toObject()).toJson(QJsonDocument::Indented);
|
||||
|
||||
const QByteArray hash =
|
||||
QCryptographicHash::hash(data, QCryptographicHash::Sha256);
|
||||
|
||||
QThreadPool::globalInstance()->start([this, data, hash]() {
|
||||
const bool success = writeAtomically(data);
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
this,
|
||||
[this, success, hash]() {
|
||||
if (!success) return;
|
||||
|
||||
QMutexLocker lock(&m_writeMutex);
|
||||
m_lastWriteHash = hash;
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
});
|
||||
}
|
||||
|
||||
void Config::saveNow() {
|
||||
m_saveTimer.stop();
|
||||
flushAsync();
|
||||
}
|
||||
|
||||
void Config::connectAutoSave(ConfigNode* node) {
|
||||
connect(node, &ConfigNode::propertiesChanged, this, [this] {
|
||||
scheduleSave();
|
||||
});
|
||||
|
||||
for (auto* child : node->childNodes())
|
||||
connectAutoSave(child);
|
||||
}
|
||||
|
||||
bool Config::writeAtomically(const QByteArray& data) {
|
||||
QSaveFile f(filePath());
|
||||
|
||||
if (!f.open(QIODevice::WriteOnly)) {
|
||||
qWarning() << "Config: failed to open for atomic write:"
|
||||
<< f.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (f.write(data) != data.size()) {
|
||||
qWarning() << "Config: failed to write:" << f.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!f.commit()) {
|
||||
qWarning() << "Config: commit failed:" << f.errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace ZShell::config
|
||||
@@ -1,114 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "configobject.hpp"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QFuture>
|
||||
#include <QMutex>
|
||||
#include <QTimer>
|
||||
#include <qqmlregistration.h>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
class QQmlEngine;
|
||||
class QJSEngine;
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class Appearance;
|
||||
class Background;
|
||||
class Bar;
|
||||
class Clipboard;
|
||||
class Colors;
|
||||
class Dashboard;
|
||||
class Dock;
|
||||
class General;
|
||||
class Launcher;
|
||||
class Llm;
|
||||
class Lock;
|
||||
class Notifs;
|
||||
class Osd;
|
||||
class Screenshot;
|
||||
class Services;
|
||||
class Sidebar;
|
||||
class Utilities;
|
||||
|
||||
class Config : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
Q_MOC_INCLUDE("appearance.hpp")
|
||||
Q_MOC_INCLUDE("background.hpp")
|
||||
Q_MOC_INCLUDE("bar.hpp")
|
||||
Q_MOC_INCLUDE("clipboard.hpp")
|
||||
Q_MOC_INCLUDE("colors.hpp")
|
||||
Q_MOC_INCLUDE("dashboard.hpp")
|
||||
Q_MOC_INCLUDE("dock.hpp")
|
||||
Q_MOC_INCLUDE("general.hpp")
|
||||
Q_MOC_INCLUDE("launcher.hpp")
|
||||
Q_MOC_INCLUDE("llm.hpp")
|
||||
Q_MOC_INCLUDE("lock.hpp")
|
||||
Q_MOC_INCLUDE("notifs.hpp")
|
||||
Q_MOC_INCLUDE("osd.hpp")
|
||||
Q_MOC_INCLUDE("screenshot.hpp")
|
||||
Q_MOC_INCLUDE("services.hpp")
|
||||
Q_MOC_INCLUDE("sidebar.hpp")
|
||||
Q_MOC_INCLUDE("utilities.hpp")
|
||||
|
||||
CONFIG_SUBOBJECT(Appearance, appearance)
|
||||
CONFIG_SUBOBJECT(Background, background)
|
||||
CONFIG_SUBOBJECT(Bar, bar)
|
||||
CONFIG_SUBOBJECT(Clipboard, clipboard)
|
||||
CONFIG_SUBOBJECT(Colors, colors)
|
||||
CONFIG_SUBOBJECT(Dashboard, dashboard)
|
||||
CONFIG_SUBOBJECT(Dock, dock)
|
||||
CONFIG_SUBOBJECT(General, general)
|
||||
CONFIG_SUBOBJECT(Launcher, launcher)
|
||||
CONFIG_SUBOBJECT(Llm, llm)
|
||||
CONFIG_SUBOBJECT(Lock, lock)
|
||||
CONFIG_SUBOBJECT(Notifs, notifs)
|
||||
CONFIG_SUBOBJECT(Osd, osd)
|
||||
CONFIG_SUBOBJECT(Screenshot, screenshot)
|
||||
CONFIG_SUBOBJECT(Services, services)
|
||||
CONFIG_SUBOBJECT(Sidebar, sidebar)
|
||||
CONFIG_SUBOBJECT(Utilities, utilities)
|
||||
|
||||
public:
|
||||
explicit Config(QObject* parent = nullptr);
|
||||
static Config* create(QQmlEngine*, QJSEngine*);
|
||||
[[nodiscard]] static Config* instance();
|
||||
|
||||
Q_INVOKABLE void load();
|
||||
Q_INVOKABLE void saveNow();
|
||||
Q_INVOKABLE void reloadAsync();
|
||||
|
||||
private Q_SLOTS:
|
||||
void scheduleSave();
|
||||
void flushAsync();
|
||||
void onWatcherEvent(const QString& path);
|
||||
|
||||
private:
|
||||
QString filePath() const;
|
||||
bool writeAtomically(const QByteArray& data);
|
||||
void updateWatch();
|
||||
void loadSync();
|
||||
void loadAsync();
|
||||
void connectAutoSave(ConfigNode* node);
|
||||
|
||||
QTimer m_saveTimer;
|
||||
QTimer m_reloadTimer;
|
||||
QFileSystemWatcher m_watcher;
|
||||
|
||||
QMutex m_writeMutex;
|
||||
QByteArray m_lastWriteHash;
|
||||
|
||||
bool m_reloadPending = false;
|
||||
bool m_loading = false;
|
||||
bool m_firstLoadDone = false;
|
||||
QFuture<void> m_loadFuture;
|
||||
|
||||
static Config* s_instance;
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -1,314 +0,0 @@
|
||||
#include "configlist.hpp"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QMetaObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QString>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
ConfigList::ConfigList(QObject* parent, const QVariantList& defaults)
|
||||
: ConfigNode(parent), m_defaults(QJsonArray::fromVariantList(defaults)) {}
|
||||
|
||||
int ConfigList::count() const {
|
||||
return static_cast<int>(m_items.size());
|
||||
}
|
||||
|
||||
QVariantList ConfigList::values() const {
|
||||
QVariantList vals;
|
||||
vals.reserve(m_items.size());
|
||||
for (auto* const item : m_items)
|
||||
vals.append(QVariant::fromValue(item));
|
||||
return vals;
|
||||
}
|
||||
|
||||
ConfigObject* ConfigList::itemAt(int index) const {
|
||||
if (index < 0 || index >= m_items.size()) return nullptr;
|
||||
|
||||
return m_items.at(index);
|
||||
}
|
||||
|
||||
const QList<ConfigObject*>& ConfigList::items() const {
|
||||
return m_items;
|
||||
}
|
||||
|
||||
void ConfigList::remove(int index) {
|
||||
if (index < 0 || index >= m_items.size()) {
|
||||
qCWarning(lcConfig)
|
||||
<< metaObject()->className() << "cannot remove index" << index
|
||||
<< "of" << count();
|
||||
return;
|
||||
}
|
||||
|
||||
destroyItem(m_items.takeAt(index));
|
||||
|
||||
m_loaded = true;
|
||||
emit countChanged();
|
||||
emit valuesChanged();
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
void ConfigList::move(int from, int to) {
|
||||
if (from < 0 || from >= m_items.size() || to < 0 || to >= m_items.size()) {
|
||||
qCWarning(lcConfig) << metaObject()->className() << "cannot move"
|
||||
<< from << "to" << to << "of" << count();
|
||||
return;
|
||||
}
|
||||
|
||||
if (from == to) return;
|
||||
|
||||
m_items.move(from, to);
|
||||
|
||||
m_loaded = true;
|
||||
emit valuesChanged();
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
void ConfigList::clear() {
|
||||
if (m_items.isEmpty()) return;
|
||||
|
||||
destroyItems();
|
||||
|
||||
m_loaded = true;
|
||||
emit countChanged();
|
||||
emit valuesChanged();
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
void ConfigList::loadFromJson(const QJsonValue& json) {
|
||||
if (!json.isArray()) {
|
||||
qCWarning(
|
||||
lcConfig,
|
||||
"Option '%s' must be a list, resetting to default",
|
||||
qUtf8Printable(propertyPath()));
|
||||
|
||||
resetToDefaults();
|
||||
materializeDefaults();
|
||||
return;
|
||||
}
|
||||
|
||||
populate(json.toArray());
|
||||
m_loaded = true;
|
||||
}
|
||||
|
||||
QJsonValue ConfigList::toJson() const {
|
||||
if (m_loaded) return elementsToJson();
|
||||
|
||||
return QJsonValue::Undefined;
|
||||
}
|
||||
|
||||
void ConfigList::clearLoadedKeys() {
|
||||
m_loaded = false;
|
||||
}
|
||||
|
||||
QStringList ConfigList::unknownKeys() const {
|
||||
QStringList keys;
|
||||
|
||||
for (int i = 0; i < m_items.size(); ++i) {
|
||||
const auto childKeys = m_items.at(i)->unknownKeys();
|
||||
for (const auto& childKey : childKeys)
|
||||
keys.append(joinPath(QString("[%1]").arg(i), childKey));
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
void ConfigList::materializeDefaults() {
|
||||
for (auto* const item : m_items)
|
||||
item->materializeDefaults();
|
||||
|
||||
if (m_global) return;
|
||||
|
||||
m_loaded = true;
|
||||
}
|
||||
|
||||
void ConfigList::resyncFromGlobal() {
|
||||
syncValuesFromGlobal();
|
||||
}
|
||||
|
||||
ConfigObject* ConfigList::insertItem(const QVariantMap& props, int index) {
|
||||
const auto pos = index < 0 || index > m_items.size() ? m_items.size()
|
||||
: index;
|
||||
|
||||
appendItem(QJsonObject::fromVariantMap(props));
|
||||
m_items.move(m_items.size() - 1, pos);
|
||||
|
||||
auto* const item = m_items.at(pos);
|
||||
const auto unknown = item->unknownKeys();
|
||||
for (const auto& key : unknown)
|
||||
qCWarning(lcConfig) << "Unknown option" << key << "for"
|
||||
<< item->metaObject()->className();
|
||||
|
||||
m_loaded = true;
|
||||
emit countChanged();
|
||||
emit valuesChanged();
|
||||
notifyChanged();
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
QJsonArray ConfigList::elementsToJson() const {
|
||||
QJsonArray arr;
|
||||
|
||||
for (auto* const item : m_items)
|
||||
arr.append(item->toJson().toObject());
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
void ConfigList::resetToDefaults() {
|
||||
loadFromJsonQuietly(m_defaults);
|
||||
m_loaded = false;
|
||||
}
|
||||
|
||||
void ConfigList::syncValuesFromGlobal() {
|
||||
if (m_loaded) return;
|
||||
|
||||
if (auto* const global = qobject_cast<ConfigList*>(m_global))
|
||||
populate(global->elementsToJson());
|
||||
}
|
||||
|
||||
void ConfigList::onGlobalPropertiesChanged(const QMap<QString, QVariant>&) {
|
||||
syncValuesFromGlobal();
|
||||
}
|
||||
|
||||
QString ConfigList::childPath(const ConfigNode* child) const {
|
||||
for (int i = 0; i < m_items.size(); ++i)
|
||||
if (m_items.at(i) == child) return QString("[%1]").arg(i);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void ConfigList::populate(const QJsonArray& arr) {
|
||||
const auto current = elementsToJson();
|
||||
if (current == arr) return;
|
||||
|
||||
const auto old = m_items;
|
||||
QList<bool> reused(old.size(), false);
|
||||
|
||||
m_items.clear();
|
||||
m_items.reserve(arr.size());
|
||||
|
||||
for (int i = 0; i < arr.size(); ++i) {
|
||||
auto match =
|
||||
i < old.size() && !reused.at(i) && current.at(i) == arr.at(i) ? i
|
||||
: -1;
|
||||
|
||||
for (int j = 0; match < 0 && j < old.size(); ++j)
|
||||
if (!reused.at(j) && current.at(j) == arr.at(i)) match = j;
|
||||
|
||||
auto update = false;
|
||||
for (int j = 0; match < 0 && j < old.size(); ++j) {
|
||||
if (!reused.at(j) &&
|
||||
sameElement(old.at(j), current.at(j), arr.at(i))) {
|
||||
match = j;
|
||||
update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (match < 0) {
|
||||
appendItem(arr.at(i));
|
||||
continue;
|
||||
}
|
||||
|
||||
reused[match] = true;
|
||||
|
||||
if (update) old.at(match)->loadFromJsonQuietly(arr.at(i));
|
||||
|
||||
m_items.append(old.at(match));
|
||||
}
|
||||
|
||||
for (int i = 0; i < old.size(); ++i)
|
||||
if (!reused.at(i)) destroyItem(old.at(i));
|
||||
|
||||
if (m_items.size() != old.size()) emit countChanged();
|
||||
|
||||
emit valuesChanged();
|
||||
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
bool ConfigList::sameElement(
|
||||
const ConfigObject* item,
|
||||
const QJsonValue& current,
|
||||
const QJsonValue& json) {
|
||||
const auto keys = item->identityKeys();
|
||||
if (keys.isEmpty()) return false;
|
||||
|
||||
const auto currentObj = current.toObject();
|
||||
const auto newObj = json.toObject();
|
||||
|
||||
if (currentObj.keys() != newObj.keys()) return false;
|
||||
|
||||
for (const auto& key : keys)
|
||||
if (!newObj.contains(key) || currentObj.value(key) != newObj.value(key))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConfigList::appendItem(const QJsonValue& json) {
|
||||
auto* const item = createItem();
|
||||
QQmlEngine::setObjectOwnership(item, QQmlEngine::CppOwnership);
|
||||
|
||||
if (m_items.isEmpty()) rejectGlobalOnlyElement(item);
|
||||
|
||||
item->loadFromJsonQuietly(json);
|
||||
|
||||
connectItem(item);
|
||||
|
||||
m_items.append(item);
|
||||
}
|
||||
|
||||
void ConfigList::rejectGlobalOnlyElement(const ConfigObject* item) const {
|
||||
const auto keys = item->globalOnlyKeys();
|
||||
if (keys.isEmpty()) return;
|
||||
|
||||
qCCritical(
|
||||
lcConfig,
|
||||
"%s declares global-only options (%s) which do not work inside a list, "
|
||||
"use CONFIG_GLOBAL_LIST on %s instead",
|
||||
item->metaObject()->className(),
|
||||
qUtf8Printable(keys.join(", ")),
|
||||
metaObject()->className());
|
||||
}
|
||||
|
||||
void ConfigList::connectItem(ConfigNode* node) {
|
||||
connect(
|
||||
node, &ConfigNode::propertiesChanged, this, &ConfigList::onItemChanged);
|
||||
|
||||
const auto children = node->childNodes();
|
||||
for (auto* const child : children)
|
||||
connectItem(child);
|
||||
}
|
||||
|
||||
void ConfigList::disconnectItem(ConfigNode* node) {
|
||||
node->disconnect(this);
|
||||
|
||||
const auto children = node->childNodes();
|
||||
for (auto* const child : children)
|
||||
disconnectItem(child);
|
||||
}
|
||||
|
||||
void ConfigList::destroyItems() {
|
||||
for (auto* const item : std::as_const(m_items))
|
||||
destroyItem(item);
|
||||
|
||||
m_items.clear();
|
||||
}
|
||||
|
||||
void ConfigList::destroyItem(ConfigObject* item) {
|
||||
disconnectItem(item);
|
||||
item->deleteLater();
|
||||
}
|
||||
|
||||
void ConfigList::onItemChanged() {
|
||||
m_loaded = true;
|
||||
notifyChanged();
|
||||
}
|
||||
|
||||
void ConfigList::notifyChanged() {
|
||||
notifyPropertyChanged("values", count());
|
||||
}
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -1,139 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "configobject.hpp"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QVariantList>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class ConfigList : public ConfigNode {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
|
||||
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||
Q_PROPERTY(QVariantList values READ values NOTIFY valuesChanged)
|
||||
|
||||
public:
|
||||
explicit ConfigList(
|
||||
QObject* parent = nullptr, const QVariantList& defaults = {});
|
||||
|
||||
[[nodiscard]] int count() const;
|
||||
[[nodiscard]] QVariantList values() const;
|
||||
[[nodiscard]] ConfigObject* itemAt(int index) const;
|
||||
[[nodiscard]] const QList<ConfigObject*>& items() const;
|
||||
|
||||
Q_INVOKABLE void remove(int index);
|
||||
Q_INVOKABLE void move(int from, int to);
|
||||
Q_INVOKABLE void clear();
|
||||
|
||||
void loadFromJson(const QJsonValue& json) override;
|
||||
[[nodiscard]] QJsonValue toJson() const override;
|
||||
void clearLoadedKeys() override;
|
||||
[[nodiscard]] QStringList unknownKeys() const override;
|
||||
void materializeDefaults() override;
|
||||
void resyncFromGlobal() override;
|
||||
|
||||
signals:
|
||||
void countChanged();
|
||||
void valuesChanged();
|
||||
|
||||
protected:
|
||||
virtual ConfigObject* createItem() = 0;
|
||||
|
||||
void resetToDefaults();
|
||||
|
||||
ConfigObject* insertItem(const QVariantMap& props, int index = -1);
|
||||
|
||||
[[nodiscard]] QJsonArray elementsToJson() const;
|
||||
|
||||
void syncValuesFromGlobal() override;
|
||||
void onGlobalPropertiesChanged(
|
||||
const QMap<QString, QVariant>& changed) override;
|
||||
[[nodiscard]] QString childPath(const ConfigNode* child) const override;
|
||||
|
||||
private:
|
||||
void populate(const QJsonArray& arr);
|
||||
static bool sameElement(
|
||||
const ConfigObject* item,
|
||||
const QJsonValue& current,
|
||||
const QJsonValue& json);
|
||||
void appendItem(const QJsonValue& json);
|
||||
void destroyItems();
|
||||
void destroyItem(ConfigObject* item);
|
||||
void rejectGlobalOnlyElement(const ConfigObject* item) const;
|
||||
void connectItem(ConfigNode* node);
|
||||
void disconnectItem(ConfigNode* node);
|
||||
void onItemChanged();
|
||||
void notifyChanged();
|
||||
|
||||
QJsonArray m_defaults;
|
||||
QList<ConfigObject*> m_items;
|
||||
bool m_loaded = false;
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
#define CONFIG_LIST_TYPE(Element, Name) \
|
||||
class Name : public ZShell::config::ConfigList { \
|
||||
Q_OBJECT \
|
||||
QML_ANONYMOUS \
|
||||
\
|
||||
public: \
|
||||
explicit Name( \
|
||||
QObject* parent = nullptr, const QVariantList& defaults = {}) \
|
||||
: ZShell::config::ConfigList(parent, defaults) { \
|
||||
resetToDefaults(); \
|
||||
} \
|
||||
\
|
||||
Q_INVOKABLE ZShell::config::Element* at(int index) const { \
|
||||
return static_cast<ZShell::config::Element*>(itemAt(index)); \
|
||||
} \
|
||||
Q_INVOKABLE ZShell::config::Element* insert( \
|
||||
const QVariantMap& props, int index = -1) { \
|
||||
return static_cast<ZShell::config::Element*>( \
|
||||
insertItem(props, index)); \
|
||||
} \
|
||||
\
|
||||
protected: \
|
||||
[[nodiscard]] ZShell::config::ConfigObject* createItem() override { \
|
||||
return new ZShell::config::Element(this); \
|
||||
} \
|
||||
};
|
||||
|
||||
#define CONFIG_LIST(Type, name, ...) \
|
||||
Q_PROPERTY(ZShell::config::Type* name READ name CONSTANT) \
|
||||
\
|
||||
public: \
|
||||
[[nodiscard]] Type* name() const { \
|
||||
return m_##name; \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
Type* m_##name = new Type(this __VA_OPT__(, __VA_ARGS__));
|
||||
|
||||
#define LIST_ENTRY(id, enabled) \
|
||||
vmap({{"id", QString::fromUtf8(#id)}, {"enabled", enabled}})
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class ListEntry : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
|
||||
CFG_PROPERTY(QString, id)
|
||||
CFG_PROPERTY(bool, enabled, true)
|
||||
|
||||
public:
|
||||
explicit ListEntry(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
|
||||
[[nodiscard]] QStringList identityKeys() const override {
|
||||
return {QStringLiteral("id")};
|
||||
}
|
||||
};
|
||||
|
||||
CONFIG_LIST_TYPE(ListEntry, EntryList)
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -1,89 +0,0 @@
|
||||
#include "confignode.hpp"
|
||||
|
||||
#include <qjsonvalue.h>
|
||||
#include <qloggingcategory.h>
|
||||
#include <qmetaobject.h>
|
||||
#include <qstring.h>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
Q_LOGGING_CATEGORY(lcConfig, "zshell.config")
|
||||
|
||||
ConfigNode::ConfigNode(QObject* parent) : QObject(parent) {
|
||||
m_batchTimer = new QTimer(this);
|
||||
m_batchTimer->setSingleShot(true);
|
||||
m_batchTimer->setInterval(0);
|
||||
connect(
|
||||
m_batchTimer, &QTimer::timeout, this, &ConfigNode::emitBatchedChanges);
|
||||
}
|
||||
|
||||
void ConfigNode::loadFromJsonQuietly(const QJsonValue& json) {
|
||||
setNotifySuppressed(true);
|
||||
loadFromJson(json);
|
||||
setNotifySuppressed(false);
|
||||
}
|
||||
|
||||
void ConfigNode::setNotifySuppressed(bool suppressed) {
|
||||
m_suppressNotify = suppressed;
|
||||
|
||||
const auto children = childNodes();
|
||||
for (auto* const child : children)
|
||||
child->setNotifySuppressed(suppressed);
|
||||
}
|
||||
|
||||
QList<ConfigNode*> ConfigNode::childNodes() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
void ConfigNode::syncFromGlobal(ConfigNode* global) {
|
||||
m_global = global;
|
||||
resyncFromGlobal();
|
||||
}
|
||||
|
||||
bool ConfigNode::isOverlay() const {
|
||||
return m_global != nullptr && this != m_global;
|
||||
}
|
||||
|
||||
QString ConfigNode::propertyPath(const QString& name) const {
|
||||
if (name.isEmpty()) return objectName();
|
||||
|
||||
QObject* const parentObj = parent();
|
||||
if (parentObj) {
|
||||
if (auto* parentNode = qobject_cast<ConfigNode*>(parentObj)) {
|
||||
const auto childName = parentNode->childPath(this);
|
||||
if (!childName.isEmpty()) {
|
||||
return joinPath(parentNode->propertyPath(childName), name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
int ConfigNode::basePropertyOffset() {
|
||||
return QObject::staticMetaObject.propertyCount();
|
||||
}
|
||||
|
||||
void ConfigNode::notifyPropertyChanged(
|
||||
const QString& name, const QVariant& value) {
|
||||
if (m_suppressNotify) return;
|
||||
|
||||
m_pendingChanges[name] = value;
|
||||
if (m_batchTimer && !m_batchTimer->isActive()) m_batchTimer->start();
|
||||
}
|
||||
|
||||
QString ConfigNode::joinPath(const QString& parent, const QString& child) {
|
||||
// List elements are addressed as parent[i], everything else as parent.child
|
||||
if (child.startsWith(QLatin1Char('['))) return parent + child;
|
||||
|
||||
return parent + QLatin1Char('.') + child;
|
||||
}
|
||||
|
||||
void ConfigNode::emitBatchedChanges() {
|
||||
if (m_pendingChanges.isEmpty()) return;
|
||||
|
||||
QMap<QString, QVariant> changes = m_pendingChanges;
|
||||
m_pendingChanges.clear();
|
||||
emit propertiesChanged(changes);
|
||||
}
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -1,65 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QJsonValue>
|
||||
#include <QLoggingCategory>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
#include <QTimer>
|
||||
#include <QVariant>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(lcConfig)
|
||||
|
||||
class ConfigNode : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigNode(QObject* parent = nullptr);
|
||||
|
||||
virtual void loadFromJson(const QJsonValue& json) = 0;
|
||||
void loadFromJsonQuietly(const QJsonValue& json);
|
||||
[[nodiscard]] virtual QJsonValue toJson() const = 0;
|
||||
|
||||
virtual void clearLoadedKeys() = 0;
|
||||
[[nodiscard]] virtual QStringList unknownKeys() const = 0;
|
||||
[[nodiscard]] virtual QList<ConfigNode*> childNodes() const;
|
||||
|
||||
virtual void materializeDefaults() = 0;
|
||||
|
||||
void syncFromGlobal(ConfigNode* global);
|
||||
virtual void resyncFromGlobal() = 0;
|
||||
|
||||
[[nodiscard]] bool isOverlay() const;
|
||||
[[nodiscard]] QString propertyPath(const QString& name = {}) const;
|
||||
|
||||
[[nodiscard]] static int basePropertyOffset();
|
||||
|
||||
signals:
|
||||
void propertiesChanged(const QMap<QString, QVariant>& changed);
|
||||
|
||||
protected:
|
||||
virtual void syncValuesFromGlobal() = 0;
|
||||
virtual void onGlobalPropertiesChanged(
|
||||
const QMap<QString, QVariant>& changed) = 0;
|
||||
|
||||
[[nodiscard]] virtual QString childPath(const ConfigNode* child) const = 0;
|
||||
|
||||
void notifyPropertyChanged(const QString& name, const QVariant& value);
|
||||
|
||||
[[nodiscard]] static QString joinPath(
|
||||
const QString& parent, const QString& child);
|
||||
|
||||
ConfigNode* m_global = nullptr;
|
||||
|
||||
private:
|
||||
void setNotifySuppressed(bool suppressed);
|
||||
void emitBatchedChanges();
|
||||
|
||||
QMap<QString, QVariant> m_pendingChanges;
|
||||
QTimer* m_batchTimer = nullptr;
|
||||
bool m_suppressNotify = false;
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -1,344 +0,0 @@
|
||||
#include "configobject.hpp"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QMetaObject>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
namespace {
|
||||
|
||||
bool isStringArray(const QJsonArray& arr) {
|
||||
for (const auto& v : arr) {
|
||||
if (!v.isString()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool jsonValueMatchesType(const QJsonValue& val, QMetaType type) {
|
||||
switch (val.type()) {
|
||||
case QJsonValue::Bool:
|
||||
return type.id() == QMetaType::Bool;
|
||||
|
||||
case QJsonValue::Double:
|
||||
switch (type.id()) {
|
||||
case QMetaType::Int:
|
||||
case QMetaType::UInt:
|
||||
case QMetaType::LongLong:
|
||||
case QMetaType::ULongLong:
|
||||
case QMetaType::Double:
|
||||
case QMetaType::Float:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
case QJsonValue::String:
|
||||
return type.id() == QMetaType::QString;
|
||||
|
||||
case QJsonValue::Array:
|
||||
if (type.id() == QMetaType::QStringList)
|
||||
return isStringArray(val.toArray());
|
||||
return type.id() == QMetaType::QVariantList;
|
||||
|
||||
case QJsonValue::Object:
|
||||
case QJsonValue::Null:
|
||||
case QJsonValue::Undefined:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ConfigObject::ConfigObject(QObject* parent) : ConfigNode(parent) {}
|
||||
|
||||
void ConfigObject::loadFromJson(const QJsonValue& json) {
|
||||
const auto obj = json.toObject();
|
||||
const auto* meta = metaObject();
|
||||
|
||||
QSet<QString> known;
|
||||
QSet<QString> invalid;
|
||||
|
||||
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
||||
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)) {
|
||||
if (auto* const node = prop.read(this).value<ConfigNode*>()) {
|
||||
node->loadFromJson(jsonVal);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!prop.isWritable()) continue;
|
||||
|
||||
if (!jsonValueMatchesType(jsonVal, prop.metaType())) {
|
||||
qWarning() << "Config: type mismatch for" << key << "in"
|
||||
<< meta->className() << "- expected"
|
||||
<< prop.metaType().name() << "got value" << jsonVal
|
||||
<< "- resetting to default";
|
||||
invalid.insert(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prop.metaType().id() == QMetaType::QStringList) {
|
||||
QStringList list;
|
||||
const auto jsonArr = jsonVal.toArray();
|
||||
for (const auto& v : jsonArr)
|
||||
list.append(v.toString());
|
||||
prop.write(this, QVariant::fromValue(list));
|
||||
m_loadedKeys.insert(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prop.metaType().id() == QMetaType::QVariantList) {
|
||||
prop.write(this, jsonVal.toVariant());
|
||||
m_loadedKeys.insert(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
prop.write(this, jsonVal.toVariant());
|
||||
m_loadedKeys.insert(key);
|
||||
}
|
||||
|
||||
m_extras = {};
|
||||
for (auto it = obj.begin(); it != obj.end(); ++it) {
|
||||
if (!known.contains(it.key()) || invalid.contains(it.key())) {
|
||||
m_extras.insert(it.key(), it.value());
|
||||
}
|
||||
}
|
||||
|
||||
materializeDefaults();
|
||||
}
|
||||
|
||||
QJsonValue ConfigObject::toJson() const {
|
||||
QJsonObject obj;
|
||||
const auto* meta = metaObject();
|
||||
|
||||
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
||||
const auto prop = meta->property(i);
|
||||
|
||||
if (!prop.isReadable()) continue;
|
||||
|
||||
const auto key = QString::fromUtf8(prop.name());
|
||||
|
||||
const auto value = prop.read(this);
|
||||
|
||||
if (prop.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
|
||||
if (auto* const node = value.value<ConfigNode*>()) {
|
||||
const auto childJson = node->toJson();
|
||||
if (!childJson.isUndefined()) obj.insert(key, childJson);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_loadedKeys.contains(key)) continue;
|
||||
|
||||
if (!prop.isWritable()) continue;
|
||||
|
||||
if (prop.metaType().id() == QMetaType::QStringList) {
|
||||
QJsonArray arr;
|
||||
const auto strList = value.toStringList();
|
||||
for (const auto& s : strList)
|
||||
arr.append(s);
|
||||
obj.insert(key, arr);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prop.metaType().id() == QMetaType::QVariantList) {
|
||||
obj.insert(key, QJsonArray::fromVariantList(value.toList()));
|
||||
continue;
|
||||
}
|
||||
|
||||
obj.insert(key, QJsonValue::fromVariant(value));
|
||||
}
|
||||
|
||||
if (obj.isEmpty()) return QJsonValue::Undefined;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ConfigObject::clearLoadedKeys() {
|
||||
m_loadedKeys.clear();
|
||||
m_extras = {};
|
||||
|
||||
const auto* meta = metaObject();
|
||||
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
||||
auto prop = meta->property(i);
|
||||
if (auto* const node = prop.read(this).value<ConfigNode*>())
|
||||
node->clearLoadedKeys();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList ConfigObject::identityKeys() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
QStringList ConfigObject::unknownKeys() const {
|
||||
auto keys = m_extras.keys();
|
||||
|
||||
const auto* meta = metaObject();
|
||||
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
||||
const auto prop = meta->property(i);
|
||||
auto* const node = prop.read(this).value<ConfigNode*>();
|
||||
if (!node) continue;
|
||||
|
||||
const auto childKeys = node->unknownKeys();
|
||||
for (const auto& childKey : childKeys)
|
||||
keys.append(joinPath(QString::fromUtf8(prop.name()), childKey));
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
QList<ConfigNode*> ConfigObject::childNodes() const {
|
||||
QList<ConfigNode*> nodes;
|
||||
|
||||
const auto* meta = metaObject();
|
||||
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
||||
const auto prop = meta->property(i);
|
||||
|
||||
if (auto* const node = prop.read(this).value<ConfigNode*>())
|
||||
nodes.append(node);
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
void ConfigObject::materializeDefaults() {
|
||||
const auto* meta = metaObject();
|
||||
|
||||
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*>()) {
|
||||
node->materializeDefaults();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!prop.isWritable()) continue;
|
||||
|
||||
if (m_global) continue;
|
||||
|
||||
m_loadedKeys.insert(key);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigObject::syncValuesFromGlobal() {
|
||||
const auto* meta = metaObject();
|
||||
|
||||
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
||||
auto prop = meta->property(i);
|
||||
const auto key = QString::fromUtf8(prop.name());
|
||||
|
||||
if (auto* const node = prop.read(this).value<ConfigNode*>()) {
|
||||
if (auto* const globalNode =
|
||||
prop.read(m_global).value<ConfigNode*>())
|
||||
node->syncFromGlobal(globalNode);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!prop.isWritable()) continue;
|
||||
|
||||
if (!m_loadedKeys.contains(key)) {
|
||||
auto val = prop.read(m_global);
|
||||
prop.write(this, val);
|
||||
m_loadedKeys.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigObject::resyncFromGlobal() {
|
||||
if (!m_global) return;
|
||||
|
||||
const auto* meta = metaObject();
|
||||
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
||||
auto prop = meta->property(i);
|
||||
const auto key = QString::fromUtf8(prop.name());
|
||||
|
||||
if (auto* const node = prop.read(this).value<ConfigNode*>()) {
|
||||
node->resyncFromGlobal();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!prop.isWritable()) continue;
|
||||
|
||||
if (!m_loadedKeys.contains(key)) {
|
||||
prop.write(this, prop.read(m_global));
|
||||
m_loadedKeys.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigObject::isPropertyLoaded(const QString& name) const {
|
||||
return m_loadedKeys.contains(name);
|
||||
}
|
||||
|
||||
bool ConfigObject::isGlobalOnly(const QString& name) const {
|
||||
return m_globalOnlyKeys.contains(name);
|
||||
}
|
||||
|
||||
QStringList ConfigObject::globalOnlyKeys() const {
|
||||
return {m_globalOnlyKeys.begin(), m_globalOnlyKeys.end()};
|
||||
}
|
||||
|
||||
void ConfigObject::resetOption(const QString& name) {
|
||||
m_loadedKeys.remove(name);
|
||||
|
||||
const int idx = metaObject()->indexOfProperty(name.toUtf8().constData());
|
||||
if (idx < 0) return;
|
||||
|
||||
const auto prop = metaObject()->property(idx);
|
||||
|
||||
if (auto* const node = prop.read(this).value<ConfigNode*>()) {
|
||||
node->clearLoadedKeys();
|
||||
node->resyncFromGlobal();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_global && prop.isWritable()) prop.write(this, prop.read(m_global));
|
||||
}
|
||||
|
||||
void ConfigObject::onGlobalPropertiesChanged(
|
||||
const QMap<QString, QVariant>& changed) {
|
||||
for (auto it = changed.begin(); it != changed.end(); ++it) {
|
||||
if (m_loadedKeys.contains(it.key()) || isGlobalOnly(it.key())) continue;
|
||||
|
||||
int idx = metaObject()->indexOfProperty(it.key().toUtf8().constData());
|
||||
if (idx >= 0) {
|
||||
metaObject()->property(idx).write(this, it.value());
|
||||
m_loadedKeys.remove(it.key());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString ConfigObject::childPath(const ConfigNode* child) const {
|
||||
const auto* meta = metaObject();
|
||||
for (int i = basePropertyOffset(); i < meta->propertyCount(); ++i) {
|
||||
const auto prop = meta->property(i);
|
||||
|
||||
if (prop.read(this).value<ConfigNode*>() == child)
|
||||
return QString::fromUtf8(prop.name());
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void ConfigObject::markPropertyLoaded(const QString& name) {
|
||||
m_loadedKeys.insert(name);
|
||||
}
|
||||
|
||||
void ConfigObject::markGlobalOnly(const QString& name) {
|
||||
m_globalOnlyKeys.insert(name);
|
||||
}
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -1,101 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "confignode.hpp"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QMetaProperty>
|
||||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
#include <QVariantList>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
inline QVariantMap vmap(
|
||||
std::initializer_list<std::pair<QString, QVariant>> entries) {
|
||||
QVariantMap map;
|
||||
for (const auto& [key, value] : entries)
|
||||
map.insert(key, value);
|
||||
return map;
|
||||
}
|
||||
|
||||
#define CFG_PROPERTY(Type, name, ...) \
|
||||
Q_PROPERTY(Type name READ name WRITE set_##name NOTIFY name##Changed) \
|
||||
public: \
|
||||
[[nodiscard]] Type name() const { \
|
||||
return m_##name; \
|
||||
} \
|
||||
void set_##name(const Type& value) { \
|
||||
if (ConfigObject::updateMember(m_##name, value)) { \
|
||||
markPropertyLoaded(QStringLiteral(#name)); \
|
||||
Q_EMIT name##Changed(); \
|
||||
notifyPropertyChanged( \
|
||||
QStringLiteral(#name), QVariant::fromValue(m_##name)); \
|
||||
} \
|
||||
} \
|
||||
Q_SIGNAL void name##Changed(); \
|
||||
\
|
||||
private: \
|
||||
Type m_##name __VA_OPT__(= __VA_ARGS__);
|
||||
|
||||
#define CONFIG_SUBOBJECT(Type, name) \
|
||||
Q_PROPERTY(ZShell::config::Type* name READ name CONSTANT) \
|
||||
\
|
||||
public: \
|
||||
[[nodiscard]] Type* name() const { \
|
||||
return m_##name; \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
Type* m_##name = nullptr;
|
||||
|
||||
class ConfigObject : public ConfigNode {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigObject(QObject* parent = nullptr);
|
||||
|
||||
void loadFromJson(const QJsonValue& json) override;
|
||||
[[nodiscard]] QJsonValue toJson() const override;
|
||||
void clearLoadedKeys() override;
|
||||
[[nodiscard]] QStringList unknownKeys() const override;
|
||||
[[nodiscard]] QList<ConfigNode*> childNodes() const override;
|
||||
void materializeDefaults() override;
|
||||
void resyncFromGlobal() override;
|
||||
|
||||
[[nodiscard]] virtual QStringList identityKeys() const;
|
||||
|
||||
[[nodiscard]] bool isPropertyLoaded(const QString& name) const;
|
||||
[[nodiscard]] bool isGlobalOnly(const QString& name) const;
|
||||
[[nodiscard]] QStringList globalOnlyKeys() const;
|
||||
|
||||
Q_INVOKABLE void resetOption(const QString& name);
|
||||
|
||||
template <typename T> static bool updateMember(T& member, const T& value) {
|
||||
if constexpr (std::is_floating_point_v<T>) {
|
||||
if (qFuzzyCompare(member + 1.0, value + 1.0)) return false;
|
||||
} else {
|
||||
if (member == value) return false;
|
||||
}
|
||||
member = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
void syncValuesFromGlobal() override;
|
||||
void onGlobalPropertiesChanged(
|
||||
const QMap<QString, QVariant>& changed) override;
|
||||
[[nodiscard]] QString childPath(const ConfigNode* child) const override;
|
||||
|
||||
void markPropertyLoaded(const QString& name);
|
||||
void markGlobalOnly(const QString& name);
|
||||
|
||||
private:
|
||||
QSet<QString> m_loadedKeys;
|
||||
QSet<QString> m_globalOnlyKeys;
|
||||
QJsonObject m_extras;
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -1,63 +1,53 @@
|
||||
#pragma once
|
||||
#include "configobject.hpp"
|
||||
#include "common.hpp"
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class Performance : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Performance : public settings::ObjectNode {
|
||||
CONFIG_NODE(Performance, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(bool, enabled, true)
|
||||
CFG_PROPERTY(bool, showBattery, false)
|
||||
CFG_PROPERTY(bool, showCpu, true)
|
||||
CFG_PROPERTY(bool, showGpu, true)
|
||||
CFG_PROPERTY(bool, showMemory, true)
|
||||
CFG_PROPERTY(bool, showNetwork, true)
|
||||
CFG_PROPERTY(bool, showStorage, true)
|
||||
CFG_PROPERTY(bool, showVram, true)
|
||||
|
||||
public:
|
||||
explicit Performance(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(bool, enabled, true)
|
||||
CONFIG_PROPERTY(bool, showBattery, false)
|
||||
CONFIG_PROPERTY(bool, showCpu, true)
|
||||
CONFIG_PROPERTY(bool, showGpu, true)
|
||||
CONFIG_PROPERTY(bool, showMemory, true)
|
||||
CONFIG_PROPERTY(bool, showNetwork, true)
|
||||
CONFIG_PROPERTY(bool, showStorage, true)
|
||||
CONFIG_PROPERTY(bool, showVram, true)
|
||||
};
|
||||
|
||||
class DashboardSizes : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class ProcessColumnEntry : public settings::ObjectNode {
|
||||
CONFIG_NODE(ProcessColumnEntry, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(int, dateTimeWidth, 110)
|
||||
CFG_PROPERTY(int, infoIconSize, 25)
|
||||
CFG_PROPERTY(int, infoWidth, 200)
|
||||
CFG_PROPERTY(int, mediaCoverArtSize, 150)
|
||||
CFG_PROPERTY(int, mediaProgressSweep, 180)
|
||||
CFG_PROPERTY(int, mediaProgressThickness, 8)
|
||||
CFG_PROPERTY(int, mediaVisualizerSize, 200)
|
||||
CFG_PROPERTY(int, mediaWidth, 200)
|
||||
CFG_PROPERTY(int, resourceProgessThickness, 10)
|
||||
CFG_PROPERTY(int, resourceSize, 200)
|
||||
CFG_PROPERTY(int, tabIndicatorHeight, 3)
|
||||
CFG_PROPERTY(int, tabIndicatorSpacing, 5)
|
||||
CFG_PROPERTY(int, weatherWidth, 250)
|
||||
|
||||
public:
|
||||
explicit DashboardSizes(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(QString, id, {})
|
||||
CONFIG_PROPERTY(double, width, 0)
|
||||
};
|
||||
|
||||
class Dashboard : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
CONFIG_LIST_TYPE(ProcessColumnEntry, ProcessColumnList)
|
||||
|
||||
CFG_PROPERTY(bool, enabled, true)
|
||||
CFG_PROPERTY(int, mediaUpdateInterval, 2000)
|
||||
class Dashboard : public settings::ObjectNode {
|
||||
CONFIG_NODE(Dashboard, settings::ObjectNode)
|
||||
|
||||
CONFIG_PROPERTY(bool, enabled, true)
|
||||
CONFIG_PROPERTY(bool, showOnHover, true)
|
||||
CONFIG_PROPERTY(int, dragThreshold, 50)
|
||||
CONFIG_PROPERTY(int, mediaUpdateInterval, 2000)
|
||||
CONFIG_PROPERTY(bool, showWeather, true)
|
||||
CONFIG_PROPERTY(bool, showDashboard, true)
|
||||
CONFIG_PROPERTY(bool, showMedia, true)
|
||||
CONFIG_PROPERTY(bool, showProcesses, false)
|
||||
CONFIG_SUBOBJECT(Performance, performance)
|
||||
CFG_PROPERTY(int, resourceUpdateInterval, 1000)
|
||||
CONFIG_SUBOBJECT(DashboardSizes, sizes)
|
||||
|
||||
public:
|
||||
explicit Dashboard(QObject* parent = nullptr)
|
||||
: ConfigObject(parent)
|
||||
, m_performance(new Performance(this))
|
||||
, m_sizes(new DashboardSizes(this)) {}
|
||||
CONFIG_PROPERTY(int, resourceUpdateInterval, 1000)
|
||||
CONFIG_LIST(
|
||||
ProcessColumnList,
|
||||
processColumns,
|
||||
DEFAULT_ARG({
|
||||
settings::vmap({{"id", "command"}}),
|
||||
settings::vmap({{"id", "pid"}}),
|
||||
settings::vmap({{"id", "cpuUsage"}}),
|
||||
settings::vmap({{"id", "memoryUsage"}}),
|
||||
}))
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "common.hpp"
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class Nightlight : public settings::ObjectNode {
|
||||
CONFIG_NODE(Nightlight, settings::ObjectNode)
|
||||
|
||||
CONFIG_PROPERTY(bool, schedule, true)
|
||||
CONFIG_PROPERTY(int, scheduleStart, 1200)
|
||||
CONFIG_PROPERTY(int, scheduleEnd, 570)
|
||||
CONFIG_PROPERTY(bool, useNative, true)
|
||||
CONFIG_PROPERTY(int, fadeDuration, 2000)
|
||||
CONFIG_PROPERTY(int, temp, 2600)
|
||||
};
|
||||
|
||||
class Display : public settings::ObjectNode {
|
||||
CONFIG_NODE(Display, settings::ObjectNode)
|
||||
|
||||
CONFIG_SUBOBJECT(Nightlight, nightlight)
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -1,29 +1,25 @@
|
||||
#pragma once
|
||||
#include "configobject.hpp"
|
||||
#include "common.hpp"
|
||||
#include <QStringList>
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class Dock : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Dock : public settings::ObjectNode {
|
||||
CONFIG_NODE(Dock, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(bool, enable, false)
|
||||
CFG_PROPERTY(int, height, 80)
|
||||
CFG_PROPERTY(bool, hoverToReveal, false)
|
||||
CFG_PROPERTY(QStringList, ignoredAppRegexes, {})
|
||||
CFG_PROPERTY(
|
||||
CONFIG_PROPERTY(bool, enable, false)
|
||||
CONFIG_PROPERTY(int, height, 80)
|
||||
CONFIG_PROPERTY(bool, hoverToReveal, false)
|
||||
CONFIG_PROPERTY(QStringList, ignoredAppRegexes, {})
|
||||
CONFIG_PROPERTY(
|
||||
QStringList,
|
||||
pinnedApps,
|
||||
(QStringList{
|
||||
QStringLiteral("com.ayugram.desktop"),
|
||||
QStringLiteral("com.obsproject.studio"),
|
||||
QStringLiteral("librewolf")}))
|
||||
CFG_PROPERTY(bool, pinnedOnStartup, false)
|
||||
|
||||
public:
|
||||
explicit Dock(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(bool, pinnedOnStartup, false)
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <qobject.h>
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
#define ENUM(Name, ...) \
|
||||
namespace Name { \
|
||||
\
|
||||
Q_NAMESPACE \
|
||||
QML_ELEMENT \
|
||||
\
|
||||
enum Enum : quint8 { __VA_ARGS__ }; \
|
||||
Q_ENUM_NS(Enum) \
|
||||
};
|
||||
|
||||
ENUM(BarWorkspaceDisplay, Shapes, Text, Icons)
|
||||
ENUM(BarWorkspaceCapitalisation, Preserve, Upper, Lower)
|
||||
ENUM(LyricsBackend, Auto, Local, LRCLIB, NetEase)
|
||||
ENUM(GpuType, Auto, Nvidia, Generic, None)
|
||||
ENUM(NotifsFullscreen, On, Off)
|
||||
ENUM(TemperatureUnit, Auto, Celsius, Fahrenheit, Kelvin)
|
||||
ENUM(DataUnit, Binary, Decimal)
|
||||
ENUM(ClockFormat, Auto, TwelveHour, TwentyFourHour)
|
||||
|
||||
#undef ENUM
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -0,0 +1,288 @@
|
||||
#include "font.hpp"
|
||||
|
||||
#include "appearance.hpp"
|
||||
#include "rootnodes.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, &settings::Node::optionChanged, this, &FontStyleBase::rebuild);
|
||||
connect(
|
||||
cfg->largeNode(),
|
||||
&settings::Node::optionChanged,
|
||||
this,
|
||||
&FontStyleBase::rebuild);
|
||||
connect(
|
||||
cfg->mediumNode(),
|
||||
&settings::Node::optionChanged,
|
||||
this,
|
||||
&FontStyleBase::rebuild);
|
||||
connect(
|
||||
cfg->smallNode(),
|
||||
&settings::Node::optionChanged,
|
||||
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(),
|
||||
&settings::Node::optionChanged,
|
||||
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)
|
||||
: QObject(parent)
|
||||
, 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)) {
|
||||
bindFont(ConfigSingleton::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
|
||||
@@ -0,0 +1,182 @@
|
||||
#pragma once
|
||||
|
||||
#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 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 QObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,158 +1,103 @@
|
||||
#pragma once
|
||||
#include "configlist.hpp"
|
||||
#include "configobject.hpp"
|
||||
#include "common.hpp"
|
||||
#include <QStandardPaths>
|
||||
#include <QStringList>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class BatteryThresholdEntry : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class BatteryThresholdEntry : public settings::ObjectNode {
|
||||
CONFIG_NODE(BatteryThresholdEntry, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(QString, icon)
|
||||
CFG_PROPERTY(QString, message)
|
||||
CFG_PROPERTY(QString, name)
|
||||
CFG_PROPERTY(int, perc)
|
||||
|
||||
public:
|
||||
explicit BatteryThresholdEntry(QObject* parent = nullptr)
|
||||
: ConfigObject(parent) {}
|
||||
|
||||
[[nodiscard]] QStringList identityKeys() const override {
|
||||
return {QStringLiteral("perc")};
|
||||
}
|
||||
CONFIG_PROPERTY(QString, icon, {})
|
||||
CONFIG_PROPERTY(QString, message, {})
|
||||
CONFIG_PROPERTY(QString, name, {})
|
||||
CONFIG_PROPERTY(int, perc, {})
|
||||
};
|
||||
|
||||
CONFIG_LIST_TYPE(BatteryThresholdEntry, BatteryThresholdList)
|
||||
|
||||
class IdleTimeoutEntry : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class IdleTimeoutEntry : public settings::ObjectNode {
|
||||
CONFIG_NODE(IdleTimeoutEntry, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(QString, idleAction)
|
||||
CFG_PROPERTY(QString, name)
|
||||
CFG_PROPERTY(int, timeout)
|
||||
|
||||
public:
|
||||
explicit IdleTimeoutEntry(QObject* parent = nullptr)
|
||||
: ConfigObject(parent) {}
|
||||
|
||||
[[nodiscard]] QStringList identityKeys() const override {
|
||||
return {QStringLiteral("name")};
|
||||
}
|
||||
CONFIG_PROPERTY(QString, idleAction, {})
|
||||
CONFIG_PROPERTY(QString, name, {})
|
||||
CONFIG_PROPERTY(int, timeout, {})
|
||||
};
|
||||
|
||||
CONFIG_LIST_TYPE(IdleTimeoutEntry, IdleTimeoutList)
|
||||
|
||||
class Apps : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Apps : public settings::ObjectNode {
|
||||
CONFIG_NODE(Apps, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(QStringList, archiver, {QStringLiteral("ark")})
|
||||
CFG_PROPERTY(QStringList, audio, {QStringLiteral("pavucontrol")})
|
||||
CFG_PROPERTY(QStringList, document, {QStringLiteral("libreoffice")})
|
||||
CFG_PROPERTY(QStringList, editor, {QStringLiteral("kate")})
|
||||
CFG_PROPERTY(QStringList, explorer, {QStringLiteral("dolphin")})
|
||||
CFG_PROPERTY(QStringList, image, {QStringLiteral("imv")})
|
||||
CFG_PROPERTY(QStringList, playback, {QStringLiteral("vlc")})
|
||||
CFG_PROPERTY(QStringList, terminal, {QStringLiteral("wezterm")})
|
||||
|
||||
public:
|
||||
explicit Apps(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(QStringList, archiver, {QStringLiteral("ark")})
|
||||
CONFIG_PROPERTY(QStringList, audio, {QStringLiteral("pavucontrol")})
|
||||
CONFIG_PROPERTY(QStringList, document, {QStringLiteral("libreoffice")})
|
||||
CONFIG_PROPERTY(QStringList, editor, {QStringLiteral("kate")})
|
||||
CONFIG_PROPERTY(QStringList, explorer, {QStringLiteral("dolphin")})
|
||||
CONFIG_PROPERTY(QStringList, image, {QStringLiteral("imv")})
|
||||
CONFIG_PROPERTY(QStringList, playback, {QStringLiteral("vlc")})
|
||||
CONFIG_PROPERTY(QStringList, terminal, {QStringLiteral("wezterm")})
|
||||
};
|
||||
|
||||
class Battery : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Battery : public settings::ObjectNode {
|
||||
CONFIG_NODE(Battery, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(int, critPerc, 5)
|
||||
CONFIG_PROPERTY(int, critPerc, 5)
|
||||
CONFIG_LIST(
|
||||
BatteryThresholdList,
|
||||
popupThresholds,
|
||||
{
|
||||
vmap(
|
||||
DEFAULT_ARG({
|
||||
settings::vmap(
|
||||
{{"icon", "battery_android_frame_2"},
|
||||
{"message", "Battery low"},
|
||||
{"name", "Low battery"},
|
||||
{"perc", 20}}),
|
||||
vmap(
|
||||
settings::vmap(
|
||||
{{"icon", "battery_android_frame_2"},
|
||||
{"message", "Battery lower"},
|
||||
{"name", "Low battery"},
|
||||
{"perc", 15}}),
|
||||
vmap(
|
||||
settings::vmap(
|
||||
{{"icon", "battery_android_frame_2"},
|
||||
{"message", "Battery lowest"},
|
||||
{"name", "Low battery"},
|
||||
{"perc", 10}}),
|
||||
})
|
||||
|
||||
public:
|
||||
explicit Battery(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
}))
|
||||
};
|
||||
|
||||
class ColorSettings : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
|
||||
CFG_PROPERTY(int, hyprsunsetTemp, 2600)
|
||||
CFG_PROPERTY(QString, mode, QStringLiteral("dark"))
|
||||
CFG_PROPERTY(bool, neovimColors, false)
|
||||
CFG_PROPERTY(bool, scheduleDark, false)
|
||||
CFG_PROPERTY(int, scheduleDarkEnd, 600)
|
||||
CFG_PROPERTY(int, scheduleDarkStart, 1140)
|
||||
CFG_PROPERTY(bool, scheduleHyprsunset, true)
|
||||
CFG_PROPERTY(int, scheduleHyprsunsetEnd, 570)
|
||||
CFG_PROPERTY(int, scheduleHyprsunsetStart, 1200)
|
||||
CFG_PROPERTY(bool, schemeGeneration, true)
|
||||
CFG_PROPERTY(bool, smart, false)
|
||||
|
||||
public:
|
||||
explicit ColorSettings(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
};
|
||||
|
||||
class Idle : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Idle : public settings::ObjectNode {
|
||||
CONFIG_NODE(Idle, settings::ObjectNode)
|
||||
|
||||
CONFIG_LIST(
|
||||
IdleTimeoutList,
|
||||
timeouts,
|
||||
{
|
||||
vmap(
|
||||
DEFAULT_ARG({
|
||||
settings::vmap(
|
||||
{{"idleAction", QStringLiteral("lock")},
|
||||
{"name", QStringLiteral("Lock")},
|
||||
{"timeout", 180}}),
|
||||
})
|
||||
|
||||
public:
|
||||
explicit Idle(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
}))
|
||||
};
|
||||
|
||||
class General : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class General : public settings::ObjectNode {
|
||||
CONFIG_NODE(General, settings::ObjectNode)
|
||||
|
||||
CONFIG_SUBOBJECT(Apps, apps)
|
||||
CONFIG_SUBOBJECT(Battery, battery)
|
||||
CONFIG_SUBOBJECT(ColorSettings, color)
|
||||
CFG_PROPERTY(QString, dateFormat, QStringLiteral("ddd d MMM - hh:mm:ss"))
|
||||
CFG_PROPERTY(bool, desktopIcons, true)
|
||||
CONFIG_PROPERTY(QString, dateFormat, QStringLiteral("ddd d MMM - hh:mm:ss"))
|
||||
CONFIG_PROPERTY(bool, desktopIcons, true)
|
||||
CONFIG_SUBOBJECT(Idle, idle)
|
||||
CFG_PROPERTY(QString, logo, QString())
|
||||
CFG_PROPERTY(bool, showOverFullscreen, true)
|
||||
CFG_PROPERTY(
|
||||
CONFIG_PROPERTY(
|
||||
QString,
|
||||
lyricsDir,
|
||||
QStandardPaths::writableLocation(QStandardPaths::MusicLocation) +
|
||||
QStringLiteral("/Lyrics/"))
|
||||
CONFIG_PROPERTY(QString, logo, QString())
|
||||
CONFIG_PROPERTY(bool, showOverFullscreen, true)
|
||||
CONFIG_PROPERTY(
|
||||
QString,
|
||||
wallpaperPath,
|
||||
QStringLiteral("/mnt/IronWolf/SDImages/SWWW_Wals/"))
|
||||
|
||||
public:
|
||||
explicit General(QObject* parent = nullptr)
|
||||
: ConfigObject(parent)
|
||||
, m_apps(new Apps(this))
|
||||
, m_battery(new Battery(this))
|
||||
, m_color(new ColorSettings(this))
|
||||
, m_idle(new Idle(this)) {}
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -1,68 +1,53 @@
|
||||
#pragma once
|
||||
#include "configlist.hpp"
|
||||
#include "configobject.hpp"
|
||||
|
||||
#include "common.hpp"
|
||||
#include <QStringList>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class LauncherActionEntry : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
using Qt::StringLiterals::operator""_s;
|
||||
using settings::vmap;
|
||||
|
||||
CFG_PROPERTY(QVariantList, command)
|
||||
CFG_PROPERTY(bool, dangerous, false)
|
||||
CFG_PROPERTY(QString, description)
|
||||
CFG_PROPERTY(bool, enabled, true)
|
||||
CFG_PROPERTY(QString, icon)
|
||||
CFG_PROPERTY(QString, name)
|
||||
class LauncherActionEntry : public settings::ObjectNode {
|
||||
CONFIG_NODE(LauncherActionEntry, settings::ObjectNode)
|
||||
|
||||
public:
|
||||
explicit LauncherActionEntry(QObject* parent = nullptr)
|
||||
: ConfigObject(parent) {}
|
||||
|
||||
[[nodiscard]] QStringList identityKeys() const override {
|
||||
return {QStringLiteral("name")};
|
||||
}
|
||||
CONFIG_PROPERTY(QVariantList, command, {})
|
||||
CONFIG_PROPERTY(bool, dangerous, false)
|
||||
CONFIG_PROPERTY(QString, description, {})
|
||||
CONFIG_PROPERTY(bool, enabled, true)
|
||||
CONFIG_PROPERTY(QString, icon, {})
|
||||
CONFIG_PROPERTY(QString, name, {})
|
||||
};
|
||||
|
||||
CONFIG_LIST_TYPE(LauncherActionEntry, LauncherActionList)
|
||||
|
||||
class LauncherSizes : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class LauncherSizes : public settings::ObjectNode {
|
||||
CONFIG_NODE(LauncherSizes, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(int, itemHeight, 50)
|
||||
CFG_PROPERTY(int, itemWidth, 600)
|
||||
CFG_PROPERTY(int, wallpaperHeight, 200)
|
||||
CFG_PROPERTY(int, wallpaperWidth, 280)
|
||||
|
||||
public:
|
||||
explicit LauncherSizes(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(int, itemHeight, 50)
|
||||
CONFIG_PROPERTY(int, itemWidth, 600)
|
||||
CONFIG_PROPERTY(int, wallpaperHeight, 200)
|
||||
CONFIG_PROPERTY(int, wallpaperWidth, 280)
|
||||
};
|
||||
|
||||
class UseFuzzy : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class UseFuzzy : public settings::ObjectNode {
|
||||
CONFIG_NODE(UseFuzzy, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(bool, actions, true)
|
||||
CFG_PROPERTY(bool, apps, true)
|
||||
CFG_PROPERTY(bool, schemes, true)
|
||||
CFG_PROPERTY(bool, variants, true)
|
||||
CFG_PROPERTY(bool, wallpapers, true)
|
||||
|
||||
public:
|
||||
explicit UseFuzzy(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(bool, actions, true)
|
||||
CONFIG_PROPERTY(bool, apps, true)
|
||||
CONFIG_PROPERTY(bool, schemes, true)
|
||||
CONFIG_PROPERTY(bool, variants, true)
|
||||
CONFIG_PROPERTY(bool, wallpapers, true)
|
||||
};
|
||||
|
||||
class Launcher : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Launcher : public settings::ObjectNode {
|
||||
CONFIG_NODE(Launcher, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(QString, actionPrefix, QStringLiteral("<"))
|
||||
CONFIG_PROPERTY(QString, actionPrefix, QStringLiteral("<"))
|
||||
CONFIG_LIST(
|
||||
LauncherActionList,
|
||||
actions,
|
||||
{
|
||||
DEFAULT_ARG({
|
||||
vmap(
|
||||
{{"command", QVariantList{"autocomplete", "calc"}},
|
||||
{"dangerous", false},
|
||||
@@ -134,44 +119,18 @@ class Launcher : public ConfigObject {
|
||||
{"enabled", true},
|
||||
{"icon", "bedtime"},
|
||||
{"name", "Sleep"}}),
|
||||
})
|
||||
CFG_PROPERTY(int, dragThreshold, 150)
|
||||
CFG_PROPERTY(bool, enableDangerousActions, true)
|
||||
CFG_PROPERTY(bool, enabled, true)
|
||||
CFG_PROPERTY(
|
||||
QStringList,
|
||||
favoriteApps,
|
||||
{QStringLiteral("Balatro"),
|
||||
QStringLiteral("Slay the Spire 2"),
|
||||
QStringLiteral("com.obsproject.Studio")})
|
||||
CFG_PROPERTY(
|
||||
QStringList,
|
||||
hiddenApps,
|
||||
{QStringLiteral("zellij"),
|
||||
QStringLiteral("vim"),
|
||||
QStringLiteral("jshell-java-openjdk"),
|
||||
QStringLiteral("jconsole-java-openjdk"),
|
||||
QStringLiteral("jshell-java21-openjdk"),
|
||||
QStringLiteral("jconsole-java21-openjdk"),
|
||||
QStringLiteral("jshell-java17-openjdk"),
|
||||
QStringLiteral("jconsole-java17-openjdk"),
|
||||
QStringLiteral("org.neovim.nvim"),
|
||||
QStringLiteral("avahi-discover"),
|
||||
QStringLiteral("bvnc"),
|
||||
QStringLiteral("bssh"),
|
||||
QStringLiteral("nm-connection-editor")})
|
||||
CFG_PROPERTY(int, maxAppsShown, 18)
|
||||
CFG_PROPERTY(int, maxWallpapers, 7)
|
||||
}))
|
||||
CONFIG_PROPERTY(int, dragThreshold, 150)
|
||||
CONFIG_PROPERTY(bool, enableDangerousActions, true)
|
||||
CONFIG_PROPERTY(bool, enabled, true)
|
||||
CONFIG_PROPERTY(QStringList, favoriteApps, {})
|
||||
CONFIG_PROPERTY(QStringList, hiddenApps, {})
|
||||
CONFIG_PROPERTY(int, maxAppsShown, 18)
|
||||
CONFIG_PROPERTY(int, maxWallpapers, 7)
|
||||
CONFIG_SUBOBJECT(LauncherSizes, sizes)
|
||||
CFG_PROPERTY(QString, specialPrefix, QStringLiteral("@"))
|
||||
CONFIG_PROPERTY(QString, specialPrefix, QStringLiteral("@"))
|
||||
CONFIG_SUBOBJECT(UseFuzzy, useFuzzy)
|
||||
CFG_PROPERTY(bool, uwsm, true)
|
||||
|
||||
public:
|
||||
explicit Launcher(QObject* parent = nullptr)
|
||||
: ConfigObject(parent)
|
||||
, m_sizes(new LauncherSizes(this))
|
||||
, m_useFuzzy(new UseFuzzy(this)) {}
|
||||
CONFIG_PROPERTY(bool, uwsm, true)
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -1,36 +1,27 @@
|
||||
#pragma once
|
||||
#include "configobject.hpp"
|
||||
#include "common.hpp"
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class LockSizes : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class LockSizes : public settings::ObjectNode {
|
||||
CONFIG_NODE(LockSizes, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(int, centerWidth, 600)
|
||||
CFG_PROPERTY(qreal, heightMult, 0.7)
|
||||
CFG_PROPERTY(qreal, ratio, 1.78)
|
||||
|
||||
public:
|
||||
explicit LockSizes(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(int, centerWidth, 600)
|
||||
CONFIG_PROPERTY(qreal, heightMult, 0.7)
|
||||
CONFIG_PROPERTY(qreal, ratio, 1.78)
|
||||
};
|
||||
|
||||
class Lock : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Lock : public settings::ObjectNode {
|
||||
CONFIG_NODE(Lock, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(int, blurAmount, 10)
|
||||
CFG_PROPERTY(bool, enableFprint, true)
|
||||
CFG_PROPERTY(int, maxFprintTries, 3)
|
||||
CFG_PROPERTY(bool, recolorLogo, false)
|
||||
CFG_PROPERTY(bool, showNotifContent, true)
|
||||
CFG_PROPERTY(bool, showNotifIcon, true)
|
||||
CONFIG_PROPERTY(int, blurAmount, 10)
|
||||
CONFIG_PROPERTY(bool, enableFprint, true)
|
||||
CONFIG_PROPERTY(int, maxFprintTries, 3)
|
||||
CONFIG_PROPERTY(bool, recolorLogo, false)
|
||||
CONFIG_PROPERTY(bool, showNotifContent, true)
|
||||
CONFIG_PROPERTY(bool, showNotifIcon, true)
|
||||
CONFIG_SUBOBJECT(LockSizes, sizes)
|
||||
|
||||
public:
|
||||
explicit Lock(QObject* parent = nullptr)
|
||||
: ConfigObject(parent), m_sizes(new LockSizes(this)) {}
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
#include "migration.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
namespace {
|
||||
|
||||
// A stored value is "missing" if the path does not resolve to an entry.
|
||||
// Note: default-constructed QJsonValue is Null, not Undefined.
|
||||
bool isMissing(const QJsonValue& v) {
|
||||
return v.isUndefined() || v.isNull();
|
||||
}
|
||||
|
||||
QJsonValue valueAt(const QJsonObject& obj, const QStringList& path) {
|
||||
QJsonObject cur = obj;
|
||||
|
||||
for (int i = 0; i < path.size() - 1; ++i) {
|
||||
if (!cur.value(path.at(i)).isObject()) return QJsonValue::Undefined;
|
||||
cur = cur.value(path.at(i)).toObject();
|
||||
}
|
||||
|
||||
const QString key = path.last();
|
||||
if (!cur.contains(key)) return QJsonValue::Undefined;
|
||||
return cur.value(key);
|
||||
}
|
||||
|
||||
QJsonObject placeAt(QJsonObject obj, QStringList path, const QJsonValue& value) {
|
||||
const QString head = path.takeFirst();
|
||||
|
||||
if (path.isEmpty()) {
|
||||
obj[head] = value;
|
||||
return obj;
|
||||
}
|
||||
|
||||
QJsonObject child = obj.value(head).isObject() ? obj.value(head).toObject()
|
||||
: QJsonObject();
|
||||
obj[head] = placeAt(child, path, value);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Removes `path` from `obj`. Returns true if a value was removed.
|
||||
// Parent objects left empty are removed as well.
|
||||
bool removeAt(QJsonObject& obj, const QStringList& path) {
|
||||
const QString head = path.first();
|
||||
|
||||
if (path.size() == 1) {
|
||||
if (!obj.contains(head)) return false;
|
||||
obj.remove(head);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!obj.value(head).isObject()) return false;
|
||||
|
||||
QJsonObject child = obj.value(head).toObject();
|
||||
const bool removed = removeAt(child, path.mid(1));
|
||||
|
||||
if (child.isEmpty())
|
||||
obj.remove(head);
|
||||
else
|
||||
obj[head] = child;
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const QList<ConfigMigrationRule>& ConfigMigrations::rules() {
|
||||
static const QList<ConfigMigrationRule> s_rules = {
|
||||
migrate(
|
||||
"general.color.scheduleHyprsunset", "display.nightlight.schedule"),
|
||||
migrate(
|
||||
"general.color.scheduleHyprsunsetStart",
|
||||
"display.nightlight.scheduleStart"),
|
||||
migrate(
|
||||
"general.color.scheduleHyprsunsetEnd",
|
||||
"display.nightlight.scheduleEnd"),
|
||||
migrate("general.color.scheduleDark", "colors.scheduleDark"),
|
||||
migrate("general.color.scheduleDarkStart", "colors.scheduleDarkStart"),
|
||||
migrate("general.color.scheduleDarkEnd", "colors.scheduleDarkEnd"),
|
||||
migrate("general.color.schemeGeneration", "colors.schemeGen"),
|
||||
migrate(
|
||||
"general.color.useNativeNightlight",
|
||||
"display.nightlight.useNative"),
|
||||
migrate(
|
||||
"general.color.nativeFadeDuration",
|
||||
"display.nightlight.fadeDuration"),
|
||||
migrate("general.color.hyprsunsetTemp", "display.nightlight.temp"),
|
||||
migrate("general.color.neovimColors", "colors.neovimColors"),
|
||||
migrate("general.color.mode", "colors.mode"),
|
||||
migrate("general.color.smart", "colors.smart"),
|
||||
};
|
||||
return s_rules;
|
||||
}
|
||||
|
||||
QJsonObject ConfigMigrations::apply(const QJsonObject& json) {
|
||||
QJsonObject obj = json;
|
||||
|
||||
for (const auto& rule : rules()) {
|
||||
const QStringList from =
|
||||
rule.from.split(QLatin1Char('.'), Qt::SkipEmptyParts);
|
||||
const QStringList to =
|
||||
rule.to.split(QLatin1Char('.'), Qt::SkipEmptyParts);
|
||||
|
||||
if (from.isEmpty() || to.isEmpty()) continue;
|
||||
|
||||
const QJsonValue oldValue = valueAt(obj, from);
|
||||
if (isMissing(oldValue)) continue;
|
||||
|
||||
const bool targetExists = !isMissing(valueAt(obj, to));
|
||||
if (!targetExists) obj = placeAt(obj, to, oldValue);
|
||||
|
||||
if (removeAt(obj, from)) {
|
||||
qInfo() << "Config migration:" << rule.from << "->" << rule.to
|
||||
<< (targetExists ? "(target already set, old value dropped)"
|
||||
: "(value moved)");
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QStringList>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
// A single migration rule: move the value stored at the dotted `from` path
|
||||
// to the dotted `to` path, then delete the old key (and any parent objects
|
||||
// left empty).
|
||||
//
|
||||
// Rules are applied to the raw JSON file on every load, before the schema
|
||||
// loads it. They are idempotent: once the old key is gone the rule never
|
||||
// fires again, so no version stamping is needed. Old rules can stay in the
|
||||
// table forever.
|
||||
//
|
||||
// WARNING: the target path must exist in the schema (i.e. a ConfigObject
|
||||
// property somewhere in the tree). If it does not, the migrated value is
|
||||
// loaded as an unknown key and silently dropped on the next save.
|
||||
struct ConfigMigrationRule {
|
||||
QString from;
|
||||
QString to;
|
||||
};
|
||||
|
||||
// Helper for writing rules: migrate("general.color.foo", "display.bar.foo")
|
||||
inline ConfigMigrationRule migrate(const QString& from, const QString& to) {
|
||||
return {from, to};
|
||||
}
|
||||
|
||||
class ConfigMigrations {
|
||||
public:
|
||||
[[nodiscard]] static const QList<ConfigMigrationRule>& rules();
|
||||
|
||||
// Applies all rules to `json`, returning the migrated object.
|
||||
[[nodiscard]] static QJsonObject apply(const QJsonObject& json);
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -1,39 +1,30 @@
|
||||
#pragma once
|
||||
#include "configobject.hpp"
|
||||
#include "common.hpp"
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class NotifsSizes : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class NotifsSizes : public settings::ObjectNode {
|
||||
CONFIG_NODE(NotifsSizes, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(int, badge, 20)
|
||||
CFG_PROPERTY(int, image, 41)
|
||||
CFG_PROPERTY(int, width, 400)
|
||||
|
||||
public:
|
||||
explicit NotifsSizes(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(int, badge, 20)
|
||||
CONFIG_PROPERTY(int, image, 41)
|
||||
CONFIG_PROPERTY(int, width, 400)
|
||||
};
|
||||
|
||||
class Notifs : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Notifs : public settings::ObjectNode {
|
||||
CONFIG_NODE(Notifs, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(bool, actionOnClick, false)
|
||||
CFG_PROPERTY(int, appNotifCooldown, 0)
|
||||
CFG_PROPERTY(qreal, clearThreshold, 0.3)
|
||||
CFG_PROPERTY(int, defaultExpireTimeout, 5000)
|
||||
CFG_PROPERTY(int, expandThreshold, 20)
|
||||
CFG_PROPERTY(bool, expire, true)
|
||||
CFG_PROPERTY(int, groupPreviewNum, 5)
|
||||
CFG_PROPERTY(bool, openExpanded, false)
|
||||
CFG_PROPERTY(bool, showInFullscreen, false)
|
||||
CONFIG_PROPERTY(bool, actionOnClick, false)
|
||||
CONFIG_PROPERTY(int, appNotifCooldown, 0)
|
||||
CONFIG_PROPERTY(qreal, clearThreshold, 0.3)
|
||||
CONFIG_PROPERTY(int, defaultExpireTimeout, 5000)
|
||||
CONFIG_PROPERTY(int, expandThreshold, 20)
|
||||
CONFIG_PROPERTY(bool, expire, true)
|
||||
CONFIG_PROPERTY(int, groupPreviewNum, 5)
|
||||
CONFIG_PROPERTY(bool, openExpanded, false)
|
||||
CONFIG_PROPERTY(bool, showInFullscreen, false)
|
||||
CONFIG_SUBOBJECT(NotifsSizes, sizes)
|
||||
|
||||
public:
|
||||
explicit Notifs(QObject* parent = nullptr)
|
||||
: ConfigObject(parent), m_sizes(new NotifsSizes(this)) {}
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -1,34 +1,25 @@
|
||||
#pragma once
|
||||
#include "configobject.hpp"
|
||||
#include "common.hpp"
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class OsdSizes : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class OsdSizes : public settings::ObjectNode {
|
||||
CONFIG_NODE(OsdSizes, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(int, sliderHeight, 150)
|
||||
CFG_PROPERTY(int, sliderWidth, 30)
|
||||
|
||||
public:
|
||||
explicit OsdSizes(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(int, sliderHeight, 150)
|
||||
CONFIG_PROPERTY(int, sliderWidth, 30)
|
||||
};
|
||||
|
||||
class Osd : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Osd : public settings::ObjectNode {
|
||||
CONFIG_NODE(Osd, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(bool, allMonBrightness, true)
|
||||
CFG_PROPERTY(bool, enableBrightness, true)
|
||||
CFG_PROPERTY(bool, enableMicrophone, true)
|
||||
CFG_PROPERTY(bool, enabled, true)
|
||||
CFG_PROPERTY(int, hideDelay, 3000)
|
||||
CONFIG_PROPERTY(bool, allMonBrightness, true)
|
||||
CONFIG_PROPERTY(bool, enableBrightness, true)
|
||||
CONFIG_PROPERTY(bool, enableMicrophone, true)
|
||||
CONFIG_PROPERTY(bool, enabled, true)
|
||||
CONFIG_PROPERTY(int, hideDelay, 3000)
|
||||
CONFIG_SUBOBJECT(OsdSizes, sizes)
|
||||
|
||||
public:
|
||||
explicit Osd(QObject* parent = nullptr)
|
||||
: ConfigObject(parent), m_sizes(new OsdSizes(this)) {}
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
#include "rootnodes.hpp"
|
||||
|
||||
#include <qjsondocument.h>
|
||||
#include <qsavefile.h>
|
||||
|
||||
#include "../toaster.hpp"
|
||||
#include "common.hpp"
|
||||
#include "migration.hpp"
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
using Qt::StringLiterals::operator""_s;
|
||||
|
||||
namespace {
|
||||
|
||||
QString nameFor(const QString& key) {
|
||||
return key.isEmpty() ? u"global"_s : key;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace detail {
|
||||
|
||||
void loaded(ConfigKind kind, settings::RootNode* layer, const QString& screen) {
|
||||
if (kind != ConfigKind::Shell || !screen.isEmpty()) return;
|
||||
|
||||
auto* const config = static_cast<ConfigRoot*>(layer);
|
||||
if (!config->utilities()->toasts()->configLoaded()) return;
|
||||
|
||||
const auto issues = static_cast<int>(config->diagnostics().count());
|
||||
const auto message = issues > 0
|
||||
? QStringLiteral("Config loaded with %1 issue%2")
|
||||
.arg(issues)
|
||||
.arg(issues == 1 ? "" : "s")
|
||||
: QStringLiteral("Config loaded successfully!");
|
||||
Toaster::instance()->toast(
|
||||
QStringLiteral("Config loaded"),
|
||||
message,
|
||||
issues > 0 ? QStringLiteral("settings_alert")
|
||||
: QStringLiteral("rule_settings"),
|
||||
issues > 0 ? Toast::Type::Warning : Toast::Type::Info);
|
||||
}
|
||||
|
||||
void loadFailed(ConfigKind kind, const QString& error, const QString& screen) {
|
||||
QString title;
|
||||
if (kind == ConfigKind::Tokens)
|
||||
title = screen.isEmpty()
|
||||
? QStringLiteral("Failed to parse token config")
|
||||
: QStringLiteral("Failed to parse token config for %1")
|
||||
.arg(screen);
|
||||
else
|
||||
title =
|
||||
screen.isEmpty()
|
||||
? QStringLiteral("Failed to parse config")
|
||||
: QStringLiteral("Failed to parse config for %1").arg(screen);
|
||||
Toaster::instance()->toast(
|
||||
title, error, QStringLiteral("settings_alert"), Toast::Type::Warning);
|
||||
}
|
||||
|
||||
void saveFailed(ConfigKind kind, const QString& error, const QString& screen) {
|
||||
if (kind != ConfigKind::Shell) return;
|
||||
|
||||
const auto title =
|
||||
screen.isEmpty()
|
||||
? QStringLiteral("Failed to save config")
|
||||
: QStringLiteral("Failed to save config for %1").arg(screen);
|
||||
Toaster::instance()->toast(
|
||||
title, error, QStringLiteral("settings_alert"), Toast::Type::Error);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
ConfigRoot::ConfigRoot(
|
||||
const QString& path, ConfigRoot* fallback, QObject* parent)
|
||||
: RootNode(path, fallback, parent) {
|
||||
migrateFile(path);
|
||||
bindTokens();
|
||||
qCDebug(lcConfig) << "Created config root for" << nameFor(key());
|
||||
}
|
||||
|
||||
void ConfigRoot::migrateFile(const QString& path) {
|
||||
QFile file(path);
|
||||
if (!file.exists() || !file.open(QIODevice::ReadOnly)) return;
|
||||
|
||||
const auto doc = QJsonDocument::fromJson(file.readAll());
|
||||
if (!doc.isObject()) return;
|
||||
|
||||
const QJsonObject raw = doc.object();
|
||||
const QJsonObject migrated = ConfigMigrations::apply(raw);
|
||||
if (migrated == raw) return;
|
||||
|
||||
file.close();
|
||||
|
||||
QSaveFile save(path);
|
||||
if (!save.open(QIODevice::WriteOnly)) {
|
||||
qCWarning(lcConfig) << "Failed to open" << path
|
||||
<< "for migration write:" << save.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
if (save.write(QJsonDocument(migrated).toJson(QJsonDocument::Indented)) !=
|
||||
save.size()) {
|
||||
qCWarning(lcConfig)
|
||||
<< "Failed to write migrated config:" << save.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!save.commit()) {
|
||||
qCWarning(lcConfig)
|
||||
<< "Failed to commit migrated config:" << save.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(lcConfig) << "Migrated config file" << path;
|
||||
}
|
||||
|
||||
void ConfigRoot::bindTokens() {
|
||||
qCDebug(lcConfig) << "Binding appearance to token values for"
|
||||
<< nameFor(key());
|
||||
|
||||
auto* const tokens = TokensSingleton::instance()->appearance();
|
||||
m_appearance->rounding()->bindTokens(tokens->rounding());
|
||||
m_appearance->spacing()->bindTokens(tokens->spacing());
|
||||
m_appearance->padding()->bindTokens(tokens->padding());
|
||||
m_appearance->anim()->durations()->bindTokens(tokens->animDurations());
|
||||
}
|
||||
|
||||
TokensRoot::TokensRoot(
|
||||
const QString& path, TokensRoot* fallback, QObject* parent)
|
||||
: RootNode(path, fallback, parent) {
|
||||
qCDebug(lcConfig) << "Created tokens root for" << nameFor(key());
|
||||
}
|
||||
|
||||
#define SINGLETON_IMPL(Type, Root, file, kind) \
|
||||
Type* Type::instance() { \
|
||||
static Type instance; \
|
||||
return &instance; \
|
||||
} \
|
||||
\
|
||||
Type* Type::create(QQmlEngine*, QJSEngine*) { \
|
||||
QQmlEngine::setObjectOwnership(instance(), QQmlEngine::CppOwnership); \
|
||||
return instance(); \
|
||||
} \
|
||||
\
|
||||
Root* Type::forScreen(const QString& screen) { \
|
||||
bool created; \
|
||||
auto* const layer = m_layers.get(screen, this, &created); \
|
||||
if (created) initLayer(layer); \
|
||||
return layer; \
|
||||
} \
|
||||
\
|
||||
Type::Type(QObject* parent) \
|
||||
: Root(configDir() + u'/' + file, nullptr, parent) \
|
||||
, m_layers(monitorConfigDir(), file, this) { \
|
||||
initLayer(this); \
|
||||
} \
|
||||
\
|
||||
void Type::onTreeLoaded(settings::RootNode* layer) { \
|
||||
detail::loaded( \
|
||||
kind, layer, m_layers.nameFor(static_cast<Root*>(layer))); \
|
||||
} \
|
||||
\
|
||||
void Type::onTreeLoadFailed( \
|
||||
settings::RootNode* layer, const QString& error) { \
|
||||
detail::loadFailed( \
|
||||
kind, error, m_layers.nameFor(static_cast<Root*>(layer))); \
|
||||
} \
|
||||
\
|
||||
void Type::onTreeSaveFailed( \
|
||||
settings::RootNode* layer, const QString& error) { \
|
||||
detail::saveFailed( \
|
||||
kind, error, m_layers.nameFor(static_cast<Root*>(layer))); \
|
||||
} \
|
||||
\
|
||||
void Type::initLayer(Root* layer) { \
|
||||
QObject::connect(layer, &Root::treeLoaded, this, &Type::onTreeLoaded); \
|
||||
QObject::connect( \
|
||||
layer, &Root::treeLoadFailed, this, &Type::onTreeLoadFailed); \
|
||||
QObject::connect( \
|
||||
layer, &Root::treeSaveFailed, this, &Type::onTreeSaveFailed); \
|
||||
layer->load(); \
|
||||
}
|
||||
|
||||
SINGLETON_IMPL(
|
||||
ConfigSingleton, ConfigRoot, u"config.json"_s, detail::ConfigKind::Shell)
|
||||
SINGLETON_IMPL(
|
||||
TokensSingleton, TokensRoot, u"tokens.json"_s, detail::ConfigKind::Tokens)
|
||||
|
||||
#undef SINGLETON_IMPL
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -0,0 +1,168 @@
|
||||
#pragma once
|
||||
|
||||
#include <qjsengine.h>
|
||||
#include <qqmlengine.h>
|
||||
|
||||
#include "settings/layerregistry.hpp"
|
||||
#include "settings/rootnode.hpp"
|
||||
#include "appearance.hpp"
|
||||
#include "background.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "clipboard.hpp"
|
||||
#include "colors.hpp"
|
||||
#include "common.hpp"
|
||||
#include "dashboard.hpp"
|
||||
#include "display.hpp"
|
||||
#include "dock.hpp"
|
||||
#include "general.hpp"
|
||||
#include "launcher.hpp"
|
||||
#include "llm.hpp"
|
||||
#include "lock.hpp"
|
||||
#include "notifs.hpp"
|
||||
#include "osd.hpp"
|
||||
#include "screenshot.hpp"
|
||||
#include "services.hpp"
|
||||
#include "sidebar.hpp"
|
||||
#include "tokens.hpp"
|
||||
#include "utilities.hpp"
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class ConfigRoot : public settings::RootNode {
|
||||
CONFIG_NODE_NO_CTOR(ConfigRoot, settings::RootNode)
|
||||
QML_ANONYMOUS
|
||||
|
||||
CONFIG_PROPERTY(bool, enabled, true)
|
||||
CONFIG_SUBOBJECT(Appearance, appearance)
|
||||
CONFIG_SUBOBJECT(Background, background)
|
||||
CONFIG_SUBOBJECT(Bar, bar)
|
||||
CONFIG_SUBOBJECT(Clipboard, clipboard)
|
||||
CONFIG_SUBOBJECT(Colors, colors)
|
||||
CONFIG_SUBOBJECT(Dashboard, dashboard)
|
||||
CONFIG_SUBOBJECT(Display, display)
|
||||
CONFIG_SUBOBJECT(Dock, dock)
|
||||
CONFIG_SUBOBJECT(General, general)
|
||||
CONFIG_SUBOBJECT(Launcher, launcher)
|
||||
CONFIG_SUBOBJECT(Llm, llm)
|
||||
CONFIG_SUBOBJECT(Lock, lock)
|
||||
CONFIG_SUBOBJECT(Notifs, notifs)
|
||||
CONFIG_SUBOBJECT(Osd, osd)
|
||||
CONFIG_SUBOBJECT(Screenshot, screenshot)
|
||||
CONFIG_SUBOBJECT(Services, services)
|
||||
CONFIG_SUBOBJECT(Sidebar, sidebar)
|
||||
CONFIG_SUBOBJECT(Utilities, utilities)
|
||||
|
||||
public:
|
||||
explicit ConfigRoot(
|
||||
const QString& path,
|
||||
ConfigRoot* fallback = nullptr,
|
||||
QObject* parent = nullptr);
|
||||
|
||||
private:
|
||||
// Binds the computed appearance values to the global token base values
|
||||
void bindTokens();
|
||||
|
||||
// Applies ConfigMigrations to the file before the first load
|
||||
void migrateFile(const QString& path);
|
||||
};
|
||||
|
||||
class TokensRoot : public settings::RootNode {
|
||||
CONFIG_NODE_NO_CTOR(TokensRoot, settings::RootNode)
|
||||
QML_ANONYMOUS
|
||||
|
||||
CONFIG_SUBOBJECT(AppearanceTokens, appearance)
|
||||
CONFIG_SUBOBJECT(SizeTokens, sizes)
|
||||
|
||||
public:
|
||||
explicit TokensRoot(
|
||||
const QString& path,
|
||||
TokensRoot* fallback = nullptr,
|
||||
QObject* parent = nullptr);
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
enum class ConfigKind : quint8 { Shell, Tokens };
|
||||
|
||||
void loaded(ConfigKind kind, settings::RootNode* layer, const QString& screen);
|
||||
void loadFailed(ConfigKind kind, const QString& error, const QString& screen);
|
||||
void saveFailed(ConfigKind kind, const QString& error, const QString& screen);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#define SINGLETON_DECL(Type, Root, QmlName) \
|
||||
class Type : public Root { \
|
||||
Q_OBJECT \
|
||||
QML_NAMED_ELEMENT(QmlName) \
|
||||
QML_SINGLETON \
|
||||
\
|
||||
public: \
|
||||
static Type* instance(); \
|
||||
static Type* create(QQmlEngine*, QJSEngine*); \
|
||||
\
|
||||
[[nodiscard]] Q_INVOKABLE Root* forScreen(const QString& screen); \
|
||||
\
|
||||
private: \
|
||||
explicit Type(QObject* parent = nullptr); \
|
||||
\
|
||||
void onTreeLoaded(settings::RootNode* layer); \
|
||||
void onTreeLoadFailed(settings::RootNode* layer, const QString& error); \
|
||||
void onTreeSaveFailed(settings::RootNode* layer, const QString& error); \
|
||||
\
|
||||
void initLayer(Root* layer); \
|
||||
\
|
||||
settings::LayerRegistry<Root> m_layers; \
|
||||
};
|
||||
|
||||
SINGLETON_DECL(ConfigSingleton, ConfigRoot, Config)
|
||||
SINGLETON_DECL(TokensSingleton, TokensRoot, TokenConfig)
|
||||
|
||||
#undef SINGLETON_DECL
|
||||
|
||||
class Tokens : public QObject {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
Q_PROPERTY(
|
||||
ZShell::config::AppearanceRounding* rounding READ rounding CONSTANT)
|
||||
Q_PROPERTY(ZShell::config::AppearanceSpacing* spacing READ spacing CONSTANT)
|
||||
Q_PROPERTY(ZShell::config::AppearancePadding* padding READ padding CONSTANT)
|
||||
Q_PROPERTY(ZShell::config::SizeTokens* sizes READ sizes CONSTANT)
|
||||
Q_PROPERTY(ZShell::config::FontTokens* font READ font CONSTANT)
|
||||
Q_PROPERTY(ZShell::config::AnimTokens* anim READ anim CONSTANT)
|
||||
|
||||
public:
|
||||
explicit Tokens(QObject* parent = nullptr)
|
||||
: QObject(parent)
|
||||
, m_font(new FontTokens(this))
|
||||
, m_anim(new AnimTokens(this)) {
|
||||
m_anim->bindCurves(TokensSingleton::instance()->appearance()->curves());
|
||||
m_anim->bindDurations(
|
||||
ConfigSingleton::instance()->appearance()->anim()->durations());
|
||||
m_font->bindFont(ConfigSingleton::instance()->appearance()->font());
|
||||
}
|
||||
|
||||
[[nodiscard]] AppearanceRounding* rounding() const {
|
||||
return ConfigSingleton::instance()->appearance()->rounding();
|
||||
}
|
||||
[[nodiscard]] AppearancePadding* padding() const {
|
||||
return ConfigSingleton::instance()->appearance()->padding();
|
||||
}
|
||||
[[nodiscard]] AppearanceSpacing* spacing() const {
|
||||
return ConfigSingleton::instance()->appearance()->spacing();
|
||||
}
|
||||
[[nodiscard]] SizeTokens* sizes() const {
|
||||
return TokensSingleton::instance()->sizes();
|
||||
}
|
||||
[[nodiscard]] FontTokens* font() const { return m_font; }
|
||||
[[nodiscard]] AnimTokens* anim() const { return m_anim; }
|
||||
|
||||
static Tokens* create(QQmlEngine*, QJSEngine*) { return new Tokens(); }
|
||||
|
||||
private:
|
||||
FontTokens* m_font;
|
||||
AnimTokens* m_anim;
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
@@ -1,27 +1,23 @@
|
||||
#pragma once
|
||||
#include "configobject.hpp"
|
||||
#include "common.hpp"
|
||||
#include <QVariantList>
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class Screenshot : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Screenshot : public settings::ObjectNode {
|
||||
CONFIG_NODE(Screenshot, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(bool, enable_pp, false)
|
||||
CFG_PROPERTY(QString, mode, QStringLiteral("manual"))
|
||||
CFG_PROPERTY(int, radius, 49)
|
||||
CFG_PROPERTY(bool, rounding, false)
|
||||
CFG_PROPERTY(bool, saveOnCopy, false)
|
||||
CFG_PROPERTY(bool, shadow, true)
|
||||
CFG_PROPERTY(int, shadow_blur, 0)
|
||||
CFG_PROPERTY(QVariantList, shadow_color, (QVariantList{0, 0, 0, 160}))
|
||||
CFG_PROPERTY(int, shadow_offset_x, 30)
|
||||
CFG_PROPERTY(int, shadow_offset_y, 50)
|
||||
|
||||
public:
|
||||
explicit Screenshot(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(bool, enable_pp, false)
|
||||
CONFIG_PROPERTY(QString, mode, QStringLiteral("manual"))
|
||||
CONFIG_PROPERTY(int, radius, 49)
|
||||
CONFIG_PROPERTY(bool, rounding, false)
|
||||
CONFIG_PROPERTY(bool, saveOnCopy, false)
|
||||
CONFIG_PROPERTY(bool, shadow, true)
|
||||
CONFIG_PROPERTY(int, shadow_blur, 0)
|
||||
CONFIG_PROPERTY(QVariantList, shadow_color, (QVariantList{0, 0, 0, 160}))
|
||||
CONFIG_PROPERTY(int, shadow_offset_x, 30)
|
||||
CONFIG_PROPERTY(int, shadow_offset_y, 50)
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -1,54 +1,43 @@
|
||||
#pragma once
|
||||
#include "configlist.hpp"
|
||||
#include "configobject.hpp"
|
||||
|
||||
#include "common.hpp"
|
||||
#include "enums.hpp"
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class PlayerAliasEntry : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class PlayerAliasEntry : public settings::ObjectNode {
|
||||
CONFIG_NODE(PlayerAliasEntry, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(QString, from)
|
||||
CFG_PROPERTY(QString, to)
|
||||
|
||||
public:
|
||||
explicit PlayerAliasEntry(QObject* parent = nullptr)
|
||||
: ConfigObject(parent) {}
|
||||
|
||||
[[nodiscard]] QStringList identityKeys() const override {
|
||||
return {QStringLiteral("from")};
|
||||
}
|
||||
CONFIG_PROPERTY(QString, from, {})
|
||||
CONFIG_PROPERTY(QString, to, {})
|
||||
};
|
||||
|
||||
CONFIG_LIST_TYPE(PlayerAliasEntry, PlayerAliasList)
|
||||
|
||||
class Services : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Services : public settings::ObjectNode {
|
||||
CONFIG_NODE(Services, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(qreal, audioIncrement, 0.1)
|
||||
CFG_PROPERTY(qreal, brightnessIncrement, 0.1)
|
||||
CFG_PROPERTY(bool, ddcutilService, true)
|
||||
CFG_PROPERTY(QString, defaultPlayer, QStringLiteral("Spotify"))
|
||||
CFG_PROPERTY(QString, gpuType, QStringLiteral("NVIDIA"))
|
||||
CFG_PROPERTY(qreal, maxVolume, 1)
|
||||
CFG_PROPERTY(qreal, minBrightness, 0.01)
|
||||
CONFIG_PROPERTY(qreal, audioIncrement, 0.1)
|
||||
CONFIG_PROPERTY(qreal, brightnessIncrement, 0.1)
|
||||
CONFIG_PROPERTY(bool, ddcutilService, true)
|
||||
CONFIG_PROPERTY(QString, defaultPlayer, QStringLiteral("Spotify"))
|
||||
CONFIG_ENUM_PROPERTY(GpuType, gpuType, GpuType::Auto)
|
||||
CONFIG_PROPERTY(LyricsBackend::Enum, lyricsBackend, LyricsBackend::Auto)
|
||||
CONFIG_PROPERTY(qreal, maxVolume, 1)
|
||||
CONFIG_PROPERTY(qreal, minBrightness, 0.01)
|
||||
CONFIG_LIST(
|
||||
PlayerAliasList,
|
||||
playerAliases,
|
||||
{
|
||||
vmap(
|
||||
DEFAULT_ARG({
|
||||
settings::vmap(
|
||||
{{"from", QStringLiteral("com.github.th_ch.youtube_music")},
|
||||
{"to", QStringLiteral("YT Music")}}),
|
||||
})
|
||||
CFG_PROPERTY(bool, updates, true)
|
||||
CFG_PROPERTY(bool, useFahrenheit, false)
|
||||
CFG_PROPERTY(bool, useTwelveHourClock, false)
|
||||
CFG_PROPERTY(int, visualizerBars, 50)
|
||||
CFG_PROPERTY(QString, weatherLocation, QString())
|
||||
|
||||
public:
|
||||
explicit Services(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
}))
|
||||
CONFIG_PROPERTY(bool, updates, true)
|
||||
CONFIG_PROPERTY(bool, useFahrenheit, false)
|
||||
CONFIG_PROPERTY(bool, useTwelveHourClock, false)
|
||||
CONFIG_PROPERTY(int, visualizerBars, 50)
|
||||
CONFIG_PROPERTY(QString, weatherLocation, QString())
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -1,30 +1,21 @@
|
||||
#pragma once
|
||||
#include "configobject.hpp"
|
||||
#include "common.hpp"
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class SidebarSizes : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class SidebarSizes : public settings::ObjectNode {
|
||||
CONFIG_NODE(SidebarSizes, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(int, width, 520)
|
||||
|
||||
public:
|
||||
explicit SidebarSizes(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(int, width, 520)
|
||||
};
|
||||
|
||||
class Sidebar : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Sidebar : public settings::ObjectNode {
|
||||
CONFIG_NODE(Sidebar, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(int, dragThreshold, 30)
|
||||
CFG_PROPERTY(bool, enabled, true)
|
||||
CONFIG_PROPERTY(int, dragThreshold, 30)
|
||||
CONFIG_PROPERTY(bool, enabled, true)
|
||||
CONFIG_SUBOBJECT(SidebarSizes, sizes)
|
||||
|
||||
public:
|
||||
explicit Sidebar(QObject* parent = nullptr)
|
||||
: ConfigObject(parent), m_sizes(new SidebarSizes(this)) {}
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
+136
-254
@@ -1,292 +1,174 @@
|
||||
#pragma once
|
||||
|
||||
#include "configobject.hpp"
|
||||
#include "anim.hpp"
|
||||
|
||||
#include <QVariantList>
|
||||
#include <limits>
|
||||
#include <qlist.h>
|
||||
#include <qobject.h>
|
||||
#include <qqmlengine.h>
|
||||
#include <qqmlintegration.h>
|
||||
#include <qqmlregistration.h>
|
||||
#include <qtypes.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "settings/objectnode.hpp"
|
||||
#include "anim.hpp"
|
||||
#include "common.hpp"
|
||||
#include "font.hpp"
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class AppearanceRounding : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class AnimCurves : public settings::ObjectNode {
|
||||
CONFIG_NODE(AnimCurves, settings::ObjectNode)
|
||||
|
||||
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) {}
|
||||
CONFIG_GLOBAL_PROPERTY(
|
||||
QList<qreal>,
|
||||
emphasized,
|
||||
DEFAULT_ARG(
|
||||
{0.05,
|
||||
0,
|
||||
2.0 / 15.0,
|
||||
0.06,
|
||||
1.0 / 6.0,
|
||||
0.4,
|
||||
5.0 / 24.0,
|
||||
0.82,
|
||||
0.25,
|
||||
1,
|
||||
1,
|
||||
1}))
|
||||
CONFIG_GLOBAL_PROPERTY(
|
||||
QList<qreal>, emphasizedAccel, DEFAULT_ARG({0.3, 0, 0.8, 0.15, 1, 1}))
|
||||
CONFIG_GLOBAL_PROPERTY(
|
||||
QList<qreal>, emphasizedDecel, DEFAULT_ARG({0.05, 0.7, 0.1, 1, 1, 1}))
|
||||
CONFIG_GLOBAL_PROPERTY(
|
||||
QList<qreal>, standard, DEFAULT_ARG({0.2, 0, 0, 1, 1, 1}))
|
||||
CONFIG_GLOBAL_PROPERTY(
|
||||
QList<qreal>, standardAccel, DEFAULT_ARG({0.3, 0, 1, 1, 1, 1}))
|
||||
CONFIG_GLOBAL_PROPERTY(
|
||||
QList<qreal>, standardDecel, DEFAULT_ARG({0, 0, 0, 1, 1, 1}))
|
||||
CONFIG_GLOBAL_PROPERTY(
|
||||
QList<qreal>,
|
||||
expressiveFastSpatial,
|
||||
DEFAULT_ARG({0.42, 1.67, 0.21, 0.9, 1, 1}))
|
||||
CONFIG_GLOBAL_PROPERTY(
|
||||
QList<qreal>,
|
||||
expressiveDefaultSpatial,
|
||||
DEFAULT_ARG({0.38, 1.21, 0.22, 1, 1, 1}))
|
||||
CONFIG_GLOBAL_PROPERTY(
|
||||
QList<qreal>,
|
||||
expressiveSlowSpatial,
|
||||
DEFAULT_ARG({0.39, 1.29, 0.35, 0.98, 1, 1}))
|
||||
CONFIG_GLOBAL_PROPERTY(
|
||||
QList<qreal>,
|
||||
expressiveFastEffects,
|
||||
DEFAULT_ARG({0.31, 0.94, 0.34, 1, 1, 1}))
|
||||
CONFIG_GLOBAL_PROPERTY(
|
||||
QList<qreal>,
|
||||
expressiveDefaultEffects,
|
||||
DEFAULT_ARG({0.34, 0.8, 0.34, 1, 1, 1}))
|
||||
CONFIG_GLOBAL_PROPERTY(
|
||||
QList<qreal>,
|
||||
expressiveSlowEffects,
|
||||
DEFAULT_ARG({0.34, 0.88, 0.34, 1, 1, 1}))
|
||||
};
|
||||
|
||||
class AppearancePadding : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class RoundingTokens : public settings::ObjectNode {
|
||||
CONFIG_NODE(RoundingTokens, settings::ObjectNode)
|
||||
|
||||
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) {}
|
||||
CONFIG_PROPERTY(int, extraSmall, 4)
|
||||
CONFIG_PROPERTY(int, small, 8)
|
||||
CONFIG_PROPERTY(int, medium, 12)
|
||||
CONFIG_PROPERTY(int, large, 16)
|
||||
CONFIG_PROPERTY(int, largeIncreased, 20)
|
||||
CONFIG_PROPERTY(int, extraLarge, 28)
|
||||
CONFIG_PROPERTY(int, extraLargeIncreased, 32)
|
||||
CONFIG_PROPERTY(int, extraExtraLarge, 48)
|
||||
CONFIG_PROPERTY(int, full, std::numeric_limits<int>::max())
|
||||
};
|
||||
|
||||
class AppearanceSpacing : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class SpacingTokens : public settings::ObjectNode {
|
||||
CONFIG_NODE(SpacingTokens, settings::ObjectNode)
|
||||
|
||||
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) {}
|
||||
CONFIG_PROPERTY(int, extraSmall, 4)
|
||||
CONFIG_PROPERTY(int, small, 8)
|
||||
CONFIG_PROPERTY(int, medium, 12)
|
||||
CONFIG_PROPERTY(int, large, 16)
|
||||
CONFIG_PROPERTY(int, largeIncreased, 20)
|
||||
CONFIG_PROPERTY(int, extraLarge, 28)
|
||||
CONFIG_PROPERTY(int, extraLargeIncreased, 32)
|
||||
CONFIG_PROPERTY(int, extraExtraLarge, 48)
|
||||
};
|
||||
|
||||
class FontSizeTokens : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class PaddingTokens : public settings::ObjectNode {
|
||||
CONFIG_NODE(PaddingTokens, settings::ObjectNode)
|
||||
|
||||
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) {}
|
||||
CONFIG_PROPERTY(int, extraSmall, 4)
|
||||
CONFIG_PROPERTY(int, smaller, 5)
|
||||
CONFIG_PROPERTY(int, small, 8)
|
||||
CONFIG_PROPERTY(int, medium, 12)
|
||||
CONFIG_PROPERTY(int, large, 16)
|
||||
CONFIG_PROPERTY(int, largeIncreased, 20)
|
||||
CONFIG_PROPERTY(int, extraLarge, 28)
|
||||
CONFIG_PROPERTY(int, extraLargeIncreased, 32)
|
||||
CONFIG_PROPERTY(int, extraExtraLarge, 48)
|
||||
};
|
||||
|
||||
class FontTokens : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class AnimDurationTokens : public settings::ObjectNode {
|
||||
CONFIG_NODE(AnimDurationTokens, settings::ObjectNode)
|
||||
|
||||
CONFIG_SUBOBJECT(FontSizeTokens, size)
|
||||
|
||||
public:
|
||||
explicit FontTokens(QObject* parent = nullptr)
|
||||
: ConfigObject(parent), m_size(new FontSizeTokens(this)) {}
|
||||
CONFIG_GLOBAL_PROPERTY(int, small, 200)
|
||||
CONFIG_GLOBAL_PROPERTY(int, normal, 400)
|
||||
CONFIG_GLOBAL_PROPERTY(int, large, 600)
|
||||
CONFIG_GLOBAL_PROPERTY(int, extraLarge, 1000)
|
||||
CONFIG_GLOBAL_PROPERTY(int, expressiveFastSpatial, 350)
|
||||
CONFIG_GLOBAL_PROPERTY(int, expressiveDefaultSpatial, 500)
|
||||
CONFIG_GLOBAL_PROPERTY(int, expressiveSlowSpatial, 650)
|
||||
CONFIG_GLOBAL_PROPERTY(int, expressiveFastEffects, 150)
|
||||
CONFIG_GLOBAL_PROPERTY(int, expressiveDefaultEffects, 200)
|
||||
CONFIG_GLOBAL_PROPERTY(int, expressiveSlowEffects, 300)
|
||||
};
|
||||
|
||||
class AnimCurves : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class AppearanceTokens : public settings::ObjectNode {
|
||||
CONFIG_NODE(AppearanceTokens, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(QList<qreal>, emphasized)
|
||||
CFG_PROPERTY(QList<qreal>, emphasizedAccel)
|
||||
CFG_PROPERTY(QList<qreal>, emphasizedDecel)
|
||||
CFG_PROPERTY(QList<qreal>, standard)
|
||||
CFG_PROPERTY(QList<qreal>, standardAccel)
|
||||
CFG_PROPERTY(QList<qreal>, standardDecel)
|
||||
CFG_PROPERTY(QList<qreal>, expressiveFastSpatial)
|
||||
CFG_PROPERTY(QList<qreal>, expressiveDefaultSpatial)
|
||||
CFG_PROPERTY(QList<qreal>, expressiveSlowSpatial)
|
||||
CFG_PROPERTY(QList<qreal>, expressiveFastEffects)
|
||||
CFG_PROPERTY(QList<qreal>, expressiveDefaultEffects)
|
||||
CFG_PROPERTY(QList<qreal>, expressiveSlowEffects)
|
||||
|
||||
public:
|
||||
explicit AnimCurves(QObject* parent = nullptr)
|
||||
: ConfigObject(parent)
|
||||
, m_emphasized(
|
||||
{0.05,
|
||||
0,
|
||||
2.0 / 15.0,
|
||||
0.06,
|
||||
1.0 / 6.0,
|
||||
0.4,
|
||||
5.0 / 24.0,
|
||||
0.82,
|
||||
0.25,
|
||||
1,
|
||||
1,
|
||||
1})
|
||||
, m_emphasizedAccel({0.3, 0, 0.8, 0.15, 1, 1})
|
||||
, m_emphasizedDecel({0.05, 0.7, 0.1, 1, 1, 1})
|
||||
, m_standard({0.2, 0, 0, 1, 1, 1})
|
||||
, m_standardAccel({0.3, 0, 1, 1, 1, 1})
|
||||
, m_standardDecel({0, 0, 0, 1, 1, 1})
|
||||
, m_expressiveFastSpatial({0.42, 1.67, 0.21, 0.9, 1, 1})
|
||||
, m_expressiveDefaultSpatial({0.38, 1.21, 0.22, 1, 1, 1})
|
||||
, m_expressiveSlowSpatial({0.39, 1.29, 0.35, 0.98, 1, 1})
|
||||
, m_expressiveFastEffects({0.31, 0.94, 0.34, 1, 1, 1})
|
||||
, m_expressiveDefaultEffects({0.34, 0.8, 0.34, 1, 1, 1})
|
||||
, m_expressiveSlowEffects({0.34, 0.88, 0.34, 1, 1, 1}) {}
|
||||
CONFIG_SUBOBJECT(AnimCurves, curves)
|
||||
CONFIG_SUBOBJECT(RoundingTokens, rounding)
|
||||
CONFIG_SUBOBJECT(SpacingTokens, spacing)
|
||||
CONFIG_SUBOBJECT(PaddingTokens, padding)
|
||||
CONFIG_SUBOBJECT(AnimDurationTokens, animDurations)
|
||||
};
|
||||
|
||||
class AnimDurations : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class BarTokens : public settings::ObjectNode {
|
||||
CONFIG_NODE(BarTokens, settings::ObjectNode)
|
||||
|
||||
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) {}
|
||||
CONFIG_PROPERTY(int, innerSize, 28)
|
||||
};
|
||||
|
||||
class AnimTokens : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class DashboardTokens : public settings::ObjectNode {
|
||||
CONFIG_NODE(DashboardTokens, settings::ObjectNode)
|
||||
|
||||
CONFIG_SUBOBJECT(AnimDurations, durations)
|
||||
|
||||
Q_PROPERTY(QEasingCurve emphasized READ emphasized NOTIFY curvesChanged)
|
||||
Q_PROPERTY(
|
||||
QEasingCurve emphasizedAccel READ emphasizedAccel NOTIFY curvesChanged)
|
||||
Q_PROPERTY(
|
||||
QEasingCurve emphasizedDecel READ emphasizedDecel NOTIFY curvesChanged)
|
||||
Q_PROPERTY(QEasingCurve standard READ standard NOTIFY curvesChanged)
|
||||
Q_PROPERTY(
|
||||
QEasingCurve standardAccel READ standardAccel NOTIFY curvesChanged)
|
||||
Q_PROPERTY(
|
||||
QEasingCurve standardDecel READ standardDecel NOTIFY curvesChanged)
|
||||
Q_PROPERTY(
|
||||
QEasingCurve expressiveFastSpatial READ expressiveFastSpatial NOTIFY
|
||||
curvesChanged)
|
||||
Q_PROPERTY(
|
||||
QEasingCurve expressiveDefaultSpatial READ expressiveDefaultSpatial
|
||||
NOTIFY curvesChanged)
|
||||
Q_PROPERTY(
|
||||
QEasingCurve expressiveSlowSpatial READ expressiveSlowSpatial NOTIFY
|
||||
curvesChanged)
|
||||
Q_PROPERTY(
|
||||
QEasingCurve expressiveFastEffects READ expressiveFastEffects NOTIFY
|
||||
curvesChanged)
|
||||
Q_PROPERTY(
|
||||
QEasingCurve expressiveDefaultEffects READ expressiveDefaultEffects
|
||||
NOTIFY curvesChanged)
|
||||
Q_PROPERTY(
|
||||
QEasingCurve expressiveSlowEffects READ expressiveSlowEffects NOTIFY
|
||||
curvesChanged)
|
||||
|
||||
public:
|
||||
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,
|
||||
&AnimEasing::curvesChanged,
|
||||
this,
|
||||
&AnimTokens::curvesChanged);
|
||||
m_easing->bindCurves(m_curves);
|
||||
}
|
||||
|
||||
[[nodiscard]] QEasingCurve emphasized() const {
|
||||
return m_easing->emphasized();
|
||||
}
|
||||
[[nodiscard]] QEasingCurve emphasizedAccel() const {
|
||||
return m_easing->emphasizedAccel();
|
||||
}
|
||||
[[nodiscard]] QEasingCurve emphasizedDecel() const {
|
||||
return m_easing->emphasizedDecel();
|
||||
}
|
||||
[[nodiscard]] QEasingCurve standard() const { return m_easing->standard(); }
|
||||
[[nodiscard]] QEasingCurve standardAccel() const {
|
||||
return m_easing->standardAccel();
|
||||
}
|
||||
[[nodiscard]] QEasingCurve standardDecel() const {
|
||||
return m_easing->standardDecel();
|
||||
}
|
||||
[[nodiscard]] QEasingCurve expressiveFastSpatial() const {
|
||||
return m_easing->expressiveFastSpatial();
|
||||
}
|
||||
[[nodiscard]] QEasingCurve expressiveDefaultSpatial() const {
|
||||
return m_easing->expressiveDefaultSpatial();
|
||||
}
|
||||
[[nodiscard]] QEasingCurve expressiveSlowSpatial() const {
|
||||
return m_easing->expressiveSlowSpatial();
|
||||
}
|
||||
[[nodiscard]] QEasingCurve expressiveFastEffects() const {
|
||||
return m_easing->expressiveFastEffects();
|
||||
}
|
||||
[[nodiscard]] QEasingCurve expressiveDefaultEffects() const {
|
||||
return m_easing->expressiveDefaultEffects();
|
||||
}
|
||||
[[nodiscard]] QEasingCurve expressiveSlowEffects() const {
|
||||
return m_easing->expressiveSlowEffects();
|
||||
}
|
||||
|
||||
signals:
|
||||
void curvesChanged();
|
||||
|
||||
private:
|
||||
AnimCurves* m_curves;
|
||||
AnimEasing* m_easing;
|
||||
CONFIG_PROPERTY(int, processesWidth, 1200)
|
||||
CONFIG_PROPERTY(int, processesHeight, 800)
|
||||
CONFIG_PROPERTY(int, processesHeaderHeight, 40)
|
||||
CONFIG_PROPERTY(int, processesRowHeight, 40)
|
||||
CONFIG_PROPERTY(int, dateTimeWidth, 110)
|
||||
CONFIG_PROPERTY(int, infoIconSize, 25)
|
||||
CONFIG_PROPERTY(int, infoWidth, 200)
|
||||
CONFIG_PROPERTY(int, mediaCoverArtSize, 150)
|
||||
CONFIG_PROPERTY(int, mediaProgressSweep, 180)
|
||||
CONFIG_PROPERTY(int, mediaProgressThickness, 8)
|
||||
CONFIG_PROPERTY(int, mediaVisualizerSize, 200)
|
||||
CONFIG_PROPERTY(int, mediaWidth, 200)
|
||||
CONFIG_PROPERTY(int, resourceProgessThickness, 10)
|
||||
CONFIG_PROPERTY(int, resourceSize, 200)
|
||||
CONFIG_PROPERTY(int, tabIndicatorHeight, 3)
|
||||
CONFIG_PROPERTY(int, tabIndicatorSpacing, 5)
|
||||
CONFIG_PROPERTY(int, weatherWidth, 250)
|
||||
};
|
||||
|
||||
class BarTokens : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class SizeTokens : public settings::ObjectNode {
|
||||
CONFIG_NODE(SizeTokens, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(int, innerSize, 28)
|
||||
|
||||
public:
|
||||
explicit BarTokens(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
};
|
||||
|
||||
class Tokens : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
CONFIG_SUBOBJECT(AppearanceRounding, rounding)
|
||||
CONFIG_SUBOBJECT(AppearancePadding, padding)
|
||||
CONFIG_SUBOBJECT(AppearanceSpacing, spacing)
|
||||
CONFIG_SUBOBJECT(FontTokens, font)
|
||||
CONFIG_SUBOBJECT(AnimTokens, anim)
|
||||
CONFIG_SUBOBJECT(DashboardTokens, dashboard)
|
||||
CONFIG_SUBOBJECT(BarTokens, bar)
|
||||
|
||||
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)) {}
|
||||
|
||||
static Tokens* create(QQmlEngine*, QJSEngine*) { return new Tokens(); }
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "configobject.hpp"
|
||||
#include "configlist.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
#include <QVariantList>
|
||||
#include <QStringList>
|
||||
@@ -9,75 +8,57 @@
|
||||
|
||||
namespace ZShell::config {
|
||||
|
||||
class UtilitiesSizes : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class UtilitiesSizes : public settings::ObjectNode {
|
||||
CONFIG_NODE(UtilitiesSizes, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(int, toastWidth, 430)
|
||||
CFG_PROPERTY(int, width, 430)
|
||||
|
||||
public:
|
||||
explicit UtilitiesSizes(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(int, toastWidth, 430)
|
||||
CONFIG_PROPERTY(int, width, 430)
|
||||
};
|
||||
|
||||
class Toasts : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Toasts : public settings::ObjectNode {
|
||||
CONFIG_NODE(Toasts, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(bool, audioInputChanged, true)
|
||||
CFG_PROPERTY(bool, audioOutputChanged, true)
|
||||
CFG_PROPERTY(bool, capsLockChanged, true)
|
||||
CFG_PROPERTY(bool, chargingChanged, true)
|
||||
CFG_PROPERTY(bool, configLoaded, true)
|
||||
CFG_PROPERTY(bool, dndChanged, true)
|
||||
CFG_PROPERTY(bool, gameModeChanged, true)
|
||||
CFG_PROPERTY(bool, kbLayoutChanged, true)
|
||||
CFG_PROPERTY(bool, kbLimit, true)
|
||||
CFG_PROPERTY(bool, nowPlaying, false)
|
||||
CFG_PROPERTY(bool, numLockChanged, true)
|
||||
CFG_PROPERTY(bool, vpnChanged, true)
|
||||
|
||||
public:
|
||||
explicit Toasts(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(bool, audioInputChanged, true)
|
||||
CONFIG_PROPERTY(bool, audioOutputChanged, true)
|
||||
CONFIG_PROPERTY(bool, capsLockChanged, true)
|
||||
CONFIG_PROPERTY(bool, chargingChanged, true)
|
||||
CONFIG_PROPERTY(bool, configLoaded, true)
|
||||
CONFIG_PROPERTY(bool, dndChanged, true)
|
||||
CONFIG_PROPERTY(bool, gameModeChanged, true)
|
||||
CONFIG_PROPERTY(bool, kbLayoutChanged, true)
|
||||
CONFIG_PROPERTY(bool, kbLimit, true)
|
||||
CONFIG_PROPERTY(bool, nowPlaying, false)
|
||||
CONFIG_PROPERTY(bool, numLockChanged, true)
|
||||
CONFIG_PROPERTY(bool, vpnChanged, true)
|
||||
};
|
||||
|
||||
class Vpn : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Vpn : public settings::ObjectNode {
|
||||
CONFIG_NODE(Vpn, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(bool, enabled, false)
|
||||
CFG_PROPERTY(QStringList, provider, (QStringList{QStringLiteral("netbird")}))
|
||||
|
||||
public:
|
||||
explicit Vpn(QObject* parent = nullptr) : ConfigObject(parent) {}
|
||||
CONFIG_PROPERTY(bool, enabled, false)
|
||||
CONFIG_PROPERTY(
|
||||
QStringList, provider, (QStringList{QStringLiteral("netbird")}))
|
||||
};
|
||||
|
||||
class Utilities : public ConfigObject {
|
||||
Q_OBJECT
|
||||
QML_ANONYMOUS
|
||||
class Utilities : public settings::ObjectNode {
|
||||
CONFIG_NODE(Utilities, settings::ObjectNode)
|
||||
|
||||
CFG_PROPERTY(bool, enabled, true)
|
||||
CFG_PROPERTY(int, maxToasts, 4)
|
||||
CONFIG_PROPERTY(bool, enabled, true)
|
||||
CONFIG_PROPERTY(int, maxToasts, 4)
|
||||
CONFIG_SUBOBJECT(UtilitiesSizes, sizes)
|
||||
CONFIG_SUBOBJECT(Toasts, toasts)
|
||||
CONFIG_SUBOBJECT(Vpn, vpn)
|
||||
CONFIG_LIST(
|
||||
EntryList,
|
||||
quickToggles,
|
||||
{LIST_ENTRY(dnd, true),
|
||||
LIST_ENTRY(wifi, true),
|
||||
LIST_ENTRY(settings, true),
|
||||
LIST_ENTRY(bluetooth, true),
|
||||
LIST_ENTRY(mic, true),
|
||||
LIST_ENTRY(audio, true),
|
||||
LIST_ENTRY(gameMode, true)})
|
||||
|
||||
public:
|
||||
explicit Utilities(QObject* parent = nullptr)
|
||||
: ConfigObject(parent)
|
||||
, m_sizes(new UtilitiesSizes(this))
|
||||
, m_toasts(new Toasts(this))
|
||||
, m_vpn(new Vpn(this)) {}
|
||||
DEFAULT_ARG(
|
||||
{LIST_ENTRY(dnd, true),
|
||||
LIST_ENTRY(wifi, true),
|
||||
LIST_ENTRY(settings, true),
|
||||
LIST_ENTRY(bluetooth, true),
|
||||
LIST_ENTRY(mic, true),
|
||||
LIST_ENTRY(audio, true),
|
||||
LIST_ENTRY(gameMode, true)}))
|
||||
};
|
||||
|
||||
} // namespace ZShell::config
|
||||
|
||||
Reference in New Issue
Block a user