forked from quickshell/quickshell
core/variants: restructure Variants to match the design of Repeater
This commit is contained in:
parent
ffbdac9977
commit
48156a55b3
|
@ -83,12 +83,12 @@ class QuickshellGlobal: public QObject {
|
||||||
/// ```qml
|
/// ```qml
|
||||||
/// ShellRoot {
|
/// ShellRoot {
|
||||||
/// Variants {
|
/// Variants {
|
||||||
/// ShellWindow {
|
|
||||||
/// // ...
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // see Variants for details
|
/// // see Variants for details
|
||||||
/// variants: Quickshell.screens.map(screen => ({ screen }))
|
/// variants: Quickshell.screens
|
||||||
|
/// PanelWindow {
|
||||||
|
/// property var modelData
|
||||||
|
/// screen: modelData
|
||||||
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qqmlengine.h>
|
#include <qqmlengine.h>
|
||||||
|
#include <qqmllist.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qvariant.h>
|
||||||
|
|
||||||
#include "reload.hpp"
|
#include "reload.hpp"
|
||||||
|
|
||||||
|
@ -17,27 +20,44 @@ void Variants::onReload(QObject* oldInstance) {
|
||||||
if (old != nullptr) {
|
if (old != nullptr) {
|
||||||
auto& values = old->instances.values;
|
auto& values = old->instances.values;
|
||||||
|
|
||||||
int matchcount = 0;
|
if (variant.canConvert<QVariantMap>()) {
|
||||||
int matchi = 0;
|
auto variantMap = variant.value<QVariantMap>();
|
||||||
int i = 0;
|
|
||||||
for (auto& [valueSet, _]: values) {
|
int matchcount = 0;
|
||||||
int count = 0;
|
int matchi = 0;
|
||||||
for (auto& [k, v]: variant.toStdMap()) {
|
int i = 0;
|
||||||
if (valueSet.contains(k) && valueSet.value(k) == v) {
|
for (auto& [value, _]: values) {
|
||||||
count++;
|
if (!value.canConvert<QVariantMap>()) continue;
|
||||||
|
auto valueSet = value.value<QVariantMap>();
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for (auto [k, v]: variantMap.asKeyValueRange()) {
|
||||||
|
if (valueSet.contains(k) && valueSet.value(k) == v) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (count > matchcount) {
|
||||||
|
matchcount = count;
|
||||||
|
matchi = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count > matchcount) {
|
if (matchcount > 0) {
|
||||||
matchcount = count;
|
oldInstance = values.takeAt(matchi).second;
|
||||||
matchi = i;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
int i = 0;
|
||||||
|
for (auto& [value, _]: values) {
|
||||||
|
if (variant == value) {
|
||||||
|
oldInstance = values.takeAt(i).second;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchcount > 0) {
|
|
||||||
oldInstance = values.takeAt(matchi).second;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,9 +70,32 @@ void Variants::onReload(QObject* oldInstance) {
|
||||||
this->loaded = true;
|
this->loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Variants::setVariants(QVariantList variants) {
|
QVariant Variants::model() const { return QVariant::fromValue(this->mModel); }
|
||||||
this->mVariants = std::move(variants);
|
|
||||||
|
void Variants::setModel(const QVariant& model) {
|
||||||
|
if (model.canConvert<QVariantList>()) {
|
||||||
|
this->mModel = model.value<QVariantList>();
|
||||||
|
} else if (model.canConvert<QQmlListReference>()) {
|
||||||
|
auto list = model.value<QQmlListReference>();
|
||||||
|
if (!list.isReadable()) {
|
||||||
|
qWarning() << "Non readable list" << model << "assigned to Variants.model, Ignoring.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList model;
|
||||||
|
auto size = list.count();
|
||||||
|
for (auto i = 0; i < size; i++) {
|
||||||
|
model.push_back(QVariant::fromValue(list.at(i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mModel = std::move(model);
|
||||||
|
} else {
|
||||||
|
qWarning() << "Non list data" << model << "assigned to Variants.model, Ignoring.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this->updateVariants();
|
this->updateVariants();
|
||||||
|
emit this->modelChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Variants::componentComplete() {
|
void Variants::componentComplete() {
|
||||||
|
@ -61,14 +104,14 @@ void Variants::componentComplete() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Variants::updateVariants() {
|
void Variants::updateVariants() {
|
||||||
if (this->mComponent == nullptr) {
|
if (this->mDelegate == nullptr) {
|
||||||
qWarning() << "Variants instance does not have a component specified";
|
qWarning() << "Variants instance does not have a component specified";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clean up removed entries
|
// clean up removed entries
|
||||||
for (auto iter = this->instances.values.begin(); iter < this->instances.values.end();) {
|
for (auto iter = this->instances.values.begin(); iter < this->instances.values.end();) {
|
||||||
if (this->mVariants.contains(iter->first)) {
|
if (this->mModel.contains(iter->first)) {
|
||||||
iter++;
|
iter++;
|
||||||
} else {
|
} else {
|
||||||
iter->second->deleteLater();
|
iter->second->deleteLater();
|
||||||
|
@ -76,32 +119,31 @@ void Variants::updateVariants() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto iter = this->mVariants.begin(); iter < this->mVariants.end(); iter++) {
|
for (auto iter = this->mModel.begin(); iter < this->mModel.end(); iter++) {
|
||||||
auto& variantObj = *iter;
|
auto& variant = *iter;
|
||||||
if (!variantObj.canConvert<QVariantMap>()) {
|
for (auto iter2 = this->mModel.begin(); iter2 < iter; iter2++) {
|
||||||
qWarning() << "value passed to Variants is not an object and will be ignored:" << variantObj;
|
if (*iter2 == variant) {
|
||||||
} else {
|
qWarning() << "same value specified twice in Variants, duplicates will be ignored:"
|
||||||
auto variant = variantObj.value<QVariantMap>();
|
<< variant;
|
||||||
|
goto outer;
|
||||||
for (auto iter2 = this->mVariants.begin(); iter2 < iter; iter2++) {
|
|
||||||
if (*iter2 == variantObj) {
|
|
||||||
qWarning() << "same value specified twice in Variants, duplicates will be ignored:"
|
|
||||||
<< variantObj;
|
|
||||||
goto outer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
if (this->instances.contains(variant)) {
|
if (this->instances.contains(variant)) {
|
||||||
continue; // we dont need to recreate this one
|
continue; // we dont need to recreate this one
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* instance = this->mComponent->createWithInitialProperties(
|
auto variantMap = QVariantMap();
|
||||||
variant,
|
variantMap.insert("modelData", variant);
|
||||||
QQmlEngine::contextForObject(this->mComponent)
|
|
||||||
|
auto* instance = this->mDelegate->createWithInitialProperties(
|
||||||
|
variantMap,
|
||||||
|
QQmlEngine::contextForObject(this->mDelegate)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (instance == nullptr) {
|
if (instance == nullptr) {
|
||||||
qWarning() << this->mComponent->errorString().toStdString().c_str();
|
qWarning() << this->mDelegate->errorString().toStdString().c_str();
|
||||||
qWarning() << "failed to create variant with object" << variant;
|
qWarning() << "failed to create variant with object" << variant;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
#include <qqmlcomponent.h>
|
#include <qqmlcomponent.h>
|
||||||
#include <qqmlparserstatus.h>
|
#include <qqmlparserstatus.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
|
#include <qvariant.h>
|
||||||
|
|
||||||
|
#include "doc.hpp"
|
||||||
#include "reload.hpp"
|
#include "reload.hpp"
|
||||||
|
|
||||||
// extremely inefficient map
|
// extremely inefficient map
|
||||||
|
@ -22,39 +24,54 @@ public:
|
||||||
QList<QPair<K, V>> values;
|
QList<QPair<K, V>> values;
|
||||||
};
|
};
|
||||||
|
|
||||||
///! Creates instances of a component based on a given set of variants.
|
///! Creates instances of a component based on a given model.
|
||||||
/// Creates and destroys instances of the given component when the given property changes.
|
/// Creates and destroys instances of the given component when the given property changes.
|
||||||
///
|
///
|
||||||
|
/// `Variants` is similar to [Repeater] except it is for *non Item* objects, and acts as
|
||||||
|
/// a reload scope.
|
||||||
|
///
|
||||||
|
/// Each non duplicate value passed to [model](#prop.model) will create a new instance of
|
||||||
|
/// [delegate](#prop.delegate) with its `modelData` property set to that value.
|
||||||
|
///
|
||||||
/// See [Quickshell.screens] for an example of using `Variants` to create copies of a window per
|
/// See [Quickshell.screens] for an example of using `Variants` to create copies of a window per
|
||||||
/// screen.
|
/// screen.
|
||||||
///
|
///
|
||||||
/// > [!WARNING] BUG: Variants currently fails to reload children if the variant set is changed as
|
/// > [!WARNING] BUG: Variants currently fails to reload children if the variant set is changed as
|
||||||
/// > it is instantiated. (usually due to a mutation during variant creation)
|
/// > it is instantiated. (usually due to a mutation during variant creation)
|
||||||
///
|
///
|
||||||
|
/// [Repeater]: https://doc.qt.io/qt-6/qml-qtquick-repeater.html
|
||||||
/// [Quickshell.screens]: ../quickshell#prop.screens
|
/// [Quickshell.screens]: ../quickshell#prop.screens
|
||||||
class Variants: public Reloadable {
|
class Variants: public Reloadable {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
/// The component to create instances of
|
/// The component to create instances of.
|
||||||
Q_PROPERTY(QQmlComponent* component MEMBER mComponent);
|
///
|
||||||
|
/// The delegate should define a `modelData` property that will be popuplated with a value
|
||||||
|
/// from the [model](#prop.model).
|
||||||
|
Q_PROPERTY(QQmlComponent* delegate MEMBER mDelegate);
|
||||||
/// The list of sets of properties to create instances with.
|
/// The list of sets of properties to create instances with.
|
||||||
/// Each set creates an instance of the component, which are updated when the input sets update.
|
/// Each set creates an instance of the component, which are updated when the input sets update.
|
||||||
Q_PROPERTY(QList<QVariant> variants MEMBER mVariants WRITE setVariants);
|
QSDOC_PROPERTY_OVERRIDE(QList<QVariant> model READ model WRITE setModel NOTIFY modelChanged);
|
||||||
Q_CLASSINFO("DefaultProperty", "component");
|
QSDOC_HIDE Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged);
|
||||||
|
Q_CLASSINFO("DefaultProperty", "delegate");
|
||||||
QML_ELEMENT;
|
QML_ELEMENT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Variants(QObject* parent = nullptr): Reloadable(parent) {}
|
explicit Variants(QObject* parent = nullptr): Reloadable(parent) {}
|
||||||
|
|
||||||
void onReload(QObject* oldInstance) override;
|
void onReload(QObject* oldInstance) override;
|
||||||
|
|
||||||
void componentComplete() override;
|
void componentComplete() override;
|
||||||
|
|
||||||
|
[[nodiscard]] QVariant model() const;
|
||||||
|
void setModel(const QVariant& model);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void modelChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setVariants(QVariantList variants);
|
|
||||||
void updateVariants();
|
void updateVariants();
|
||||||
|
|
||||||
QQmlComponent* mComponent = nullptr;
|
QQmlComponent* mDelegate = nullptr;
|
||||||
QVariantList mVariants;
|
QVariantList mModel;
|
||||||
AwfulMap<QVariantMap, QObject*> instances;
|
AwfulMap<QVariant, QObject*> instances;
|
||||||
bool loaded = false;
|
bool loaded = false;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue