fix: define declared method + insert new bar entries to config file list + rename Hyprsunset -> Nightlight
This commit is contained in:
@@ -11,435 +11,525 @@ using Qt::StringLiterals::operator""_s;
|
||||
namespace {
|
||||
|
||||
QString valuesKey() {
|
||||
return u"values"_s;
|
||||
return u"values"_s;
|
||||
}
|
||||
|
||||
void deleteNode(Node* node) {
|
||||
node->detachFallback();
|
||||
node->setParent(nullptr);
|
||||
node->deleteLater();
|
||||
node->detachFallback();
|
||||
node->setParent(nullptr);
|
||||
node->deleteLater();
|
||||
}
|
||||
|
||||
bool isVariantListType(const QVariant& value) {
|
||||
return value.typeId() == QMetaType::QVariantList || value.typeId() == qMetaTypeId<QList<QVariantMap>>();
|
||||
return value.typeId() == QMetaType::QVariantList ||
|
||||
value.typeId() == qMetaTypeId<QList<QVariantMap>>();
|
||||
}
|
||||
|
||||
QList<QVariantMap> toMaps(const QVariant& value) {
|
||||
if (value.typeId() == qMetaTypeId<QList<QVariantMap>>())
|
||||
return value.value<QList<QVariantMap>>();
|
||||
if (value.typeId() == qMetaTypeId<QList<QVariantMap>>())
|
||||
return value.value<QList<QVariantMap>>();
|
||||
|
||||
const auto list = value.toList();
|
||||
QList<QVariantMap> maps;
|
||||
maps.reserve(list.count());
|
||||
const auto list = value.toList();
|
||||
QList<QVariantMap> maps;
|
||||
maps.reserve(list.count());
|
||||
|
||||
for (const auto& el : list) {
|
||||
if (el.typeId() != QMetaType::QVariantMap) {
|
||||
qCCritical(lcSettings, "Expected a map in list properties, got %s.", el.typeName());
|
||||
continue;
|
||||
}
|
||||
for (const auto& el : list) {
|
||||
if (el.typeId() != QMetaType::QVariantMap) {
|
||||
qCCritical(
|
||||
lcSettings,
|
||||
"Expected a map in list properties, got %s.",
|
||||
el.typeName());
|
||||
continue;
|
||||
}
|
||||
|
||||
maps << el.toMap();
|
||||
}
|
||||
maps << el.toMap();
|
||||
}
|
||||
|
||||
return maps;
|
||||
return maps;
|
||||
}
|
||||
|
||||
void syncNodeRecursive(Node* node, const QVariantMap& props) {
|
||||
const auto& schema = node->schema();
|
||||
const auto& schema = node->schema();
|
||||
|
||||
for (const auto& [key, val] : props.asKeyValueRange()) {
|
||||
const auto* desc = schema.get(key);
|
||||
for (const auto& [key, val] : props.asKeyValueRange()) {
|
||||
const auto* desc = schema.get(key);
|
||||
|
||||
if (!desc) {
|
||||
qCCritical(lcSettings, "Unknown key %s in props for %s, something is wrong.", qUtf8Printable(key),
|
||||
qUtf8Printable(node->path()));
|
||||
continue;
|
||||
}
|
||||
if (!desc) {
|
||||
qCCritical(
|
||||
lcSettings,
|
||||
"Unknown key %s in props for %s, something is wrong.",
|
||||
qUtf8Printable(key),
|
||||
qUtf8Printable(node->path()));
|
||||
continue;
|
||||
}
|
||||
|
||||
// `setValue` warns on type mismatches itself
|
||||
if (!desc->isNode) {
|
||||
node->setValue(key, val);
|
||||
continue;
|
||||
}
|
||||
// `setValue` warns on type mismatches itself
|
||||
if (!desc->isNode) {
|
||||
node->setValue(key, val);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* const child = node->value(key).value<Node*>();
|
||||
auto* const child = node->value(key).value<Node*>();
|
||||
|
||||
// Nested lists take their elements straight from props
|
||||
if (auto* const list = qobject_cast<ListNode*>(child)) {
|
||||
if (!isVariantListType(val)) {
|
||||
qCCritical(lcSettings, "Expected a list for node key %s on %s, got %s.", qUtf8Printable(key),
|
||||
qUtf8Printable(node->path()), val.typeName());
|
||||
continue;
|
||||
}
|
||||
// Nested lists take their elements straight from props
|
||||
if (auto* const list = qobject_cast<ListNode*>(child)) {
|
||||
if (!isVariantListType(val)) {
|
||||
qCCritical(
|
||||
lcSettings,
|
||||
"Expected a list for node key %s on %s, got %s.",
|
||||
qUtf8Printable(key),
|
||||
qUtf8Printable(node->path()),
|
||||
val.typeName());
|
||||
continue;
|
||||
}
|
||||
|
||||
list->setValue(valuesKey(), val);
|
||||
continue;
|
||||
}
|
||||
list->setValue(valuesKey(), val);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (val.typeId() != QMetaType::QVariantMap) {
|
||||
qCCritical(lcSettings, "Expected a map for node key %s on %s, got %s.", qUtf8Printable(key),
|
||||
qUtf8Printable(node->path()), val.typeName());
|
||||
continue;
|
||||
}
|
||||
if (val.typeId() != QMetaType::QVariantMap) {
|
||||
qCCritical(
|
||||
lcSettings,
|
||||
"Expected a map for node key %s on %s, got %s.",
|
||||
qUtf8Printable(key),
|
||||
qUtf8Printable(node->path()),
|
||||
val.typeName());
|
||||
continue;
|
||||
}
|
||||
|
||||
syncNodeRecursive(child, val.toMap());
|
||||
}
|
||||
syncNodeRecursive(child, val.toMap());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ListNode::ListNode(ListNode* fallback, QObject* parent, bool globalOnly)
|
||||
: Node(fallback, parent, globalOnly) {
|
||||
if (fallback) {
|
||||
// Disconnect generic fallback notify, lists use a custom one
|
||||
QObject::disconnect(fallback, &ListNode::optionChanged, this, nullptr);
|
||||
: Node(fallback, parent, globalOnly) {
|
||||
if (fallback) {
|
||||
// Disconnect generic fallback notify, lists use a custom one
|
||||
QObject::disconnect(fallback, &ListNode::optionChanged, this, nullptr);
|
||||
|
||||
if (!m_globalOnly)
|
||||
QObject::connect(fallback, &ListNode::elementsChanged, this, &ListNode::onFallbackListNotify);
|
||||
}
|
||||
if (!m_globalOnly)
|
||||
QObject::connect(
|
||||
fallback,
|
||||
&ListNode::elementsChanged,
|
||||
this,
|
||||
&ListNode::onFallbackListNotify);
|
||||
}
|
||||
}
|
||||
|
||||
qsizetype ListNode::count() const {
|
||||
return m_elements.count();
|
||||
return m_elements.count();
|
||||
}
|
||||
|
||||
QVariantList ListNode::values() const {
|
||||
QVariantList list;
|
||||
list.reserve(m_elements.count());
|
||||
for (auto* const element : m_elements)
|
||||
list << QVariant::fromValue(element);
|
||||
return list;
|
||||
QVariantList list;
|
||||
list.reserve(m_elements.count());
|
||||
for (auto* const element : m_elements)
|
||||
list << QVariant::fromValue(element);
|
||||
return list;
|
||||
}
|
||||
|
||||
void ListNode::remove(qsizetype index) {
|
||||
if (rejectGlobalMutation())
|
||||
return;
|
||||
if (rejectGlobalMutation()) return;
|
||||
|
||||
const WriteScope scope(this, WriteOrigin::Qml);
|
||||
const WriteScope scope(this, WriteOrigin::Qml);
|
||||
|
||||
if (!validIndex(index)) {
|
||||
qCWarning(lcSettings, "Attempted to remove invalid index %lld from list node %s, ignoring.", index,
|
||||
qUtf8Printable(path()));
|
||||
if (!validIndex(index)) {
|
||||
qCWarning(
|
||||
lcSettings,
|
||||
"Attempted to remove invalid index %lld from list node %s, "
|
||||
"ignoring.",
|
||||
index,
|
||||
qUtf8Printable(path()));
|
||||
|
||||
recordWrite(valuesKey(), false);
|
||||
return;
|
||||
}
|
||||
recordWrite(valuesKey(), false);
|
||||
return;
|
||||
}
|
||||
|
||||
deleteNode(m_elements.takeAt(index));
|
||||
deleteNode(m_elements.takeAt(index));
|
||||
|
||||
recordWrite(valuesKey(), true);
|
||||
emit countChanged();
|
||||
emit elementsChanged({}, { index }, {});
|
||||
recordWrite(valuesKey(), true);
|
||||
emit countChanged();
|
||||
emit elementsChanged({}, {index}, {});
|
||||
}
|
||||
|
||||
void ListNode::move(qsizetype from, qsizetype to) {
|
||||
if (rejectGlobalMutation())
|
||||
return;
|
||||
if (rejectGlobalMutation()) return;
|
||||
|
||||
const WriteScope scope(this, WriteOrigin::Qml);
|
||||
const WriteScope scope(this, WriteOrigin::Qml);
|
||||
|
||||
const auto fromInvalid = !validIndex(from);
|
||||
if (fromInvalid || !validIndex(to)) {
|
||||
if (fromInvalid)
|
||||
qCWarning(lcSettings, "Attempted to move invalid index %lld to index %lld on list node %s, ignoring.", from,
|
||||
to, qUtf8Printable(path()));
|
||||
else
|
||||
qCWarning(lcSettings, "Attempted to move index %lld to invalid index %lld on list node %s, ignoring.", from,
|
||||
to, qUtf8Printable(path()));
|
||||
const auto fromInvalid = !validIndex(from);
|
||||
if (fromInvalid || !validIndex(to)) {
|
||||
if (fromInvalid)
|
||||
qCWarning(
|
||||
lcSettings,
|
||||
"Attempted to move invalid index %lld to index %lld on list "
|
||||
"node %s, ignoring.",
|
||||
from,
|
||||
to,
|
||||
qUtf8Printable(path()));
|
||||
else
|
||||
qCWarning(
|
||||
lcSettings,
|
||||
"Attempted to move index %lld to invalid index %lld on list "
|
||||
"node %s, ignoring.",
|
||||
from,
|
||||
to,
|
||||
qUtf8Printable(path()));
|
||||
|
||||
recordWrite(valuesKey(), false);
|
||||
return;
|
||||
}
|
||||
recordWrite(valuesKey(), false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_elements.move(from, to);
|
||||
m_elements.move(from, to);
|
||||
|
||||
recordWrite(valuesKey(), true);
|
||||
emit elementsChanged({}, {}, { { from, to } });
|
||||
recordWrite(valuesKey(), true);
|
||||
emit elementsChanged({}, {}, {{from, to}});
|
||||
}
|
||||
|
||||
void ListNode::clear() {
|
||||
if (rejectGlobalMutation())
|
||||
return;
|
||||
if (rejectGlobalMutation()) return;
|
||||
|
||||
const WriteScope scope(this, WriteOrigin::Qml);
|
||||
const WriteScope scope(this, WriteOrigin::Qml);
|
||||
|
||||
if (m_elements.isEmpty()) {
|
||||
recordWrite(valuesKey(), false);
|
||||
return;
|
||||
}
|
||||
if (m_elements.isEmpty()) {
|
||||
recordWrite(valuesKey(), false);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto len = m_elements.count();
|
||||
const auto len = m_elements.count();
|
||||
|
||||
for (auto* const element : std::as_const(m_elements))
|
||||
deleteNode(element);
|
||||
m_elements.clear();
|
||||
for (auto* const element : std::as_const(m_elements))
|
||||
deleteNode(element);
|
||||
m_elements.clear();
|
||||
|
||||
recordWrite(valuesKey(), true);
|
||||
emit countChanged();
|
||||
recordWrite(valuesKey(), true);
|
||||
emit countChanged();
|
||||
|
||||
NodeChanges removed(len);
|
||||
std::iota(removed.begin(), removed.end(), 0);
|
||||
emit elementsChanged({}, removed, {});
|
||||
NodeChanges removed(len);
|
||||
std::iota(removed.begin(), removed.end(), 0);
|
||||
emit elementsChanged({}, removed, {});
|
||||
}
|
||||
|
||||
QString ListNode::pathFor(const QString& key) const {
|
||||
return elementPath(path(), key);
|
||||
return elementPath(path(), key);
|
||||
}
|
||||
|
||||
const Schema& ListNode::schema() const {
|
||||
// Offset +1 so count is not registered (allow read only cause values is read only)
|
||||
static const auto k_schema = Schema::build(&staticMetaObject, Node::staticMetaObject.propertyCount() + 1, true);
|
||||
return k_schema;
|
||||
// Offset +1 so count is not registered (allow read only cause values is read only)
|
||||
static const auto k_schema = Schema::build(
|
||||
&staticMetaObject, Node::staticMetaObject.propertyCount() + 1, true);
|
||||
return k_schema;
|
||||
}
|
||||
|
||||
QVariant ListNode::value(const QString& key) const {
|
||||
if (key != valuesKey()) {
|
||||
qCCritical(lcSettings,
|
||||
"Attempted to read %s on list node %s. List nodes only have a 'values' key, something is wrong.",
|
||||
qUtf8Printable(key), qUtf8Printable(path()));
|
||||
return {};
|
||||
}
|
||||
if (key != valuesKey()) {
|
||||
qCCritical(
|
||||
lcSettings,
|
||||
"Attempted to read %s on list node %s. List nodes only have a "
|
||||
"'values' key, something is wrong.",
|
||||
qUtf8Printable(key),
|
||||
qUtf8Printable(path()));
|
||||
return {};
|
||||
}
|
||||
|
||||
return QVariant::fromValue(m_elements);
|
||||
return QVariant::fromValue(m_elements);
|
||||
}
|
||||
|
||||
bool ListNode::setValue(const QString& key, const QVariant& value) {
|
||||
return setValue(key, value, nullptr);
|
||||
return setValue(key, value, nullptr);
|
||||
}
|
||||
|
||||
bool ListNode::setValue(const QString& key, const QVariant& value, QList<Diagnostic>* diagnostics) {
|
||||
if (key != valuesKey()) {
|
||||
qCCritical(lcSettings,
|
||||
"Attempted to write %s on list node %s. List nodes only have a 'values' key, something is wrong.",
|
||||
qUtf8Printable(key), qUtf8Printable(path()));
|
||||
return false;
|
||||
}
|
||||
bool ListNode::setValue(
|
||||
const QString& key, const QVariant& value, QList<Diagnostic>* diagnostics) {
|
||||
if (key != valuesKey()) {
|
||||
qCCritical(
|
||||
lcSettings,
|
||||
"Attempted to write %s on list node %s. List nodes only have a "
|
||||
"'values' key, something is wrong.",
|
||||
qUtf8Printable(key),
|
||||
qUtf8Printable(path()));
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto len = m_elements.count();
|
||||
const auto len = m_elements.count();
|
||||
|
||||
for (auto* const element : std::as_const(m_elements))
|
||||
deleteNode(element);
|
||||
m_elements.clear();
|
||||
for (auto* const element : std::as_const(m_elements))
|
||||
deleteNode(element);
|
||||
m_elements.clear();
|
||||
|
||||
if (value.typeId() == qMetaTypeId<QList<Node*>>()) {
|
||||
// On reset to fallback
|
||||
const auto nodes = value.value<QList<Node*>>();
|
||||
for (auto* const node : nodes)
|
||||
insertNode(m_elements.count(), node);
|
||||
} else if (isVariantListType(value)) {
|
||||
// From defaults or QML
|
||||
const auto list = toMaps(value);
|
||||
for (qsizetype i = 0; i < list.count(); ++i)
|
||||
insertNode(i, list.at(i), fallbackFor(i));
|
||||
} else if (value.typeId() == QMetaType::QJsonArray) {
|
||||
// From syncJson
|
||||
const auto array = value.toJsonArray();
|
||||
for (const auto& json : array)
|
||||
insertNode(m_elements.count(), json.toObject(), *diagnostics);
|
||||
} else {
|
||||
qCCritical(lcSettings, "Unexpected type %s for list node %s, something is wrong.", value.typeName(),
|
||||
qUtf8Printable(path()));
|
||||
return false;
|
||||
}
|
||||
if (value.typeId() == qMetaTypeId<QList<Node*>>()) {
|
||||
// On reset to fallback
|
||||
const auto nodes = value.value<QList<Node*>>();
|
||||
for (auto* const node : nodes)
|
||||
insertNode(m_elements.count(), node);
|
||||
} else if (isVariantListType(value)) {
|
||||
// From defaults or QML
|
||||
const auto list = toMaps(value);
|
||||
for (qsizetype i = 0; i < list.count(); ++i)
|
||||
insertNode(i, list.at(i), fallbackFor(i));
|
||||
} else if (value.typeId() == QMetaType::QJsonArray) {
|
||||
// From syncJson
|
||||
const auto array = value.toJsonArray();
|
||||
for (const auto& json : array)
|
||||
insertNode(m_elements.count(), json.toObject(), *diagnostics);
|
||||
insertMissingDefaults();
|
||||
} else {
|
||||
qCCritical(
|
||||
lcSettings,
|
||||
"Unexpected type %s for list node %s, something is wrong.",
|
||||
value.typeName(),
|
||||
qUtf8Printable(path()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!recordWrite(valuesKey(), true))
|
||||
return false;
|
||||
if (!recordWrite(valuesKey(), true)) return false;
|
||||
|
||||
if (len != m_elements.count())
|
||||
emit countChanged();
|
||||
if (len != m_elements.count()) emit countChanged();
|
||||
|
||||
NodeChanges added(m_elements.count());
|
||||
NodeChanges removed(len);
|
||||
std::iota(added.begin(), added.end(), 0);
|
||||
std::iota(removed.begin(), removed.end(), 0);
|
||||
emit elementsChanged(added, removed, {});
|
||||
NodeChanges added(m_elements.count());
|
||||
NodeChanges removed(len);
|
||||
std::iota(added.begin(), added.end(), 0);
|
||||
std::iota(removed.begin(), removed.end(), 0);
|
||||
emit elementsChanged(added, removed, {});
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ListNode::insertMissingDefaults() {
|
||||
if (fallbackNode()) return;
|
||||
|
||||
const auto* desc = getDescriptor();
|
||||
if (!desc) return;
|
||||
|
||||
const auto idKey = desc->identityKey();
|
||||
if (idKey.isEmpty()) return;
|
||||
|
||||
if (!m_elements.isEmpty() && !m_elements.first()->schema().get(idKey)) {
|
||||
qCCritical(
|
||||
lcSettings,
|
||||
"List %s has identityKey '%s' which its elements don't define.",
|
||||
qUtf8Printable(path()),
|
||||
qUtf8Printable(idKey));
|
||||
return;
|
||||
}
|
||||
|
||||
QHash<QString, QList<Node*>> unmatched;
|
||||
for (auto* const element : std::as_const(m_elements))
|
||||
unmatched[element->value(idKey).toString()] << element;
|
||||
|
||||
Node* anchor = nullptr;
|
||||
for (const auto& props : defaultValue()) {
|
||||
const auto id = props.value(idKey).toString();
|
||||
if (id.isEmpty()) continue;
|
||||
|
||||
auto& candidates = unmatched[id];
|
||||
if (!candidates.isEmpty()) {
|
||||
anchor = candidates.takeFirst();
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto index = anchor ? m_elements.indexOf(anchor) + 1 : 0;
|
||||
anchor = insertNode(index, props, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void ListNode::resetToDefaults() {
|
||||
if (isNested()) {
|
||||
qCCritical(lcSettings, "List node %s has a parent list node, resetToDefaults should never be called on it.",
|
||||
qUtf8Printable(path()));
|
||||
return;
|
||||
}
|
||||
if (isNested()) {
|
||||
qCCritical(
|
||||
lcSettings,
|
||||
"List node %s has a parent list node, resetToDefaults should never "
|
||||
"be called on it.",
|
||||
qUtf8Printable(path()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't reset global only list nodes on overlays
|
||||
if (fallbackNode() && m_globalOnly)
|
||||
return;
|
||||
// Don't reset global only list nodes on overlays
|
||||
if (fallbackNode() && m_globalOnly) return;
|
||||
|
||||
const WriteScope scope(this, WriteOrigin::FileReset);
|
||||
setValue(valuesKey(), fallbackNode() ? fallbackNode()->value(valuesKey()) : QVariant::fromValue(defaultValue()));
|
||||
const WriteScope scope(this, WriteOrigin::FileReset);
|
||||
setValue(
|
||||
valuesKey(),
|
||||
fallbackNode() ? fallbackNode()->value(valuesKey())
|
||||
: QVariant::fromValue(defaultValue()));
|
||||
}
|
||||
|
||||
QJsonValue ListNode::toJson(bool sparse) const {
|
||||
Q_UNUSED(sparse) // Lists can't be sparse
|
||||
Q_UNUSED(sparse) // Lists can't be sparse
|
||||
|
||||
QJsonArray array;
|
||||
for (auto* const element : m_elements)
|
||||
array << element->toJson(false);
|
||||
return array;
|
||||
QJsonArray array;
|
||||
for (auto* const element : m_elements)
|
||||
array << element->toJson(false);
|
||||
return array;
|
||||
}
|
||||
|
||||
bool ListNode::syncJson(const QJsonValue& json, QList<Diagnostic>& diagnostics) {
|
||||
if (!json.isArray()) {
|
||||
const auto d = Diagnostic::mismatch(ExpectedType::Array, json, path());
|
||||
qCWarning(lcSettings, "Error decoding option %s: %s", qUtf8Printable(d.option),
|
||||
qUtf8Printable(d.message));
|
||||
diagnostics << d;
|
||||
return false;
|
||||
}
|
||||
if (!json.isArray()) {
|
||||
const auto d = Diagnostic::mismatch(ExpectedType::Array, json, path());
|
||||
qCWarning(
|
||||
lcSettings,
|
||||
"Error decoding option %s: %s",
|
||||
qUtf8Printable(d.option),
|
||||
qUtf8Printable(d.message));
|
||||
diagnostics << d;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Refuse syncs to global only list nodes on overlays
|
||||
if (rejectGlobalSync(diagnostics))
|
||||
return false;
|
||||
// Refuse syncs to global only list nodes on overlays
|
||||
if (rejectGlobalSync(diagnostics)) return false;
|
||||
|
||||
const WriteScope scope(this, WriteOrigin::File);
|
||||
setValue(valuesKey(), json.toArray(), &diagnostics);
|
||||
const WriteScope scope(this, WriteOrigin::File);
|
||||
setValue(valuesKey(), json.toArray(), &diagnostics);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ListNode::recordWrite(const QString& key, bool changed) {
|
||||
const auto wasOverride = isOverride(key);
|
||||
const auto notify = Node::recordWrite(key, changed);
|
||||
const auto wasOverride = isOverride(key);
|
||||
const auto notify = Node::recordWrite(key, changed);
|
||||
|
||||
// Elements inside overridden lists have no fallbacks, so detach here
|
||||
if (!wasOverride && isOverride(key))
|
||||
for (auto* const element : std::as_const(m_elements))
|
||||
element->detachFallback();
|
||||
// Elements inside overridden lists have no fallbacks, so detach here
|
||||
if (!wasOverride && isOverride(key))
|
||||
for (auto* const element : std::as_const(m_elements))
|
||||
element->detachFallback();
|
||||
|
||||
return notify;
|
||||
return notify;
|
||||
}
|
||||
|
||||
QString ListNode::keyOf(const Node* child) const {
|
||||
return QString::number(m_elements.indexOf(const_cast<Node*>(child)));
|
||||
return QString::number(m_elements.indexOf(const_cast<Node*>(child)));
|
||||
}
|
||||
|
||||
Node* ListNode::elementAt(qsizetype index) const {
|
||||
if (!validIndex(index))
|
||||
return nullptr;
|
||||
return m_elements.at(index);
|
||||
if (!validIndex(index)) return nullptr;
|
||||
return m_elements.at(index);
|
||||
}
|
||||
|
||||
Node* ListNode::insertElement(const QVariantMap& props, qsizetype index) {
|
||||
if (rejectGlobalMutation())
|
||||
return nullptr;
|
||||
if (rejectGlobalMutation()) return nullptr;
|
||||
|
||||
const WriteScope scope(this, WriteOrigin::Qml);
|
||||
const WriteScope scope(this, WriteOrigin::Qml);
|
||||
|
||||
if (!validIndex(index)) // Invalid index means append
|
||||
index = m_elements.count();
|
||||
if (!validIndex(index)) // Invalid index means append
|
||||
index = m_elements.count();
|
||||
|
||||
auto* const element = insertNode(index, props, nullptr);
|
||||
auto* const element = insertNode(index, props, nullptr);
|
||||
|
||||
recordWrite(valuesKey(), true);
|
||||
emit countChanged();
|
||||
emit elementsChanged({ index }, {}, {});
|
||||
recordWrite(valuesKey(), true);
|
||||
emit countChanged();
|
||||
emit elementsChanged({index}, {}, {});
|
||||
|
||||
return element;
|
||||
return element;
|
||||
}
|
||||
|
||||
QList<QVariantMap> ListNode::defaultValue() const {
|
||||
const auto* desc = getDescriptor();
|
||||
if (!desc)
|
||||
return {};
|
||||
return desc->defaultValue().value<QList<QVariantMap>>();
|
||||
const auto* desc = getDescriptor();
|
||||
if (!desc) return {};
|
||||
return desc->defaultValue().value<QList<QVariantMap>>();
|
||||
}
|
||||
|
||||
bool ListNode::isNested() const {
|
||||
return qobject_cast<ListNode*>(parentNode());
|
||||
return qobject_cast<ListNode*>(parentNode());
|
||||
}
|
||||
|
||||
bool ListNode::rejectGlobalMutation() const {
|
||||
if (!m_globalOnly || !fallbackNode())
|
||||
return false;
|
||||
if (!m_globalOnly || !fallbackNode()) return false;
|
||||
|
||||
qCWarning(lcSettings,
|
||||
"Attempted to mutate global list %s from an overlay layer, ignoring. "
|
||||
"This should not be used, mutate global lists from the global layer instead.",
|
||||
qUtf8Printable(path()));
|
||||
qCWarning(
|
||||
lcSettings,
|
||||
"Attempted to mutate global list %s from an overlay layer, ignoring. "
|
||||
"This should not be used, mutate global lists from the global layer "
|
||||
"instead.",
|
||||
qUtf8Printable(path()));
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ListNode::validIndex(qsizetype index) const {
|
||||
return index >= 0 && index < m_elements.count();
|
||||
return index >= 0 && index < m_elements.count();
|
||||
}
|
||||
|
||||
const Descriptor* ListNode::getDescriptor() const {
|
||||
if (!parentNode()) { // List nodes should not be root nodes
|
||||
qCCritical(lcSettings, "List node %s has no parent, something is wrong.", qUtf8Printable(path()));
|
||||
return nullptr;
|
||||
}
|
||||
if (!parentNode()) { // List nodes should not be root nodes
|
||||
qCCritical(
|
||||
lcSettings,
|
||||
"List node %s has no parent, something is wrong.",
|
||||
qUtf8Printable(path()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto* desc = parentNode()->schema().get(key());
|
||||
const auto* desc = parentNode()->schema().get(key());
|
||||
|
||||
if (!desc) {
|
||||
qCCritical(lcSettings, "List node %s is not in parent schema, something is wrong.", qUtf8Printable(path()));
|
||||
return nullptr;
|
||||
}
|
||||
if (!desc) {
|
||||
qCCritical(
|
||||
lcSettings,
|
||||
"List node %s is not in parent schema, something is wrong.",
|
||||
qUtf8Printable(path()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return desc;
|
||||
return desc;
|
||||
}
|
||||
|
||||
Node* ListNode::fallbackFor(qsizetype index) const {
|
||||
if (isOverride(valuesKey()))
|
||||
return nullptr;
|
||||
if (isOverride(valuesKey())) return nullptr;
|
||||
|
||||
const auto* fallback = static_cast<ListNode*>(fallbackNode());
|
||||
if (!fallback || !fallback->validIndex(index))
|
||||
return nullptr;
|
||||
const auto* fallback = static_cast<ListNode*>(fallbackNode());
|
||||
if (!fallback || !fallback->validIndex(index)) return nullptr;
|
||||
|
||||
return fallback->m_elements.at(index);
|
||||
return fallback->m_elements.at(index);
|
||||
}
|
||||
|
||||
Node* ListNode::insertNode(qsizetype index, const QVariantMap& props, Node* fallback) {
|
||||
auto* const node = createElement(fallback);
|
||||
m_elements.insert(index, node);
|
||||
Node* ListNode::insertNode(
|
||||
qsizetype index, const QVariantMap& props, Node* fallback) {
|
||||
auto* const node = createElement(fallback);
|
||||
m_elements.insert(index, node);
|
||||
|
||||
const WriteScope scope(node, WriteOrigin::Init);
|
||||
syncNodeRecursive(node, props);
|
||||
const WriteScope scope(node, WriteOrigin::Init);
|
||||
syncNodeRecursive(node, props);
|
||||
|
||||
return node;
|
||||
return node;
|
||||
}
|
||||
|
||||
Node* ListNode::insertNode(qsizetype index, Node* node) {
|
||||
auto* const copy = createElement(node);
|
||||
m_elements.insert(index, copy);
|
||||
return copy;
|
||||
auto* const copy = createElement(node);
|
||||
m_elements.insert(index, copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
Node* ListNode::insertNode(qsizetype index, const QJsonObject& json, QList<Diagnostic>& diagnostics) {
|
||||
// Write origin will be file/file reset, which is correct.
|
||||
// This function should only be called from the JSON path of setValue.
|
||||
auto* const node = createElement(nullptr); // Nodes synced from JSON get no fallback as they are overrides
|
||||
m_elements.insert(index, node);
|
||||
Node* ListNode::insertNode(
|
||||
qsizetype index, const QJsonObject& json, QList<Diagnostic>& diagnostics) {
|
||||
// Write origin will be file/file reset, which is correct.
|
||||
// This function should only be called from the JSON path of setValue.
|
||||
auto* const node = createElement(
|
||||
nullptr); // Nodes synced from JSON get no fallback as they are overrides
|
||||
m_elements.insert(index, node);
|
||||
|
||||
node->syncJson(json, diagnostics);
|
||||
node->syncJson(json, diagnostics);
|
||||
|
||||
return node;
|
||||
return node;
|
||||
}
|
||||
|
||||
void ListNode::onFallbackListNotify(const NodeChanges& added, const NodeChanges& removed, const MoveChanges& moved) {
|
||||
if (isOverride(valuesKey()))
|
||||
return;
|
||||
void ListNode::onFallbackListNotify(
|
||||
const NodeChanges& added,
|
||||
const NodeChanges& removed,
|
||||
const MoveChanges& moved) {
|
||||
if (isOverride(valuesKey())) return;
|
||||
|
||||
const WriteScope scope(this, WriteOrigin::Layer);
|
||||
const WriteScope scope(this, WriteOrigin::Layer);
|
||||
|
||||
for (const auto index : removed | std::views::reverse)
|
||||
deleteNode(m_elements.takeAt(index));
|
||||
for (const auto index : added)
|
||||
insertNode(index, fallbackFor(index));
|
||||
for (const auto& move : moved)
|
||||
m_elements.move(move.first, move.second);
|
||||
for (const auto index : removed | std::views::reverse)
|
||||
deleteNode(m_elements.takeAt(index));
|
||||
for (const auto index : added)
|
||||
insertNode(index, fallbackFor(index));
|
||||
for (const auto& move : moved)
|
||||
m_elements.move(move.first, move.second);
|
||||
|
||||
if (!recordWrite(valuesKey(), true))
|
||||
return;
|
||||
if (!recordWrite(valuesKey(), true)) return;
|
||||
|
||||
if (!added.isEmpty() || !removed.isEmpty())
|
||||
emit countChanged();
|
||||
emit elementsChanged(added, removed, moved);
|
||||
if (!added.isEmpty() || !removed.isEmpty()) emit countChanged();
|
||||
emit elementsChanged(added, removed, moved);
|
||||
}
|
||||
|
||||
} // namespace ZShell::settings
|
||||
|
||||
Reference in New Issue
Block a user