qt: add enum editor

This commit is contained in:
Jussi Kuokkanen 2020-06-03 23:24:07 +03:00
parent 38217e1a26
commit afaaa48e9d
5 changed files with 258 additions and 75 deletions

View File

@ -1,15 +1,21 @@
#include "DeviceModel.hpp"
#include "AssignableItem.hpp"
#include "AssignableProxy.hpp"
#include "DynamicReadableProxy.hpp"
#include <fplus/fplus.hpp>
#include <QDebug>
#include <QVariantAnimation>
// 'match' is a method in QAbstractItemModel :(
namespace p = mpark::patterns;
using namespace fplus;
using namespace mpark::patterns;
using namespace TuxClocker::Device;
Q_DECLARE_METATYPE(AssignableItemData)
Q_DECLARE_METATYPE(TCDBus::Enumeration)
Q_DECLARE_METATYPE(TCDBus::Range)
Q_DECLARE_METATYPE(EnumerationVec)
DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent) :
QStandardItemModel(parent) {
@ -36,75 +42,21 @@ DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent)
nameItem->setText(nodeName);
rowItems.append(nameItem);
// Add assignable data if this is one
if (node.value().interface == "org.tuxclocker.Assignable") {
QDBusInterface ifaceNode("org.tuxclocker", node.value().path,
"org.tuxclocker.Assignable", conn);
// Should never fail
auto a_info =
qvariant_cast<QDBusVariant>(ifaceNode.property("assignableInfo"))
.variant();
/* TODO: bad hack: this code can only differentiate between
arrays and structs */
auto d_arg = qvariant_cast<QDBusArgument>(a_info);
switch (d_arg.currentType()) {
case QDBusArgument::StructureType: {
auto ifaceItem = new AssignableItem;
ifaceItem->setEditable(true);
//ifaceItem->setCheckable(true);
auto proxy = new AssignableProxy(node.value().path, conn, this);
connect(ifaceItem, &AssignableItem::assignableDataChanged,
[=](QVariant v) {
proxy->setValue(v);
ifaceItem->setData(unappliedColor(), Qt::BackgroundRole);
});
connect(proxy, &AssignableProxy::applied, [=](auto err) {
// Fade out result color
auto startColor = (err.has_value()) ? errorColor()
: successColor();
auto anim = new QVariantAnimation;
anim->setDuration(fadeOutTime());
anim->setStartValue(startColor);
anim->setEndValue(QPalette().color(QPalette::Base));
connect(anim, &QVariantAnimation::valueChanged, [=](QVariant v) {
QVariant iv;
iv.setValue(v.value<QColor>());
ifaceItem->setData(iv, Qt::BackgroundRole);
});
anim->start(QAbstractAnimation::DeleteWhenStopped);
});
connect(this, &DeviceModel::changesApplied, [=] {
proxy->apply();
});
TCDBus::Range r;
d_arg >> r;
QVariant v;
AssignableItemData data(r.toAssignableInfo());
v.setValue(data);
ifaceItem->setData(v, Role::AssignableRole);
//ifaceItem->setText(r.min.variant().toString());
rowItems.append(ifaceItem);
break;
}
case QDBusArgument::ArrayType: {
auto ifaceItem = new QStandardItem;
QVector<TCDBus::Enumeration> e;
d_arg >> e;
QVariant v;
v.setValue(e);
ifaceItem->setData(v, Role::AssignableRole);
ifaceItem->setText(e.first().name);
rowItems.append(ifaceItem);
break;
}
default:
break;
}
}
p::match(node.value().interface) (
pattern("org.tuxclocker.Assignable") = [=, &rowItems]{
if_let(pattern(some(arg)) = setupAssignable(node, conn))
= [&](auto item) {
rowItems.append(item);
};
},
pattern("org.tuxclocker.DynamicReadable") = [=, &rowItems] {
if_let(pattern(some(arg)) = setupDynReadable(node, conn))
= [&](auto item) {
rowItems.append(item);
};
},
pattern(_) = []{}
);
item->appendRow(rowItems);
for (auto c_node : node.children())
@ -115,3 +67,142 @@ DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent)
for (auto &node : root.children())
traverse(node, rootItem);
}
EnumerationVec toEnumVec(QVector<TCDBus::Enumeration> enums) {
return transform([](auto e) {
return Enumeration{e.name.toStdString(), e.key};
}, enums.toStdVector());
}
void DeviceModel::connectAssignable(TC::TreeNode<TCDBus::DeviceNode> node,
QDBusConnection conn, AssignableItem *ifaceItem) {
auto proxy = new AssignableProxy(node.value().path, conn, this);
connect(ifaceItem, &AssignableItem::assignableDataChanged,
[=](QVariant v) {
proxy->setValue(v);
ifaceItem->setData(unappliedColor(), Qt::BackgroundRole);
});
connect(proxy, &AssignableProxy::applied, [=](auto err) {
// Fade out result color
auto startColor = (err.has_value()) ? errorColor()
: successColor();
auto anim = new QVariantAnimation;
anim->setDuration(fadeOutTime());
anim->setStartValue(startColor);
anim->setEndValue(QPalette().color(QPalette::Base));
connect(anim, &QVariantAnimation::valueChanged, [=](QVariant v) {
QVariant iv;
iv.setValue(v.value<QColor>());
ifaceItem->setData(iv, Qt::BackgroundRole);
});
anim->start(QAbstractAnimation::DeleteWhenStopped);
});
connect(this, &DeviceModel::changesApplied, [=] {
proxy->apply();
});
}
std::optional<QStandardItem*> DeviceModel::setupAssignable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
QDBusInterface ifaceNode("org.tuxclocker", node.value().path,
"org.tuxclocker.Assignable", conn);
// Should never fail
auto a_info =
qvariant_cast<QDBusVariant>(ifaceNode.property("assignableInfo"))
.variant();
/* TODO: bad hack: this code can only differentiate between
arrays and structs: make it based on signature instead */
auto d_arg = qvariant_cast<QDBusArgument>(a_info);
switch (d_arg.currentType()) {
case QDBusArgument::StructureType: {
auto ifaceItem = new AssignableItem;
ifaceItem->setEditable(true);
//ifaceItem->setCheckable(true);
/*auto proxy = new AssignableProxy(node.value().path, conn, this);
connect(ifaceItem, &AssignableItem::assignableDataChanged,
[=](QVariant v) {
proxy->setValue(v);
ifaceItem->setData(unappliedColor(), Qt::BackgroundRole);
});
connect(proxy, &AssignableProxy::applied, [=](auto err) {
// Fade out result color
auto startColor = (err.has_value()) ? errorColor()
: successColor();
auto anim = new QVariantAnimation;
anim->setDuration(fadeOutTime());
anim->setStartValue(startColor);
anim->setEndValue(QPalette().color(QPalette::Base));
connect(anim, &QVariantAnimation::valueChanged, [=](QVariant v) {
QVariant iv;
iv.setValue(v.value<QColor>());
ifaceItem->setData(iv, Qt::BackgroundRole);
});
anim->start(QAbstractAnimation::DeleteWhenStopped);
});
connect(this, &DeviceModel::changesApplied, [=] {
proxy->apply();
});*/
connectAssignable(node, conn, ifaceItem);
TCDBus::Range r;
d_arg >> r;
QVariant v;
AssignableItemData data(r.toAssignableInfo());
v.setValue(data);
ifaceItem->setData(v, Role::AssignableRole);
return ifaceItem;
}
case QDBusArgument::ArrayType: {
auto ifaceItem = new AssignableItem;
connectAssignable(node, conn, ifaceItem);
QVector<TCDBus::Enumeration> e;
// TODO: convert to EnumerationVec
d_arg >> e;
QVariant v;
AssignableItemData data(toEnumVec(e));
v.setValue(data);
ifaceItem->setData(v, Role::AssignableRole);
ifaceItem->setText(e.first().name);
return ifaceItem;
}
default:
return std::nullopt;
}
}
std::optional<QStandardItem*> DeviceModel::setupDynReadable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
auto item = new QStandardItem;
auto proxy = new DynamicReadableProxy(node.value().path, conn, this);
connect(proxy, &DynamicReadableProxy::valueChanged, [=](ReadResult res) {
p::match(res)(
pattern(as<ReadableValue>(arg)) = [=](auto rv) {
p::match(rv)(
pattern(as<double>(arg)) = [=](auto d) {
item->setText(QString::number(d));
},
pattern(as<int>(arg)) = [=](auto i) {
item->setText(QString::number(i));
},
pattern(as<uint>(arg)) = [=](auto u) {
item->setText(QString::number(u));
},
pattern(_) = []{}
);
},
pattern(_) = []{}
);
});
return item;
}

