Implemented qt port + e2e
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* @file SourceSidebar.cpp
|
||||
*/
|
||||
|
||||
#include "SourceSidebar.h"
|
||||
#include "Hub.h"
|
||||
#include "Theme.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QTreeWidget>
|
||||
#include <QHeaderView>
|
||||
#include <QPushButton>
|
||||
#include <QMenu>
|
||||
#include <QMimeData>
|
||||
#include <QDrag>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QIcon>
|
||||
#include <QSet>
|
||||
#include <QDataStream>
|
||||
#include <QApplication>
|
||||
|
||||
namespace shq {
|
||||
|
||||
const char* const kMimeSignal = "application/x-shq-signal";
|
||||
|
||||
/* Roles for tree items. */
|
||||
enum { RoleSrcIdx = Qt::UserRole + 1, RoleSigIdx, RoleSrcId };
|
||||
|
||||
/** QTreeWidget subclass that emits a signal drag with the qint32[2] payload. */
|
||||
class SourceTree : public QTreeWidget {
|
||||
public:
|
||||
explicit SourceTree(QWidget* parent = nullptr) : QTreeWidget(parent) {
|
||||
setDragEnabled(true);
|
||||
setDragDropMode(QAbstractItemView::DragOnly);
|
||||
}
|
||||
protected:
|
||||
void startDrag(Qt::DropActions /*supported*/) override {
|
||||
QTreeWidgetItem* it = currentItem();
|
||||
if (!it) { return; }
|
||||
bool ok = false;
|
||||
int sigIdx = it->data(0, RoleSigIdx).toInt(&ok);
|
||||
if (!ok || sigIdx < 0) { return; } /* only signal leaves drag */
|
||||
int srcIdx = it->data(0, RoleSrcIdx).toInt();
|
||||
|
||||
QByteArray payload;
|
||||
{
|
||||
QDataStream ds(&payload, QIODevice::WriteOnly);
|
||||
ds.setByteOrder(QDataStream::LittleEndian);
|
||||
ds << static_cast<qint32>(srcIdx) << static_cast<qint32>(sigIdx);
|
||||
}
|
||||
auto* mime = new QMimeData;
|
||||
mime->setData(kMimeSignal, payload);
|
||||
|
||||
auto* drag = new QDrag(this);
|
||||
drag->setMimeData(mime);
|
||||
drag->exec(Qt::CopyAction);
|
||||
}
|
||||
};
|
||||
|
||||
SourceSidebar::SourceSidebar(Hub* hub, QWidget* parent)
|
||||
: QWidget(parent), hub_(hub) {
|
||||
auto* lay = new QVBoxLayout(this);
|
||||
lay->setContentsMargins(4, 4, 4, 4);
|
||||
lay->setSpacing(4);
|
||||
|
||||
tree_ = new SourceTree(this);
|
||||
tree_->setHeaderHidden(true);
|
||||
tree_->setColumnCount(1);
|
||||
tree_->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
tree_->setIndentation(12);
|
||||
connect(tree_, &QTreeWidget::customContextMenuRequested,
|
||||
this, &SourceSidebar::onContextMenu);
|
||||
lay->addWidget(tree_, 1);
|
||||
|
||||
auto* addBtn = new QPushButton("+ Add Source", this);
|
||||
connect(addBtn, &QPushButton::clicked, this,
|
||||
&SourceSidebar::addSourceRequested);
|
||||
lay->addWidget(addBtn);
|
||||
|
||||
auto* saveBtn = new QPushButton("Save Sources", this);
|
||||
connect(saveBtn, &QPushButton::clicked, this,
|
||||
[this]() { hub_->sendSaveSources(); });
|
||||
lay->addWidget(saveBtn);
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
/** Small square color swatch icon. */
|
||||
static QIcon swatch(const QColor& c) {
|
||||
QPixmap pm(12, 12);
|
||||
pm.fill(Qt::transparent);
|
||||
QPainter p(&pm);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(c);
|
||||
p.drawRect(0, 0, 12, 12);
|
||||
return QIcon(pm);
|
||||
}
|
||||
|
||||
void SourceSidebar::refresh() {
|
||||
/* Preserve expansion state by source id. */
|
||||
QSet<QString> expanded;
|
||||
for (int i = 0; i < tree_->topLevelItemCount(); i++) {
|
||||
QTreeWidgetItem* it = tree_->topLevelItem(i);
|
||||
if (it->isExpanded()) {
|
||||
expanded.insert(it->data(0, RoleSrcId).toString());
|
||||
}
|
||||
}
|
||||
|
||||
tree_->clear();
|
||||
const auto& sources = hub_->sources();
|
||||
for (int s = 0; s < static_cast<int>(sources.size()); s++) {
|
||||
const Source& src = sources[s];
|
||||
QString label = QString::fromStdString(
|
||||
src.label.empty() ? src.id : src.label);
|
||||
bool connected = (src.state == "connected");
|
||||
QString dot = connected ? QString::fromUtf8("\u25cf ")
|
||||
: QString::fromUtf8("\u25cb ");
|
||||
auto* top = new QTreeWidgetItem(tree_);
|
||||
top->setText(0, dot + label);
|
||||
top->setForeground(0, connected ? col::green() : col::red());
|
||||
top->setData(0, RoleSrcIdx, s);
|
||||
top->setData(0, RoleSigIdx, -1);
|
||||
top->setData(0, RoleSrcId, QString::fromStdString(src.id));
|
||||
top->setFlags(Qt::ItemIsEnabled);
|
||||
|
||||
for (int g = 0; g < static_cast<int>(src.signals.size()); g++) {
|
||||
const SignalView& sig = src.signals[g];
|
||||
QString name = QString::fromStdString(sig.meta.name);
|
||||
if (sig.meta.numElements > 1) {
|
||||
name += QString(" [%1]").arg(sig.meta.numElements);
|
||||
}
|
||||
if (!sig.meta.unit.empty()) {
|
||||
name += " (" + QString::fromStdString(sig.meta.unit) + ")";
|
||||
}
|
||||
auto* leaf = new QTreeWidgetItem(top);
|
||||
leaf->setText(0, name);
|
||||
leaf->setIcon(0, swatch(sig.color));
|
||||
leaf->setData(0, RoleSrcIdx, s);
|
||||
leaf->setData(0, RoleSigIdx, g);
|
||||
leaf->setData(0, RoleSrcId, QString::fromStdString(src.id));
|
||||
leaf->setForeground(0, col::text());
|
||||
leaf->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable |
|
||||
Qt::ItemIsDragEnabled);
|
||||
}
|
||||
|
||||
if (expanded.isEmpty() ||
|
||||
expanded.contains(QString::fromStdString(src.id))) {
|
||||
top->setExpanded(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SourceSidebar::onContextMenu(const QPoint& pos) {
|
||||
QTreeWidgetItem* it = tree_->itemAt(pos);
|
||||
if (!it) { return; }
|
||||
QString srcId = it->data(0, RoleSrcId).toString();
|
||||
if (srcId.isEmpty()) { return; }
|
||||
|
||||
QMenu menu(this);
|
||||
QAction* rm = menu.addAction("Remove source");
|
||||
if (menu.exec(tree_->viewport()->mapToGlobal(pos)) == rm) {
|
||||
hub_->sendRemoveSource(srcId.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace shq */
|
||||
Reference in New Issue
Block a user