mirror of
https://github.com/Lurkki14/tuxclocker.git
synced 2025-02-25 18:55:24 -06:00
qt: add applying for range-based assignables
This commit is contained in:
parent
a2ab1b99b6
commit
b3a8dddc0d
@ -1,4 +1,9 @@
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
#include <DBusTypes.hpp>
|
||||
#include <DeviceBrowser.hpp>
|
||||
#include <DeviceModel.hpp>
|
||||
#include <DeviceModelDelegate.hpp>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusMetaType>
|
||||
@ -10,10 +15,6 @@
|
||||
#include <QVector>
|
||||
#include <Tree.hpp>
|
||||
|
||||
#include <DeviceModel.hpp>
|
||||
#include <DeviceModelDelegate.hpp>
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
namespace TCDBus = TuxClocker::DBus;
|
||||
|
||||
using namespace TuxClocker;
|
||||
@ -48,9 +49,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
|
||||
|
||||
auto root = flatTree.toTree(flatTree);
|
||||
|
||||
auto view = new QTreeView;
|
||||
/*auto view = new QTreeView;
|
||||
view->setItemDelegate(new DeviceModelDelegate);
|
||||
auto model = new DeviceModel(root);
|
||||
view->setModel(model);
|
||||
setCentralWidget(view);
|
||||
setCentralWidget(view);*/
|
||||
|
||||
auto model = new DeviceModel(root);
|
||||
auto browser = new DeviceBrowser(*model);
|
||||
setCentralWidget(browser);
|
||||
}
|
||||
|
16
src/tuxclocker-qt/data/AssignableItem.cpp
Normal file
16
src/tuxclocker-qt/data/AssignableItem.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "AssignableItem.hpp"
|
||||
|
||||
Q_DECLARE_METATYPE(AssignableItemData)
|
||||
|
||||
void AssignableItem::setData(const QVariant &v, int role) {
|
||||
if (role == DeviceModel::AssignableRole) {
|
||||
auto data = v.value<AssignableItemData>().value();
|
||||
// Value is empty
|
||||
if (data.isValid()) {
|
||||
QVariant vr;
|
||||
vr.setValue(data);
|
||||
emit assignableDataChanged(vr);
|
||||
}
|
||||
}
|
||||
QStandardItem::setData(v, role);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
//#include "DeviceModel.hpp"
|
||||
#include "DeviceModel.hpp"
|
||||
|
||||
#include <QObject>
|
||||
#include <QStandardItem>
|
||||
@ -12,11 +12,14 @@ class AssignableItem : public QObject, public QStandardItem {
|
||||
public:
|
||||
AssignableItem(QObject *parent = nullptr) : QObject(parent), QStandardItem() {
|
||||
}
|
||||
void setData(const QVariant &v, int role = Qt::UserRole + 1) {
|
||||
//if (role == DeviceModel::AssignableRole) ;
|
||||
}
|
||||
bool committal() {return m_committed;}
|
||||
// Whether or not the set value shall be applied. Doesn't reset or change it.
|
||||
void setCommittal(bool on) {m_committed = on;}
|
||||
void setData(const QVariant &v, int role = Qt::UserRole + 1);
|
||||
signals:
|
||||
void assignableDataChanged(QVariant value);
|
||||
private:
|
||||
Q_OBJECT
|
||||
|
||||
bool m_committed = false;
|
||||
};
|
||||
|
@ -12,6 +12,9 @@ public:
|
||||
m_enabled = false;
|
||||
}
|
||||
TC::Device::AssignableInfo assignableInfo() {return m_info;}
|
||||
bool committal() {return m_enabled;}
|
||||
// Whether or not the set value shall be applied. Doesn't reset or change it.
|
||||
void setCommittal(bool on) {m_enabled = on;}
|
||||
void setValue(QVariant v) {m_targetValue = v;}
|
||||
QVariant value() {return m_targetValue;}
|
||||
private:
|
||||
@ -19,4 +22,3 @@ private:
|
||||
TC::Device::AssignableInfo m_info;
|
||||
QVariant m_targetValue;
|
||||
};
|
||||
Q_DECLARE_METATYPE(TC::Device::AssignableInfo)
|
||||
|
43
src/tuxclocker-qt/data/AssignableProxy.cpp
Normal file
43
src/tuxclocker-qt/data/AssignableProxy.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include "AssignableProxy.hpp"
|
||||
|
||||
#include <DBusTypes.hpp>
|
||||
#include <QDBusReply>
|
||||
#include <QDBusMessage>
|
||||
|
||||
namespace TCD = TuxClocker::DBus;
|
||||
|
||||
using namespace TuxClocker::Device;
|
||||
|
||||
Q_DECLARE_METATYPE(TCD::Result<int>)
|
||||
|
||||
AssignableProxy::AssignableProxy(QString path, QDBusConnection conn,
|
||||
QObject *parent) : QObject(parent) {
|
||||
qDBusRegisterMetaType<TCD::Result<int>>();
|
||||
m_iface = new QDBusInterface("org.tuxclocker",
|
||||
path, "org.tuxclocker.Assignable", conn, this);
|
||||
}
|
||||
|
||||
void AssignableProxy::apply() {
|
||||
// A value hasn't been set yet
|
||||
if (m_value.isNull())
|
||||
return;
|
||||
|
||||
// Use QDBusVariant since otherwise tries to call with the wrong signature
|
||||
QDBusVariant dv(m_value);
|
||||
QVariant v;
|
||||
v.setValue(dv);
|
||||
QDBusReply<TCD::Result<int>> reply = m_iface->call("assign", v);
|
||||
if (reply.isValid()) {
|
||||
qDebug() << reply.value().error << reply.value().value;
|
||||
|
||||
TCD::Result<AssignmentError> ar{reply.value().error,
|
||||
static_cast<AssignmentError>(reply.value().value)};
|
||||
qDebug("Success!");
|
||||
emit applied(ar.toOptional());
|
||||
}
|
||||
// TODO: Maybe indicate that calling dbus errored out
|
||||
else
|
||||
emit applied(AssignmentError::UnknownError);
|
||||
// Indicate that there's no pending value to applied by making value invalid
|
||||
m_value = QVariant();
|
||||
}
|
28
src/tuxclocker-qt/data/AssignableProxy.hpp
Normal file
28
src/tuxclocker-qt/data/AssignableProxy.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
// Acts as a buffer for unapplied assignables and applies them asynchronously
|
||||
|
||||
#include <Device.hpp>
|
||||
#include <memory>
|
||||
#include <QDebug>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusInterface>
|
||||
#include <QObject>
|
||||
#include <variant>
|
||||
|
||||
namespace TC = TuxClocker;
|
||||
|
||||
class AssignableProxy : public QObject {
|
||||
public:
|
||||
AssignableProxy(QString path, QDBusConnection conn,
|
||||
QObject *parent = nullptr);
|
||||
void apply();
|
||||
void setValue(QVariant v) {m_value = v;}
|
||||
signals:
|
||||
void applied(std::optional<TC::Device::AssignmentError>);
|
||||
private:
|
||||
Q_OBJECT
|
||||
|
||||
QVariant m_value;
|
||||
QDBusInterface *m_iface;
|
||||
};
|
@ -1,5 +1,12 @@
|
||||
#include "DeviceModel.hpp"
|
||||
|
||||
#include "AssignableItem.hpp"
|
||||
#include "AssignableProxy.hpp"
|
||||
#include <QDebug>
|
||||
#include <QVariantAnimation>
|
||||
|
||||
using namespace mpark::patterns;
|
||||
|
||||
Q_DECLARE_METATYPE(AssignableItemData)
|
||||
Q_DECLARE_METATYPE(TCDBus::Enumeration)
|
||||
Q_DECLARE_METATYPE(TCDBus::Range)
|
||||
@ -16,10 +23,12 @@ DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent)
|
||||
|
||||
setColumnCount(2);
|
||||
|
||||
std::function<void(TC::TreeNode<TCDBus::DeviceNode> node, QStandardItem*)> traverse;
|
||||
traverse = [&traverse](auto node, auto item) {
|
||||
std::function<void(TC::TreeNode<TCDBus::DeviceNode> node,
|
||||
QStandardItem*)> traverse;
|
||||
traverse = [&traverse, this](auto node, auto item) {
|
||||
auto conn = QDBusConnection::systemBus();
|
||||
QDBusInterface nodeIface("org.tuxclocker", node.value().path, "org.tuxclocker.Node", conn);
|
||||
QDBusInterface nodeIface("org.tuxclocker",
|
||||
node.value().path,"org.tuxclocker.Node", conn);
|
||||
auto nodeName = nodeIface.property("name").toString();
|
||||
|
||||
QList<QStandardItem*> rowItems;
|
||||
@ -40,14 +49,44 @@ DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent)
|
||||
auto d_arg = qvariant_cast<QDBusArgument>(a_info);
|
||||
switch (d_arg.currentType()) {
|
||||
case QDBusArgument::StructureType: {
|
||||
auto ifaceItem = new QStandardItem;
|
||||
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());
|
||||
//ifaceItem->setText(r.min.variant().toString());
|
||||
rowItems.append(ifaceItem);
|
||||
break;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
//#include "AssignableItem.hpp"
|
||||
#include "AssignableItem.hpp"
|
||||
#include "AssignableItemData.hpp"
|
||||
|
||||
#include <DBusTypes.hpp>
|
||||
@ -10,6 +10,7 @@
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusInterface>
|
||||
#include <QStandardItemModel>
|
||||
#include <QPalette>
|
||||
|
||||
namespace p = mpark::patterns;
|
||||
namespace TC = TuxClocker;
|
||||
@ -32,4 +33,17 @@ public:
|
||||
enum class FilterFlag {
|
||||
|
||||
};
|
||||
// For decoupling AssignableItems created in the model
|
||||
void applyChanges() {emit changesApplied();}
|
||||
signals:
|
||||
void changesApplied();
|
||||
private:
|
||||
Q_OBJECT
|
||||
|
||||
constexpr int fadeOutTime() {return 5000;}
|
||||
constexpr int transparency() {return 120;}
|
||||
// Colors for items
|
||||
QColor errorColor() {return QColor(255, 0, 0, transparency());} // red
|
||||
QColor unappliedColor() {return QColor(255, 255, 0, transparency());} // yellow
|
||||
QColor successColor() {return QColor(0, 255, 0, transparency());} // green
|
||||
};
|
||||
|
@ -56,3 +56,15 @@ void DeviceModelDelegate::setModelData(QWidget *editor,
|
||||
model->setData(index, v, DeviceModel::AssignableRole);
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceModelDelegate::updateEditorGeometry(QWidget *editor,
|
||||
const QStyleOptionViewItem &option, const QModelIndex&) const {
|
||||
// Why do I need to override this to perform such a basic task?
|
||||
editor->setGeometry(option.rect);
|
||||
}
|
||||
|
||||
/*void DeviceModelDelegate::drawCheck(QPainter *painter,
|
||||
const QStyleOptionViewItem &option, const QRect &rect,
|
||||
Qt::CheckState state) const {
|
||||
QStyledItemDelegate::drawCheck(painter, option, rect, state)
|
||||
}*/
|
||||
|
@ -2,13 +2,14 @@
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
// TODO: align checkbox to the right
|
||||
class DeviceModelDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
DeviceModelDelegate(QObject *parent = nullptr);
|
||||
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const;
|
||||
//void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
|
||||
// const QModelIndex &index) const;
|
||||
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const;
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const;
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model,
|
||||
const QModelIndex &index) const;
|
||||
|
@ -2,12 +2,17 @@ qt5 = import('qt5')
|
||||
qt5_dep = dependency('qt5', modules: ['DBus', 'Charts', 'Widgets'])
|
||||
|
||||
moc_files = qt5.preprocess(moc_headers: ['MainWindow.hpp',
|
||||
'data/AssignableItem.hpp'],
|
||||
'data/AssignableItem.hpp',
|
||||
'data/AssignableProxy.hpp',
|
||||
'data/DeviceModel.hpp'],
|
||||
dependencies: qt5_dep)
|
||||
|
||||
sources = ['main.cpp',
|
||||
'data/AssignableItem.cpp',
|
||||
'data/AssignableProxy.cpp',
|
||||
'data/DeviceModel.cpp',
|
||||
'data/DeviceModelDelegate.cpp',
|
||||
'widgets/DeviceTreeView.cpp',
|
||||
'MainWindow.cpp']
|
||||
|
||||
local_incdir = include_directories(['data',
|
||||
@ -17,6 +22,7 @@ local_incdir = include_directories(['data',
|
||||
executable('tuxclocker-qt',
|
||||
moc_files,
|
||||
sources,
|
||||
override_options : ['cpp_std=c++17'],
|
||||
dependencies: qt5_dep,
|
||||
include_directories: [incdir, local_incdir],
|
||||
install: true)
|
||||
|
32
src/tuxclocker-qt/widgets/DeviceBrowser.hpp
Normal file
32
src/tuxclocker-qt/widgets/DeviceBrowser.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <DeviceTreeView.hpp>
|
||||
#include <DeviceModel.hpp>
|
||||
#include <QGridLayout>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
|
||||
// Class for viewing/editing the main tuxclocker tree
|
||||
class DeviceBrowser : public QWidget {
|
||||
public:
|
||||
DeviceBrowser(DeviceModel &model,
|
||||
QWidget *parent = nullptr) : QWidget(parent), m_deviceModel(model) {
|
||||
m_layout = new QGridLayout(this);
|
||||
m_treeView = new DeviceTreeView(model);
|
||||
m_apply = new QPushButton("Apply changes");
|
||||
m_apply->setEnabled(true);
|
||||
|
||||
connect(m_apply, &QPushButton::pressed, &m_deviceModel, &DeviceModel::applyChanges);
|
||||
|
||||
m_layout->addWidget(m_treeView, 0, 0);
|
||||
m_layout->addWidget(m_apply, 1, 0);
|
||||
|
||||
setLayout(m_layout);
|
||||
}
|
||||
|
||||
private:
|
||||
DeviceModel &m_deviceModel;
|
||||
DeviceTreeView *m_treeView;
|
||||
QPushButton *m_apply;
|
||||
QGridLayout *m_layout;
|
||||
};
|
39
src/tuxclocker-qt/widgets/DeviceTreeView.cpp
Normal file
39
src/tuxclocker-qt/widgets/DeviceTreeView.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include "DeviceTreeView.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDebug>
|
||||
|
||||
Q_DECLARE_METATYPE(AssignableItemData)
|
||||
|
||||
DeviceTreeView::DeviceTreeView(DeviceModel &model, QWidget *parent)
|
||||
: QTreeView(parent), m_deviceModel(model) {
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, &QTreeView::customContextMenuRequested, [this](QPoint point) {
|
||||
auto index = indexAt(point);
|
||||
auto data = index.data(DeviceModel::AssignableRole);
|
||||
QMenu menu;
|
||||
if (data.canConvert<AssignableItemData>()) {
|
||||
auto a_data = data.value<AssignableItemData>();
|
||||
QCheckBox commitCb("Commit");
|
||||
auto commitAct = new QWidgetAction(&menu);
|
||||
commitAct->setDefaultWidget(&commitCb);
|
||||
commitCb.setChecked(a_data.committal());
|
||||
connect(&commitCb, &QCheckBox::toggled, [&](bool toggled) {
|
||||
a_data.setCommittal(toggled);
|
||||
});
|
||||
// Write the committal value to the model only on menu close
|
||||
connect(&menu, &QMenu::aboutToHide, [&] {
|
||||
qDebug() << a_data.committal();
|
||||
QVariant v;
|
||||
v.setValue(a_data);
|
||||
m_deviceModel.setData(index, v, DeviceModel::AssignableRole);
|
||||
});
|
||||
menu.addAction(commitAct);
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
});
|
||||
m_delegate = new DeviceModelDelegate(this);
|
||||
|
||||
setItemDelegate(m_delegate);
|
||||
setModel(&m_deviceModel);
|
||||
}
|
19
src/tuxclocker-qt/widgets/DeviceTreeView.hpp
Normal file
19
src/tuxclocker-qt/widgets/DeviceTreeView.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <AssignableItemData.hpp>
|
||||
#include <DeviceModel.hpp>
|
||||
#include <DeviceModelDelegate.hpp>
|
||||
#include <QMenu>
|
||||
#include <QTreeView>
|
||||
#include <QWidgetAction>
|
||||
|
||||
// Class for handling menus on DeviceModel
|
||||
class DeviceTreeView : public QTreeView {
|
||||
public:
|
||||
DeviceTreeView(DeviceModel &model, QWidget *parent = nullptr);
|
||||
// Accessor method for connecting everything in the browser
|
||||
const DeviceModel &deviceModel() {return m_deviceModel;}
|
||||
private:
|
||||
DeviceModel &m_deviceModel;
|
||||
DeviceModelDelegate *m_delegate;
|
||||
};
|
Loading…
Reference in New Issue
Block a user