View File

@ -12,10 +12,11 @@
#include <QStandardItemModel>
#include <QPalette>
namespace p = mpark::patterns;
namespace TC = TuxClocker;
namespace TCDBus = TuxClocker::DBus;
// Why the fuck do I have to forward declare this?
class AssignableItem;
class DeviceModel : public QStandardItemModel {
public:
@ -40,8 +41,15 @@ signals:
private:
Q_OBJECT
constexpr int fadeOutTime() {return 5000;}
constexpr int transparency() {return 120;}
// Separate handling interfaces since otherwise we run out of columns
void connectAssignable(TC::TreeNode<TCDBus::DeviceNode> node,
QDBusConnection conn, AssignableItem *ifaceItem);
std::optional<QStandardItem*> setupAssignable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn);
std::optional<QStandardItem*> setupDynReadable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn);
constexpr int fadeOutTime() {return 5000;} // milliseconds
constexpr int transparency() {return 120;} // 0-255
// Colors for items
QColor errorColor() {return QColor(255, 0, 0, transparency());} // red
QColor unappliedColor() {return QColor(255, 255, 0, transparency());} // yellow

View File

@ -2,6 +2,7 @@
#include "DeviceModel.hpp"
#include <DoubleRangeEditor.hpp>
#include <EnumEditor.hpp>
#include <IntRangeEditor.hpp>
#include <patterns.hpp>
@ -26,8 +27,13 @@ QWidget *DeviceModelDelegate::createEditor(QWidget *parent,
},
pattern(as<Range<double>>(arg)) = [&](auto dr) {
editor = new DoubleRangeEditor(dr, parent);
});
});
}
);
},
pattern(as<EnumerationVec>(arg)) = [&](auto ev) {
editor = new EnumEditor(ev, parent);
}
);
}
return editor;
}

