mirror of
https://github.com/Lurkki14/tuxclocker.git
synced 2025-02-25 18:55:24 -06:00
qt: add parametrization basics
This commit is contained in:
parent
b29495629a
commit
b0c39bfaeb
6
src/tuxclocker-qt/data/AbstractAssignableConnection.hpp
Normal file
6
src/tuxclocker-qt/data/AbstractAssignableConnection.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
class AbstractAssignableConnection {
|
||||
public:
|
||||
|
||||
};
|
32
src/tuxclocker-qt/data/AssignableConnection.hpp
Normal file
32
src/tuxclocker-qt/data/AssignableConnection.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/signals2.hpp>
|
||||
#include <functional>
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
|
||||
// Interface for the function for an assignable
|
||||
class AssignableConnection : public QObject {
|
||||
public:
|
||||
AssignableConnection(QObject *parent = nullptr) : QObject(parent) {}
|
||||
/*AssignableConnection(const AssignableConnection &other) {
|
||||
targetValueChanged.connect(other.targetValueChanged);
|
||||
}*/
|
||||
virtual ~AssignableConnection() {}
|
||||
// Returns the data as QVariant for saving to settings
|
||||
virtual QVariant connectionData() = 0;
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
/* Value and text representation of desired value,
|
||||
since converting QDBusVariant to QString isn't trivial
|
||||
for complex types. */
|
||||
/*boost::signals2::signal<void(QVariant, QString)> targetValueChanged;
|
||||
boost::signals2::signal<void()> started;
|
||||
boost::signals2::signal<void()> stopped;*/
|
||||
signals:
|
||||
void targetValueChanged(QVariant, QString);
|
||||
void started();
|
||||
void stopped();
|
||||
private:
|
||||
Q_OBJECT
|
||||
};
|
@ -1,5 +1,6 @@
|
||||
#include "AssignableProxy.hpp"
|
||||
|
||||
|
||||
#include <DBusTypes.hpp>
|
||||
#include <QDBusReply>
|
||||
#include <QDBusMessage>
|
||||
@ -16,7 +17,20 @@ AssignableProxy::AssignableProxy(QString path, QDBusConnection conn,
|
||||
m_iface = new QDBusInterface("org.tuxclocker",
|
||||
path, "org.tuxclocker.Assignable", conn, this);
|
||||
}
|
||||
|
||||
|
||||
std::optional<AssignmentError> AssignableProxy::doApply(const QVariant &v) {
|
||||
//qDebug() << v;
|
||||
QDBusReply<TCD::Result<int>> reply = m_iface->call("assign", v);
|
||||
if (reply.isValid()) {
|
||||
TCD::Result<AssignmentError> ar{reply.value().error,
|
||||
static_cast<AssignmentError>(reply.value().value)};
|
||||
//qDebug("Success!");
|
||||
return ar.toOptional();
|
||||
}
|
||||
// TODO: indicate dbus error
|
||||
return AssignmentError::UnknownError;
|
||||
}
|
||||
|
||||
void AssignableProxy::apply() {
|
||||
// A value hasn't been set yet
|
||||
if (m_value.isNull())
|
||||
@ -26,18 +40,21 @@ void AssignableProxy::apply() {
|
||||
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);
|
||||
emit applied(doApply(v));
|
||||
// Indicate that there's no pending value to applied by making value invalid
|
||||
m_value = QVariant();
|
||||
}
|
||||
|
||||
void AssignableProxy::startConnection(std::shared_ptr<AssignableConnection> conn) {
|
||||
m_assignableConnection = conn;
|
||||
connect(conn.get(), &AssignableConnection::targetValueChanged, [this](auto targetValue, auto text) {
|
||||
auto err = doApply(targetValue);
|
||||
if (err.has_value())
|
||||
emit connectionValueChanged(err.value(), text);
|
||||
else
|
||||
emit connectionValueChanged(targetValue, text);
|
||||
});
|
||||
// Emit started signal in case a connection emits a new value right away
|
||||
emit connectionStarted();
|
||||
m_assignableConnection->start();
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
// Acts as a buffer for unapplied assignables and applies them asynchronously
|
||||
|
||||
#include <AssignableConnection.hpp>
|
||||
#include <Device.hpp>
|
||||
#include <memory>
|
||||
#include <QDebug>
|
||||
@ -17,12 +18,23 @@ public:
|
||||
AssignableProxy(QString path, QDBusConnection conn,
|
||||
QObject *parent = nullptr);
|
||||
void apply();
|
||||
void startConnection(std::shared_ptr<AssignableConnection> conn);
|
||||
// Stop connection and clear current connection
|
||||
void stopConnection();
|
||||
void setValue(QVariant v) {m_value = v;}
|
||||
signals:
|
||||
void applied(std::optional<TC::Device::AssignmentError>);
|
||||
void connectionValueChanged(std::variant<QVariant, TC::Device::AssignmentError>,
|
||||
QString text);
|
||||
void connectionStarted();
|
||||
void connectionStopped();
|
||||
private:
|
||||
Q_OBJECT
|
||||
|
||||
QVariant m_value;
|
||||
QDBusInterface *m_iface;
|
||||
// This is a bit of a peril but not sure if we can store interfaces any better...
|
||||
std::shared_ptr<AssignableConnection> m_assignableConnection;
|
||||
|
||||
std::optional<TC::Device::AssignmentError> doApply(const QVariant &v);
|
||||
};
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include "AssignableProxy.hpp"
|
||||
#include "DynamicReadableProxy.hpp"
|
||||
#include "qnamespace.h"
|
||||
#include "qstandarditemmodel.h"
|
||||
#include <fplus/fplus.hpp>
|
||||
#include <QApplication>
|
||||
#include <QDBusReply>
|
||||
@ -16,6 +18,8 @@ using namespace mpark::patterns;
|
||||
using namespace TuxClocker::Device;
|
||||
|
||||
Q_DECLARE_METATYPE(AssignableItemData)
|
||||
Q_DECLARE_METATYPE(AssignableProxy*)
|
||||
Q_DECLARE_METATYPE(DynamicReadableProxy*)
|
||||
Q_DECLARE_METATYPE(TCDBus::Enumeration)
|
||||
Q_DECLARE_METATYPE(TCDBus::Range)
|
||||
Q_DECLARE_METATYPE(EnumerationVec)
|
||||
@ -50,13 +54,8 @@ DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent)
|
||||
if_let(pattern(some(arg)) = setupAssignable(node, conn))
|
||||
= [&](auto item) {
|
||||
nameItem->setData(Assignable, InterfaceTypeRole);
|
||||
|
||||
//auto style = QApplication::style();
|
||||
//auto icon = style->standardIcon(QStyle::SP_ComputerIcon);
|
||||
auto icon = assignableIcon();
|
||||
//QIcon icon("/home/jussi/Downloads/wrench.png");
|
||||
nameItem->setData(icon, Qt::DecorationRole);
|
||||
|
||||
rowItems.append(item);
|
||||
};
|
||||
},
|
||||
@ -69,6 +68,7 @@ DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent)
|
||||
nameItem->setData(DeviceModel::DynamicReadable,
|
||||
InterfaceTypeRole);
|
||||
rowItems.append(item);
|
||||
//qDebug() << item->data(DynamicReadableProxyRole);
|
||||
};
|
||||
},
|
||||
pattern("org.tuxclocker.StaticReadable") = [=, &rowItems] {
|
||||
@ -100,10 +100,35 @@ EnumerationVec toEnumVec(QVector<TCDBus::Enumeration> enums) {
|
||||
}, enums.toStdVector());
|
||||
}
|
||||
|
||||
std::optional<const AssignableProxy*>
|
||||
DeviceModel::assignableProxyFromItem(QStandardItem *item) {
|
||||
return (m_assignableProxyHash.contains(item)) ?
|
||||
std::optional(m_assignableProxyHash.value(item)) :
|
||||
std::nullopt;
|
||||
}
|
||||
|
||||
QStandardItem *DeviceModel::createAssignable(TC::TreeNode<TCDBus::DeviceNode> node,
|
||||
QDBusConnection conn, AssignableItemData itemData) {
|
||||
auto ifaceItem = new AssignableItem(this);
|
||||
auto proxy = new AssignableProxy(node.value().path, conn, this);
|
||||
|
||||
connect(proxy, &AssignableProxy::connectionValueChanged, [=] (auto result, auto text) {
|
||||
p::match(result) (
|
||||
pattern(as<QVariant>(arg)) = [=](auto v) {
|
||||
QVariant data;
|
||||
data.setValue(connectionColor());
|
||||
ifaceItem->setData(data, Qt::BackgroundRole);
|
||||
ifaceItem->setText(text);
|
||||
//qDebug() << text;
|
||||
},
|
||||
pattern(_) = []{}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
QVariant pv;
|
||||
pv.setValue(proxy);
|
||||
ifaceItem->setData(pv, AssignableProxyRole);
|
||||
QVariant v;
|
||||
v.setValue(itemData);
|
||||
ifaceItem->setData(v, AssignableRole);
|
||||
@ -159,6 +184,21 @@ QStandardItem *DeviceModel::createAssignable(TC::TreeNode<TCDBus::DeviceNode> no
|
||||
return ifaceItem;
|
||||
}
|
||||
|
||||
QVariant DeviceModel::data(const QModelIndex &index, int role) const {
|
||||
if (index.row() != DeviceModel::NameColumn && role == DeviceModel::NodeNameRole) {
|
||||
// Get name from adjacent column
|
||||
auto nameIndex =
|
||||
this->index(index.row(), DeviceModel::NameColumn, index.parent());
|
||||
return nameIndex.data(Qt::DisplayRole);
|
||||
}
|
||||
if (index.column() != InterfaceColumn && role == DynamicReadableProxyRole) {
|
||||
auto idx =
|
||||
this->index(index.row(), DeviceModel::InterfaceColumn, index.parent());
|
||||
return idx.data(DynamicReadableProxyRole);
|
||||
}
|
||||
return QStandardItemModel::data(index, role);
|
||||
}
|
||||
|
||||
std::optional<QStandardItem*> DeviceModel::setupAssignable(
|
||||
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
|
||||
QDBusInterface ifaceNode("org.tuxclocker", node.value().path,
|
||||
@ -197,13 +237,17 @@ void updateReadItemText(QStandardItem *item, T value,
|
||||
QString("%1 %2").arg(value).arg(unit.value()) :
|
||||
QString("%1").arg(value);
|
||||
item->setText(text);
|
||||
//qDebug() << item->data(DeviceModel::DynamicReadableProxyRole);
|
||||
}
|
||||
|
||||
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);
|
||||
auto unit = proxy->unit();
|
||||
QVariant v;
|
||||
v.setValue(proxy);
|
||||
item->setData(v, DynamicReadableProxyRole);
|
||||
auto unit = proxy->unit();
|
||||
|
||||
connect(proxy, &DynamicReadableProxy::valueChanged, [=](ReadResult res) {
|
||||
p::match(res)(
|
||||
|
@ -2,23 +2,26 @@
|
||||
|
||||
#include "AssignableItem.hpp"
|
||||
#include "AssignableItemData.hpp"
|
||||
#include "DynamicReadableProxy.hpp"
|
||||
|
||||
#include <DBusTypes.hpp>
|
||||
#include <Device.hpp>
|
||||
#include <patterns.hpp>
|
||||
#include <Tree.hpp>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusInterface>
|
||||
#include <QFlags>
|
||||
#include <QHash>
|
||||
#include <QIcon>
|
||||
#include <QStandardItemModel>
|
||||
#include <QPalette>
|
||||
#include <Tree.hpp>
|
||||
|
||||
namespace TC = TuxClocker;
|
||||
namespace TCDBus = TuxClocker::DBus;
|
||||
|
||||
// Why the fuck do I have to forward declare this?
|
||||
class AssignableItem;
|
||||
class AssignableProxy;
|
||||
|
||||
class DeviceModel : public QStandardItemModel {
|
||||
public:
|
||||
@ -30,8 +33,11 @@ public:
|
||||
|
||||
enum Role {
|
||||
AssignableRole = Qt::UserRole, // Holds the data about the assignable
|
||||
AssignableProxyRole,
|
||||
ConnectionRole, // Data about the connection
|
||||
InterfaceTypeRole // InterfaceType
|
||||
DynamicReadableProxyRole,
|
||||
InterfaceTypeRole, // InterfaceType
|
||||
NodeNameRole //
|
||||
};
|
||||
|
||||
enum InterfaceFlag {
|
||||
@ -41,14 +47,15 @@ public:
|
||||
AllInterfaces = (Assignable | DynamicReadable | StaticReadable)
|
||||
};
|
||||
typedef QFlags<InterfaceFlag> InterfaceFlags;
|
||||
|
||||
enum class FilterFlag {
|
||||
|
||||
};
|
||||
|
||||
// For decoupling AssignableItems created in the model
|
||||
void applyChanges() {emit changesApplied();}
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
static QIcon assignableIcon() {return QIcon::fromTheme("edit-entry");}
|
||||
// Get AssignableProxy of an item
|
||||
std::optional<const AssignableProxy*> assignableProxyFromItem(QStandardItem *item);
|
||||
// AssignableProxies are associated with both row items
|
||||
//QVariant data(QModelIndex&);
|
||||
static QIcon dynamicReadableIcon() {return QIcon(":/ruler.svg");}
|
||||
static QIcon staticReadableIcon() {return QIcon::fromTheme("help-about");}
|
||||
signals:
|
||||
@ -56,6 +63,8 @@ signals:
|
||||
private:
|
||||
Q_OBJECT
|
||||
|
||||
QHash<QStandardItem*, AssignableProxy*> m_assignableProxyHash;
|
||||
|
||||
// Separate handling interfaces since otherwise we run out of columns
|
||||
QStandardItem *createAssignable(TC::TreeNode<TCDBus::DeviceNode> node,
|
||||
QDBusConnection conn, AssignableItemData data);
|
||||
@ -68,6 +77,7 @@ private:
|
||||
constexpr int fadeOutTime() {return 5000;} // milliseconds
|
||||
constexpr int transparency() {return 120;} // 0-255
|
||||
// Colors for items
|
||||
QColor connectionColor() {return QColor(0, 0, 255, transparency());} // blue
|
||||
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
|
||||
|
@ -5,7 +5,8 @@
|
||||
Q_DECLARE_METATYPE(DeviceModel::InterfaceFlag)
|
||||
|
||||
DeviceProxyModel::DeviceProxyModel(DeviceModel &model, QObject *parent) :
|
||||
QSortFilterProxyModel(parent), m_showIcons(true), m_showValueColumn(true) {
|
||||
QSortFilterProxyModel(parent), m_disableFiltered(false),
|
||||
m_showIcons(true), m_showValueColumn(true) {
|
||||
setSourceModel(&model);
|
||||
m_flags = DeviceModel::AllInterfaces;
|
||||
}
|
||||
@ -37,6 +38,22 @@ bool DeviceProxyModel::filterAcceptsRow(int sourceRow,
|
||||
return !shouldHide;
|
||||
}
|
||||
|
||||
Qt::ItemFlags DeviceProxyModel::flags(const QModelIndex &index) const {
|
||||
if (!m_disableFiltered)
|
||||
return QSortFilterProxyModel::flags(index);
|
||||
|
||||
auto iface =
|
||||
QSortFilterProxyModel::data(index, DeviceModel::InterfaceTypeRole);
|
||||
auto removedFlags = Qt::ItemIsSelectable;
|
||||
auto itemFlags = QSortFilterProxyModel::flags(index);
|
||||
if (!iface.isValid())
|
||||
// Remove selectable flag
|
||||
return itemFlags &(~removedFlags);
|
||||
auto flags = iface.value<DeviceModel::InterfaceFlag>();
|
||||
return (flags & m_flags) ? itemFlags :
|
||||
itemFlags &(~removedFlags);
|
||||
}
|
||||
|
||||
bool DeviceProxyModel::lessThan(const QModelIndex &left,
|
||||
const QModelIndex &right) const {
|
||||
// TODO: doesn't work as expected if sorted from interface column
|
||||
|
@ -9,6 +9,8 @@ class DeviceProxyModel : public QSortFilterProxyModel {
|
||||
public:
|
||||
DeviceProxyModel(DeviceModel &model, QObject *parent = nullptr);
|
||||
DeviceModel::InterfaceFlags filterFlags() {return m_flags;}
|
||||
// Disable items that don't contain the interface in m_flags
|
||||
void setDisableFiltered(bool on) {m_disableFiltered = on;}
|
||||
void setFlags(DeviceModel::InterfaceFlags flags) {
|
||||
m_flags = flags;
|
||||
invalidateFilter();
|
||||
@ -37,9 +39,12 @@ protected:
|
||||
const override {
|
||||
return !(!m_showValueColumn && sourceColumn == DeviceModel::InterfaceColumn);
|
||||
}
|
||||
// Optionally disable selection of items that don't have the wanted interface
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
// Overridden for showing items with children last/first
|
||||
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||
private:
|
||||
bool m_disableFiltered;
|
||||
bool m_showIcons;
|
||||
bool m_showValueColumn;
|
||||
DeviceModel::InterfaceFlags m_flags;
|
||||
|
106
src/tuxclocker-qt/data/DynamicReadableConnection.hpp
Normal file
106
src/tuxclocker-qt/data/DynamicReadableConnection.hpp
Normal file
@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include "AssignableConnection.hpp"
|
||||
#include <DynamicReadableProxy.hpp>
|
||||
#include <fplus/fplus.hpp>
|
||||
#include <patterns.hpp>
|
||||
#include <QDBusVariant>
|
||||
#include <QDebug>
|
||||
#include <QPointF>
|
||||
#include <QRandomGenerator>
|
||||
#include <QTimer>
|
||||
#include <QVector>
|
||||
#include <memory>
|
||||
|
||||
// delete these
|
||||
using namespace fplus;
|
||||
using namespace mpark::patterns;
|
||||
using namespace TuxClocker::Device;
|
||||
|
||||
class DynamicReadableConnectionData {
|
||||
public:
|
||||
// y = f(x)
|
||||
QVector<QPointF> points;
|
||||
// DBus path
|
||||
QString dynamicReadablePath;
|
||||
};
|
||||
|
||||
// TODO: make DynamicReadableProxy type parametrized so we can be sure to only get numeric values from it
|
||||
// Connection of an Assignable with a DynamicReadable
|
||||
template <typename OutType> // Result of linear interpolation
|
||||
class DynamicReadableConnection : public AssignableConnection {
|
||||
public:
|
||||
DynamicReadableConnection(DynamicReadableProxy &proxy,
|
||||
QVector<QPointF> points, QObject *parent = nullptr)
|
||||
: AssignableConnection(parent), m_proxy(proxy), m_points(points) {
|
||||
auto sorted = sort_by([](auto point_l, auto point_r) {
|
||||
return point_l.x() < point_r.x();
|
||||
}, m_points);
|
||||
m_points = sorted;
|
||||
}
|
||||
virtual QVariant connectionData() override {return QVariant();}
|
||||
virtual void start() override {
|
||||
/*QObject::connect(&m_timer, &QTimer::timeout, [this] {
|
||||
int rand = QRandomGenerator::global()->bounded(0, 10);
|
||||
|
||||
QDBusVariant arg{QVariant{rand}};
|
||||
QVariant v;
|
||||
v.setValue(arg);
|
||||
emit targetValueChanged(v, QString::number(rand));
|
||||
});
|
||||
m_timer.start(1000);*/
|
||||
|
||||
connect(&m_proxy, &DynamicReadableProxy::valueChanged, [this](auto val) {
|
||||
match(val) (
|
||||
pattern(as<ReadableValue>(arg)) = [this](auto rv) {
|
||||
match(rv) (
|
||||
pattern(as<uint>(arg)) = [this](auto u) {
|
||||
emitTargetValue(u);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
/*QDBusVariant arg{QVariant{1}};
|
||||
QVariant v;
|
||||
v.setValue(arg);
|
||||
emit targetValueChanged(v, "1");*/
|
||||
|
||||
}
|
||||
virtual void stop() override {
|
||||
}
|
||||
private:
|
||||
DynamicReadableProxy &m_proxy;
|
||||
QTimer m_timer;
|
||||
QVector<QPointF> m_points;
|
||||
|
||||
template <typename U>
|
||||
void emitTargetValue(U reading) {
|
||||
// Find two points from the vector so that:
|
||||
// p[i].x < val < p[i + 1].x
|
||||
std::optional<int> leftIndex = std::nullopt;
|
||||
for (int i = 0; i < m_points.length() - 1; i++) {
|
||||
if (m_points[i].x() < reading && reading < m_points[i + 1].x()) {
|
||||
leftIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (leftIndex == std::nullopt)
|
||||
return;
|
||||
int li = leftIndex.value();
|
||||
// What percentage the value is from dx of left and right interp points
|
||||
double dx = m_points[li + 1].x() - m_points[li].x();
|
||||
double dvx = reading - m_points[li].x();
|
||||
double p = dvx / dx;
|
||||
OutType interp_y = lerp(m_points[li].y(),
|
||||
m_points[li + 1].y(), p);
|
||||
QDBusVariant arg{QVariant{interp_y}};
|
||||
QVariant v;
|
||||
v.setValue(arg);
|
||||
emit targetValueChanged(v, QString::number(interp_y));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T lerp(T a, T b, double t) {return a + (t * (b - a));}
|
||||
};
|
@ -4,7 +4,7 @@
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
QApplication app(argc, argv);
|
||||
|
||||
|
||||
MainWindow mw;
|
||||
mw.show();
|
||||
|
||||
|
@ -5,9 +5,11 @@ qt5_dep = dependency('qt5', modules: ['DBus', 'Charts', 'Widgets'])
|
||||
boost_dep = dependency('boost')
|
||||
|
||||
moc_files = qt5.preprocess(moc_headers: ['MainWindow.hpp',
|
||||
'data/AssignableConnection.hpp',
|
||||
'data/AssignableItem.hpp',
|
||||
'data/AssignableProxy.hpp',
|
||||
'data/DeviceModel.hpp',
|
||||
'data/DynamicReadableConnection.hpp',
|
||||
'data/DynamicReadableProxy.hpp',
|
||||
'widgets/DragChartView.hpp'],
|
||||
qresources : ['resources/resources.qrc'],
|
||||
@ -20,6 +22,7 @@ sources = ['main.cpp',
|
||||
'data/DeviceModelDelegate.cpp',
|
||||
'data/DeviceProxyModel.cpp',
|
||||
'data/DynamicReadableProxy.cpp',
|
||||
'widgets/DeviceBrowser.cpp',
|
||||
'widgets/DeviceTreeView.cpp',
|
||||
'widgets/DragChartView.cpp',
|
||||
'widgets/EnumEditor.cpp',
|
||||
|
89
src/tuxclocker-qt/widgets/DeviceBrowser.cpp
Normal file
89
src/tuxclocker-qt/widgets/DeviceBrowser.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include "DeviceBrowser.hpp"
|
||||
#include "AssignableItemData.hpp"
|
||||
#include "qnamespace.h"
|
||||
|
||||
#include <patterns.hpp>
|
||||
#include <QVariant>
|
||||
|
||||
using namespace mpark::patterns;
|
||||
using namespace TuxClocker::Device;
|
||||
|
||||
Q_DECLARE_METATYPE(AssignableItemData)
|
||||
Q_DECLARE_METATYPE(AssignableProxy*)
|
||||
|
||||
DeviceBrowser::DeviceBrowser(DeviceModel &model, QWidget *parent)
|
||||
: QWidget(parent), m_deviceModel(model) {
|
||||
m_layout = new QGridLayout(this);
|
||||
m_proxyModel = new DeviceProxyModel(model, this);
|
||||
m_treeView = new DeviceTreeView;
|
||||
m_treeView->setModel(m_proxyModel);
|
||||
m_flagLabel = new QLabel("Showing:");
|
||||
m_apply = new QPushButton("Apply changes");
|
||||
m_apply->setEnabled(true);
|
||||
|
||||
m_flagEditor = new FlagEditor(
|
||||
QVector({
|
||||
std::tuple(
|
||||
QString("Assignables"),
|
||||
DeviceModel::assignableIcon(),
|
||||
DeviceModel::Assignable
|
||||
),
|
||||
std::tuple(
|
||||
QString("Dynamic Values"),
|
||||
DeviceModel::dynamicReadableIcon(),
|
||||
DeviceModel::DynamicReadable
|
||||
),
|
||||
std::tuple(
|
||||
QString("Static Values"),
|
||||
DeviceModel::staticReadableIcon(),
|
||||
DeviceModel::StaticReadable
|
||||
)
|
||||
}), this);
|
||||
|
||||
connect(m_apply, &QPushButton::pressed, &m_deviceModel,
|
||||
&DeviceModel::applyChanges);
|
||||
|
||||
m_treeView->functionEditorRequested.connect([this](QModelIndex &index) {
|
||||
auto a_data = index.data(DeviceModel::AssignableRole);
|
||||
if (!a_data.isValid())
|
||||
return;
|
||||
|
||||
auto a_info = a_data.value<AssignableItemData>();
|
||||
auto proxy = index.data(DeviceModel::AssignableProxyRole)
|
||||
.value<AssignableProxy*>();
|
||||
auto name = index.data(DeviceModel::NodeNameRole).toString();
|
||||
match(a_info.assignableInfo()) (
|
||||
pattern(as<RangeInfo>(arg)) = [=](auto ri) {
|
||||
auto f_editor = new FunctionEditor{m_deviceModel,
|
||||
ri, *proxy, name};
|
||||
f_editor->show();
|
||||
f_editor->assignableConnectionChanged
|
||||
.connect([=](auto conn) {
|
||||
proxy->startConnection(conn);
|
||||
});
|
||||
},
|
||||
pattern(_) = [] {}
|
||||
);
|
||||
|
||||
/*auto f_editor = new FunctionEditor(m_deviceModel, rangeInfo, proxy);
|
||||
f_editor->show();
|
||||
|
||||
f_editor->assignableConnectionChanged.connect(
|
||||
[&proxy] (auto assignableConnection) {
|
||||
proxy.startConnection(assignableConnection);
|
||||
});*/
|
||||
});
|
||||
|
||||
m_flagEditor->setFlags(DeviceModel::AllInterfaces);
|
||||
|
||||
m_flagEditor->flagsChanged.connect([=](auto flags) {
|
||||
m_proxyModel->setFlags(flags);
|
||||
});
|
||||
|
||||
m_layout->addWidget(m_flagLabel, 0, 0);
|
||||
m_layout->addWidget(m_flagEditor, 0, 1);
|
||||
m_layout->addWidget(m_treeView, 1, 0, 1, 2);
|
||||
m_layout->addWidget(m_apply, 2, 0, 1, 2);
|
||||
|
||||
setLayout(m_layout);
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
#include <DeviceModel.hpp>
|
||||
#include <DeviceProxyModel.hpp>
|
||||
#include <FlagEditor.hpp>
|
||||
//#include <FunctionEditor.hpp>
|
||||
#include <FunctionEditor.hpp>
|
||||
#include <QDebug>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
@ -14,57 +14,7 @@
|
||||
// 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_proxyModel = new DeviceProxyModel(model, this);
|
||||
m_treeView = new DeviceTreeView;
|
||||
m_treeView->setModel(m_proxyModel);
|
||||
m_flagLabel = new QLabel("Showing:");
|
||||
m_apply = new QPushButton("Apply changes");
|
||||
m_apply->setEnabled(true);
|
||||
|
||||
m_flagEditor = new FlagEditor(
|
||||
QVector({
|
||||
std::tuple(
|
||||
QString("Assignables"),
|
||||
DeviceModel::assignableIcon(),
|
||||
DeviceModel::Assignable
|
||||
),
|
||||
std::tuple(
|
||||
QString("Dynamic Values"),
|
||||
DeviceModel::dynamicReadableIcon(),
|
||||
DeviceModel::DynamicReadable
|
||||
),
|
||||
std::tuple(
|
||||
QString("Static Values"),
|
||||
DeviceModel::staticReadableIcon(),
|
||||
DeviceModel::StaticReadable
|
||||
)
|
||||
}), this);
|
||||
|
||||
connect(m_apply, &QPushButton::pressed, &m_deviceModel,
|
||||
&DeviceModel::applyChanges);
|
||||
|
||||
m_treeView->functionEditorRequested.connect([this] {
|
||||
//auto f_editor = new FunctionEditor(m_deviceModel);
|
||||
//f_editor->show();
|
||||
});
|
||||
|
||||
m_flagEditor->setFlags(DeviceModel::AllInterfaces);
|
||||
|
||||
m_flagEditor->flagsChanged.connect([=](auto flags) {
|
||||
m_proxyModel->setFlags(flags);
|
||||
});
|
||||
|
||||
m_layout->addWidget(m_flagLabel, 0, 0);
|
||||
m_layout->addWidget(m_flagEditor, 0, 1);
|
||||
m_layout->addWidget(m_treeView, 1, 0, 1, 2);
|
||||
m_layout->addWidget(m_apply, 2, 0, 1, 2);
|
||||
|
||||
setLayout(m_layout);
|
||||
}
|
||||
|
||||
DeviceBrowser(DeviceModel &model, QWidget *parent = nullptr);
|
||||
private:
|
||||
DeviceModel &m_deviceModel;
|
||||
DeviceProxyModel *m_proxyModel;
|
||||
|
@ -1,45 +1,50 @@
|
||||
#include "DeviceTreeView.hpp"
|
||||
#include "qcheckbox.h"
|
||||
|
||||
#include <DragChartView.hpp>
|
||||
//#include <FunctionEditor.hpp>
|
||||
#include <patterns.hpp>
|
||||
#include <QCheckBox>
|
||||
#include <QDebug>
|
||||
#include <QHeaderView>
|
||||
|
||||
using namespace mpark::patterns;
|
||||
using namespace TuxClocker::Device;
|
||||
|
||||
Q_DECLARE_METATYPE(AssignableItemData)
|
||||
Q_DECLARE_METATYPE(AssignableProxy*)
|
||||
|
||||
DeviceTreeView::DeviceTreeView(QWidget *parent)
|
||||
: QTreeView(parent) {
|
||||
header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
setSortingEnabled(true);
|
||||
auto triggers = editTriggers() ^= DoubleClicked;
|
||||
triggers |= SelectedClicked;
|
||||
setEditTriggers(SelectedClicked | EditKeyPressed);
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, &QTreeView::customContextMenuRequested, [this](QPoint point) {
|
||||
auto index = indexAt(point);
|
||||
auto data = index.data(DeviceModel::AssignableRole);
|
||||
auto proxyData = index.data(DeviceModel::AssignableProxyRole);
|
||||
QMenu menu;
|
||||
if (data.canConvert<AssignableItemData>()) {
|
||||
QCheckBox checkBox("Enable connection");
|
||||
QAction editConn("Edit connection...");
|
||||
auto enableConn = new QWidgetAction(&menu);
|
||||
enableConn->setDefaultWidget(&checkBox);
|
||||
menu.addActions({&editConn, enableConn});
|
||||
if (data.canConvert<AssignableItemData>() &&
|
||||
proxyData.canConvert<AssignableProxy*>()) {
|
||||
functionEditorRequested(index);
|
||||
/*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, [&] {
|
||||
QVariant v;
|
||||
v.setValue(a_data);
|
||||
m_deviceModel.setData(index, v, DeviceModel::AssignableRole);
|
||||
});
|
||||
menu.addAction(commitAct);
|
||||
menu.exec(QCursor::pos());*/
|
||||
auto proxy = proxyData.value<AssignableProxy*>();
|
||||
match(a_data.assignableInfo()) (
|
||||
pattern(as<RangeInfo>(arg)) = [this, &menu, proxy, &editConn](auto ri) {
|
||||
//functionEditorRequested(*proxy, ri);
|
||||
connect(&editConn, &QAction::triggered, [this, proxy, ri] {
|
||||
functionEditorRequested(*proxy, ri);
|
||||
});
|
||||
menu.exec(QCursor::pos());
|
||||
},
|
||||
pattern(_) = []{}
|
||||
);*/
|
||||
|
||||
//auto dragView = new FunctionEditor;
|
||||
//dragView->show();
|
||||
|
||||
functionEditorRequested();
|
||||
}
|
||||
});
|
||||
m_delegate = new DeviceModelDelegate(this);
|
||||
|
@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <AssignableItemData.hpp>
|
||||
#include <AssignableProxy.hpp>
|
||||
#include <boost/signals2.hpp>
|
||||
#include <Device.hpp>
|
||||
#include <DeviceModel.hpp>
|
||||
#include <DeviceModelDelegate.hpp>
|
||||
#include <QMenu>
|
||||
@ -14,7 +16,9 @@ public:
|
||||
DeviceTreeView(QWidget *parent = nullptr);
|
||||
// Accessor method for connecting everything in the browser
|
||||
//const DeviceModel &deviceModel() {return m_deviceModel;}
|
||||
boost::signals2::signal<void()> functionEditorRequested;
|
||||
// TODO: make this more generalized
|
||||
// Defers the complexity to DeviceBrowser
|
||||
boost::signals2::signal<void(QModelIndex&)> functionEditorRequested;
|
||||
protected:
|
||||
/* Workaround for the retarded behavior of waiting for a double click,
|
||||
you can't even disable it! */
|
||||
|
@ -176,6 +176,12 @@ void DragChartView::setVector(const QVector <QPointF> vector) {
|
||||
m_series.replace(vector);
|
||||
}
|
||||
|
||||
void DragChartView::setRange(qreal xmin, qreal xmax, qreal ymin, qreal ymax) {
|
||||
m_series.clear();
|
||||
m_xAxis.setRange(xmin, xmax);
|
||||
m_yAxis.setRange(ymin, ymax);
|
||||
}
|
||||
|
||||
bool DragChartView::event(QEvent *event) {
|
||||
//qDebug() << event->type();
|
||||
|
||||
@ -227,7 +233,10 @@ void DragChartView::mouseMoveEvent(QMouseEvent *event) {
|
||||
m_toolTipLabel->show();
|
||||
}
|
||||
|
||||
m_toolTipLabel->setText(QString("%1, %2").arg(QString::number(m_latestScatterPoint.x()), QString::number(m_latestScatterPoint.y())));
|
||||
m_toolTipLabel->setText(QString("%1, %2")
|
||||
.arg(QString::number(m_latestScatterPoint.x()),
|
||||
QString::number(m_latestScatterPoint.y())));
|
||||
|
||||
// FIXME : doesn't work properly when screen is switched(?)
|
||||
m_toolTipLabel->move(event->screenPos().toPoint() + toolTipOffset(this, event->windowPos().toPoint()));
|
||||
|
||||
@ -324,14 +333,17 @@ void DragChartView::drawFillerLines(QScatterSeries *series) {
|
||||
|
||||
for (int i = 0; i < sorted.length() - 1; i++) {
|
||||
m_lineFillerItems[i]->setLine(QLineF(chart()->mapToPosition(sorted[i]),
|
||||
chart()->mapToPosition(sorted[i + 1])));
|
||||
chart()->mapToPosition(sorted[i + 1])));
|
||||
}
|
||||
|
||||
m_leftLineFillerItem->setLine(QLineF(chart()->mapToPosition(QPointF(m_xAxis.min(), sorted[0].y())),
|
||||
chart()->mapToPosition(sorted[0])));
|
||||
m_leftLineFillerItem->setLine(
|
||||
QLineF(chart()->mapToPosition(QPointF(
|
||||
m_xAxis.min(), sorted[0].y())),
|
||||
chart()->mapToPosition(sorted[0])));
|
||||
|
||||
m_rightLineFillerItem->setLine(QLineF(chart()->mapToPosition(sorted.last()),
|
||||
chart()->mapToPosition(QPointF(m_xAxis.max(), sorted.last().y()))));
|
||||
m_rightLineFillerItem->setLine(
|
||||
QLineF(chart()->mapToPosition(sorted.last()),
|
||||
chart()->mapToPosition(QPointF(m_xAxis.max(), sorted.last().y()))));
|
||||
|
||||
chart()->update();
|
||||
}
|
||||
|
@ -10,11 +10,13 @@ using namespace QtCharts;
|
||||
class DragChartView : public QChartView {
|
||||
public:
|
||||
DragChartView(QWidget *parent = nullptr);
|
||||
QValueAxis *xAxis() {return &m_xAxis;}
|
||||
QValueAxis *yAxis() {return &m_yAxis;}
|
||||
QValueAxis &xAxis() {return m_xAxis;}
|
||||
QValueAxis &yAxis() {return m_yAxis;}
|
||||
|
||||
void setVector(const QVector <QPointF> vector);
|
||||
QVector <QPointF> vector() {return m_series.pointsVector();}
|
||||
// Clear the points and set new range
|
||||
void setRange(qreal xmin, qreal xmax, qreal ymin, qreal ymax);
|
||||
protected:
|
||||
bool event(QEvent*);
|
||||
void mousePressEvent(QMouseEvent*);
|
||||
|
@ -94,6 +94,9 @@ protected:
|
||||
QStylePainter painter(this);
|
||||
QStyleOptionComboBox opt;
|
||||
initStyleOption(&opt);
|
||||
QPalette rp;
|
||||
rp.setColor(QPalette::Highlight, Qt::red);
|
||||
opt.palette = rp;
|
||||
|
||||
// Show which flags are selected
|
||||
auto items = m_flagHash.values();
|
||||
|
122
src/tuxclocker-qt/widgets/FunctionEditor.hpp
Normal file
122
src/tuxclocker-qt/widgets/FunctionEditor.hpp
Normal file
@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
/* Widget for editing the function for an assignable.
|
||||
Possible options, where y = value of assignable, t = time,
|
||||
x = value of readable, n = static value set in model editor:
|
||||
- y = n
|
||||
- y = f(x)
|
||||
- y = f(t)
|
||||
*/
|
||||
|
||||
#include "DynamicReadableProxy.hpp"
|
||||
#include "qnamespace.h"
|
||||
#include <AssignableConnection.hpp>
|
||||
#include <AssignableProxy.hpp>
|
||||
#include <boost/signals2.hpp>
|
||||
#include <DeviceProxyModel.hpp>
|
||||
#include <DragChartView.hpp>
|
||||
#include <DynamicReadableConnection.hpp>
|
||||
#include <NodeSelector.hpp>
|
||||
#include <patterns.hpp>
|
||||
#include <QAbstractItemView>
|
||||
#include <QComboBox>
|
||||
#include <QDebug>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QModelIndex>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
// Delet this
|
||||
namespace p = mpark::patterns;
|
||||
|
||||
Q_DECLARE_METATYPE(DynamicReadableProxy*)
|
||||
|
||||
// TODO: make constructor of the type data Editor a = Maybe (Range a)
|
||||
class FunctionEditor : public QWidget {
|
||||
public:
|
||||
FunctionEditor(DeviceModel &model, TuxClocker::Device::RangeInfo rangeInfo,
|
||||
AssignableProxy &proxy, QString nodeName,
|
||||
QWidget *parent = nullptr)
|
||||
: QWidget(parent), m_assignableProxy(proxy),
|
||||
m_model(model), m_proxyModel(model),
|
||||
m_rangeInfo(rangeInfo) {
|
||||
m_proxyModel.setDisableFiltered(true);
|
||||
m_proxyModel.setFlags(DeviceModel::DynamicReadable);
|
||||
m_proxyModel.setShowIcons(false);
|
||||
//m_proxyModel.setShowValueColumn(false);
|
||||
m_layout = new QGridLayout(this);
|
||||
m_functionComboBox = new QComboBox;
|
||||
m_functionComboBox->addItem("Function of time");
|
||||
m_dependableReadableComboBox = new NodeSelector;
|
||||
auto treeView = new QTreeView;
|
||||
m_dependableReadableComboBox->setModel(&m_proxyModel);
|
||||
m_dependableReadableComboBox->setView(treeView);
|
||||
treeView->expandAll();
|
||||
m_dependableLabel = new QLabel("Connecting with:");
|
||||
m_layout->addWidget(m_dependableReadableComboBox, 0, 0);
|
||||
m_layout->addWidget(m_functionComboBox, 0, 1);
|
||||
m_dragView = new DragChartView;
|
||||
|
||||
p::match(rangeInfo) (
|
||||
p::pattern(p::as<TuxClocker::Device::Range<double>>(p::arg))
|
||||
= [this](auto dr) {
|
||||
m_dragView->setRange(0, 100, dr.min, dr.max);
|
||||
},
|
||||
p::pattern(p::as<TuxClocker::Device::Range<int>>(p::arg))
|
||||
= [this](auto ir) {
|
||||
m_dragView->setRange(0, 100, ir.min, ir.max);
|
||||
}
|
||||
);
|
||||
//m_dragView->setRange(0, 100, 0, 100);
|
||||
m_layout->addWidget(m_dragView, 1, 0, 1, 2);
|
||||
m_applyButton = new QPushButton("Apply");
|
||||
// No connection to apply at first
|
||||
m_applyButton->setEnabled(false);
|
||||
m_cancelButton = new QPushButton("Cancel");
|
||||
m_layout->addWidget(m_cancelButton, 2, 0, 1, 1);
|
||||
m_layout->addWidget(m_applyButton, 2, 1, 1, 1);
|
||||
|
||||
connect(m_applyButton, &QPushButton::clicked, [this] {
|
||||
auto proxy = m_latestNodeIndex
|
||||
.data(DeviceModel::DynamicReadableProxyRole)
|
||||
.value<DynamicReadableProxy*>();
|
||||
//qDebug() << proxy;
|
||||
auto points = m_dragView->vector();
|
||||
if (points.length() < 2)
|
||||
return;
|
||||
//qDebug() << points;
|
||||
auto conn = std::make_shared<DynamicReadableConnection<int>>(
|
||||
*proxy, points);
|
||||
assignableConnectionChanged(conn);
|
||||
});
|
||||
|
||||
m_dragView->yAxis().setTitleText(nodeName);
|
||||
|
||||
m_dependableReadableComboBox->indexChanged
|
||||
.connect([this](auto &index) {
|
||||
m_latestNodeIndex = index;
|
||||
m_applyButton->setEnabled(true);
|
||||
auto nodeName = index.data(Qt::DisplayRole).toString();
|
||||
m_dragView->xAxis().setTitleText(nodeName);
|
||||
});
|
||||
|
||||
setLayout(m_layout);
|
||||
}
|
||||
boost::signals2::signal<void(std::shared_ptr<AssignableConnection>)>
|
||||
assignableConnectionChanged;
|
||||
private:
|
||||
AssignableProxy &m_assignableProxy;
|
||||
DeviceModel &m_model;
|
||||
DeviceProxyModel m_proxyModel;
|
||||
DragChartView *m_dragView;
|
||||
//NodeSelector *m_nodeSelector;
|
||||
QComboBox *m_functionComboBox;
|
||||
NodeSelector *m_dependableReadableComboBox;
|
||||
QGridLayout *m_layout;
|
||||
QLabel *m_dependableLabel;
|
||||
QModelIndex m_latestNodeIndex;
|
||||
QPushButton *m_applyButton, *m_cancelButton;
|
||||
TuxClocker::Device::RangeInfo m_rangeInfo;
|
||||
};
|
65
src/tuxclocker-qt/widgets/NodeSelector.hpp
Normal file
65
src/tuxclocker-qt/widgets/NodeSelector.hpp
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/signals2.hpp>
|
||||
#include <QApplication>
|
||||
#include <QComboBox>
|
||||
#include <QDebug>
|
||||
#include <QEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollBar>
|
||||
|
||||
// TODO: If a disabled item is clicked, the item that is highlighted get selected instead
|
||||
class NodeSelector : public QComboBox {
|
||||
public:
|
||||
NodeSelector(QWidget *parent = nullptr) : QComboBox(parent),
|
||||
m_skipNextHide(false) {
|
||||
view()->viewport()->installEventFilter(this);
|
||||
}
|
||||
bool eventFilter(QObject *obj, QEvent *ev) {
|
||||
if (ev->type() == QEvent::MouseButtonPress && obj == view()->viewport()) {
|
||||
auto mouse_ev = static_cast<QMouseEvent*>(ev);
|
||||
auto index = view()->indexAt(mouse_ev->pos());
|
||||
// Hide popup when an enabled item is clicked
|
||||
if (!view()->visualRect(index).contains(mouse_ev->pos()) ||
|
||||
!(model()->flags(index) & Qt::ItemIsEnabled))
|
||||
m_skipNextHide = true;
|
||||
else if (model()->flags(index) & Qt::ItemIsSelectable) {
|
||||
qDebug() << index.data(DeviceModel::DynamicReadableProxyRole);
|
||||
indexChanged(index);
|
||||
m_skipNextHide = false;
|
||||
}
|
||||
}
|
||||
return QComboBox::eventFilter(obj, ev);
|
||||
}
|
||||
virtual void hidePopup() override {
|
||||
if (m_skipNextHide)
|
||||
m_skipNextHide = false;
|
||||
else
|
||||
QComboBox::hidePopup();
|
||||
}
|
||||
/*void mouseReleaseEvent(QMouseEvent *event) override {
|
||||
auto index = view()->indexAt(event->pos());
|
||||
if (!view()->visualRect(index).contains(event->pos()) ||
|
||||
!(model()->flags(index) & Qt::ItemIsEnabled))
|
||||
qDebug() << index.data();
|
||||
|
||||
QComboBox::mouseReleaseEvent(event);
|
||||
}*/
|
||||
void setView(QAbstractItemView *view) {
|
||||
// Why no signal, Qt?
|
||||
view->viewport()->installEventFilter(this);
|
||||
|
||||
QComboBox::setView(view);
|
||||
}
|
||||
virtual void showPopup() override {
|
||||
// TODO: don't account for scrollbar width when it isn't visible
|
||||
// This is quite a weird way to do it but it works
|
||||
auto vBarWidth =
|
||||
QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
|
||||
view()->setMinimumWidth(view()->sizeHintForColumn(0) + vBarWidth);
|
||||
QComboBox::showPopup();
|
||||
}
|
||||
boost::signals2::signal<void(QModelIndex&)> indexChanged;
|
||||
private:
|
||||
bool m_skipNextHide;
|
||||
};
|
Loading…
Reference in New Issue
Block a user