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 "DeviceModel.hpp"
|
||||||
|
|
||||||
#include "AssignableItem.hpp"
|
|
||||||
#include "AssignableProxy.hpp"
|
#include "AssignableProxy.hpp"
|
||||||
|
#include "DynamicReadableProxy.hpp"
|
||||||
|
#include <fplus/fplus.hpp>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QVariantAnimation>
|
#include <QVariantAnimation>
|
||||||
|
|
||||||
|
// 'match' is a method in QAbstractItemModel :(
|
||||||
|
namespace p = mpark::patterns;
|
||||||
|
using namespace fplus;
|
||||||
using namespace mpark::patterns;
|
using namespace mpark::patterns;
|
||||||
|
using namespace TuxClocker::Device;
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(AssignableItemData)
|
Q_DECLARE_METATYPE(AssignableItemData)
|
||||||
Q_DECLARE_METATYPE(TCDBus::Enumeration)
|
Q_DECLARE_METATYPE(TCDBus::Enumeration)
|
||||||
Q_DECLARE_METATYPE(TCDBus::Range)
|
Q_DECLARE_METATYPE(TCDBus::Range)
|
||||||
|
Q_DECLARE_METATYPE(EnumerationVec)
|
||||||
|
|
||||||
DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent) :
|
DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent) :
|
||||||
QStandardItemModel(parent) {
|
QStandardItemModel(parent) {
|
||||||
@ -36,75 +42,21 @@ DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent)
|
|||||||
nameItem->setText(nodeName);
|
nameItem->setText(nodeName);
|
||||||
rowItems.append(nameItem);
|
rowItems.append(nameItem);
|
||||||
|
|
||||||
// Add assignable data if this is one
|
p::match(node.value().interface) (
|
||||||
if (node.value().interface == "org.tuxclocker.Assignable") {
|
pattern("org.tuxclocker.Assignable") = [=, &rowItems]{
|
||||||
QDBusInterface ifaceNode("org.tuxclocker", node.value().path,
|
if_let(pattern(some(arg)) = setupAssignable(node, conn))
|
||||||
"org.tuxclocker.Assignable", conn);
|
= [&](auto item) {
|
||||||
// Should never fail
|
rowItems.append(item);
|
||||||
auto a_info =
|
};
|
||||||
qvariant_cast<QDBusVariant>(ifaceNode.property("assignableInfo"))
|
},
|
||||||
.variant();
|
pattern("org.tuxclocker.DynamicReadable") = [=, &rowItems] {
|
||||||
/* TODO: bad hack: this code can only differentiate between
|
if_let(pattern(some(arg)) = setupDynReadable(node, conn))
|
||||||
arrays and structs */
|
= [&](auto item) {
|
||||||
auto d_arg = qvariant_cast<QDBusArgument>(a_info);
|
rowItems.append(item);
|
||||||
switch (d_arg.currentType()) {
|
};
|
||||||
case QDBusArgument::StructureType: {
|
},
|
||||||
auto ifaceItem = new AssignableItem;
|
pattern(_) = []{}
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
item->appendRow(rowItems);
|
item->appendRow(rowItems);
|
||||||
|
|
||||||
for (auto c_node : node.children())
|
for (auto c_node : node.children())
|
||||||
@ -115,3 +67,142 @@ DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent)
|
|||||||
for (auto &node : root.children())
|
for (auto &node : root.children())
|
||||||
traverse(node, rootItem);
|
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 <QStandardItemModel>
|
||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
|
|
||||||
namespace p = mpark::patterns;
|
|
||||||
namespace TC = TuxClocker;
|
namespace TC = TuxClocker;
|
||||||
namespace TCDBus = TuxClocker::DBus;
|
namespace TCDBus = TuxClocker::DBus;
|
||||||
|
|
||||||
|
// Why the fuck do I have to forward declare this?
|
||||||
|
class AssignableItem;
|
||||||
|
|
||||||
class DeviceModel : public QStandardItemModel {
|
class DeviceModel : public QStandardItemModel {
|
||||||
public:
|
public:
|
||||||
@ -40,8 +41,15 @@ signals:
|
|||||||
private:
|
private:
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
constexpr int fadeOutTime() {return 5000;}
|
// Separate handling interfaces since otherwise we run out of columns
|
||||||
constexpr int transparency() {return 120;}
|
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
|
// Colors for items
|
||||||
QColor errorColor() {return QColor(255, 0, 0, transparency());} // red
|
QColor errorColor() {return QColor(255, 0, 0, transparency());} // red
|
||||||
QColor unappliedColor() {return QColor(255, 255, 0, transparency());} // yellow
|
QColor unappliedColor() {return QColor(255, 255, 0, transparency());} // yellow
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "DeviceModel.hpp"
|
#include "DeviceModel.hpp"
|
||||||
#include <DoubleRangeEditor.hpp>
|
#include <DoubleRangeEditor.hpp>
|
||||||
|
#include <EnumEditor.hpp>
|
||||||
#include <IntRangeEditor.hpp>
|
#include <IntRangeEditor.hpp>
|
||||||
#include <patterns.hpp>
|
#include <patterns.hpp>
|
||||||
|
|
||||||
@ -26,8 +27,13 @@ QWidget *DeviceModelDelegate::createEditor(QWidget *parent,
|
|||||||
},
|
},
|
||||||
pattern(as<Range<double>>(arg)) = [&](auto dr) {
|
pattern(as<Range<double>>(arg)) = [&](auto dr) {
|
||||||
editor = new DoubleRangeEditor(dr, parent);
|
editor = new DoubleRangeEditor(dr, parent);
|
||||||
});
|
}
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
pattern(as<EnumerationVec>(arg)) = [&](auto ev) {
|
||||||
|
editor = new EnumEditor(ev, parent);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return editor;
|
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