mirror of
https://github.com/Lurkki14/tuxclocker.git
synced 2025-02-25 18:55:24 -06:00
qt: add enum editor
This commit is contained in:
parent
38217e1a26
commit
afaaa48e9d
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
59
src/tuxclocker-qt/widgets/EnumEditor.cpp
Normal file
59
src/tuxclocker-qt/widgets/EnumEditor.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
19
src/tuxclocker-qt/widgets/EnumEditor.hpp
Normal file
19
src/tuxclocker-qt/widgets/EnumEditor.hpp
Normal 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;
|
||||
};
|
Loading…
Reference in New Issue
Block a user