View File

@ -0,0 +1,59 @@
#include "EnumEditor.hpp"
#include <fplus/fplus.hpp>
#include <QDebug>
#include <QHBoxLayout>
#include <QStandardItem>
#include <QVariant>
#include <QVector>
using namespace fplus;
constexpr int KeyRole = Qt::UserRole + 1; // Stores the associated key (uint)
EnumEditor::EnumEditor(QWidget *parent) : AbstractAssignableEditor(parent) {
setAutoFillBackground(true);
auto layout = new QHBoxLayout(this);
layout->setMargin(0);
m_comboBox = new QComboBox;
layout->addWidget(m_comboBox);
}
EnumEditor::EnumEditor(TuxClocker::Device::EnumerationVec enums, QWidget *parent)
: EnumEditor(parent) {
auto qStrings = QVector<QString>::fromStdVector(transform([](auto e) {
return QString::fromStdString(e.name);
}, enums));
for (uint i = 0; i < qStrings.length(); i++) {
auto item = new QStandardItem;
item->setText(qStrings[i]);
item->setData(i, KeyRole);
m_model.appendRow(item);
}
m_model.sort(0);
m_comboBox->setModel(&m_model);
}
QVariant EnumEditor::assignableData() {
auto r = m_model.index(m_comboBox->currentIndex(), 0)
.data(KeyRole).toUInt();
return r;
}
QString EnumEditor::displayData() {
auto r = m_model.index(m_comboBox->currentIndex(), 0)
.data(Qt::DisplayRole).toString();
return r;
}
void EnumEditor::setAssignableData(QVariant data) {
// TODO: make worst case better than O(n)
auto u = data.toUInt();
for (int i = 0; i < m_comboBox->model()->rowCount(); i++) {
if (m_comboBox->itemData(i, KeyRole).toUInt() == u) {
m_comboBox->setCurrentIndex(i);
break;
}
}
}

View File

@ -0,0 +1,19 @@
#pragma once
#include "AbstractAssignableEditor.hpp"
#include <Device.hpp>
#include <QComboBox>
#include <QStandardItemModel>
class EnumEditor : public AbstractAssignableEditor {
public:
EnumEditor(QWidget *parent = nullptr);
EnumEditor(TuxClocker::Device::EnumerationVec enums, QWidget *parent = nullptr);
virtual QVariant assignableData() override;
virtual QString displayData() override;
virtual void setAssignableData(QVariant data) override;
private:
QComboBox *m_comboBox;
QStandardItemModel m_model;
};