From afb2d2317376b898caed5c298b0ecad667f93759 Mon Sep 17 00:00:00 2001 From: Carl Philipp Klemm Date: Sun, 26 Apr 2026 14:47:24 +0200 Subject: [PATCH] UI: Add group support to the sensor list widget --- src/ui/sensorlistwidget.cpp | 160 ++++++++++++++++++++++++------------ src/ui/sensorlistwidget.h | 8 +- 2 files changed, 113 insertions(+), 55 deletions(-) diff --git a/src/ui/sensorlistwidget.cpp b/src/ui/sensorlistwidget.cpp index f4a5cb6..9b229a8 100644 --- a/src/ui/sensorlistwidget.cpp +++ b/src/ui/sensorlistwidget.cpp @@ -3,36 +3,39 @@ #include #include #include +#include #include "sensorsettingsdialog.h" -SensorListWidget::SensorListWidget(const bool showHidden, QWidget *parent): QTableWidget(parent), +SensorListWidget::SensorListWidget(const bool showHidden, QWidget *parent): QTreeWidget(parent), showHidden_(showHidden) { setColumnCount(3); + setHeaderLabels({"Sensor", "Value", "Time"}); setSelectionBehavior(QAbstractItemView::SelectRows); - horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + header()->setSectionResizeMode(0, QHeaderView::Interactive); + header()->setSectionResizeMode(1, QHeaderView::Interactive); + header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); QScroller::grabGesture(this, QScroller::LeftMouseButtonGesture); setAutoScroll(true); setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); sensorsChanged(std::vector()); - verticalHeader()->hide(); - connect(this, &QTableWidget::doubleClicked, this, &SensorListWidget::onDoubleClick); + connect(this, &QTreeWidget::itemDoubleClicked, this, &SensorListWidget::onDoubleClick); } SensorListWidget::SensorListWidget(SensorStore& sensorStore, const bool showHidden, - QWidget* parent): QTableWidget (parent), showHidden_(showHidden) + QWidget* parent): QTreeWidget (parent), showHidden_(showHidden) { sensorsChanged(*(sensorStore.getSensors())); - connect(this, &QTableWidget::doubleClicked, this, &SensorListWidget::onDoubleClick); + connect(this, &QTreeWidget::itemDoubleClicked, this, &SensorListWidget::onDoubleClick); } -void SensorListWidget::onDoubleClick(const QModelIndex &index) +void SensorListWidget::onDoubleClick(QTreeWidgetItem *item, int column) { - if(index.isValid()) + if(item && item->type() == 1001) { - const Sensor& sensor = getSensorForIndex(index); + const Sensor& sensor = getSensorForIndex(currentIndex()); SensorSettingsDialog diag(sensor, this); if(diag.exec()) { @@ -47,56 +50,110 @@ void SensorListWidget::onDoubleClick(const QModelIndex &index) void SensorListWidget::sensorsChanged(std::vector sensors) { - clear(); - setHorizontalHeaderItem(0, new QTableWidgetItem("Sensor")); - setHorizontalHeaderItem(1, new QTableWidgetItem("Value")); - setHorizontalHeaderItem(2, new QTableWidgetItem("Time")); - size_t listLen = 0; - for(size_t i = 0; i < sensors.size(); ++i) - if(showHidden_ || !sensors[i].hidden) - ++listLen; - setRowCount(static_cast(listLen)); - size_t row = 0; - for(size_t i = 0; i < sensors.size(); ++i) + QMap expandedStates; + QList columnWidths; + + for(int i = 0; i < columnCount(); ++i) + columnWidths.append(columnWidth(i)); + + for(int i = 0; i < topLevelItemCount(); ++i) { - if(showHidden_ || !sensors[i].hidden) + QTreeWidgetItem* item = topLevelItem(i); + if(item->type() != 1001) { - QString itemString; - itemString.append(QString::number(sensors[i].field)); - itemString.append(' '); - - if(sensors[i].type == Sensor::TYPE_DOOR) - { - if(static_cast(sensors[i].field)) - itemString.append("\"Open\""); - else itemString.append("\"Closed\""); - } - else if(sensors[i].type == Sensor::TYPE_AUDIO_OUTPUT) - { - if(static_cast(sensors[i].field)) - itemString.append("\"Playing\""); - else itemString.append("\"Silent\""); - } - else if(!sensors[i].getUnit().isEmpty()) - { - itemString.append(" "); - itemString.append(sensors[i].getUnit()); - } - - setItem(static_cast(row), 0, new SensorListItem(sensors[i].name + (sensors[i].hidden ? " (H)" : ""), sensors[i])); - setItem(static_cast(row), 1, new QTableWidgetItem(itemString)); - if(sensors[i].type <= 128) - setItem(static_cast(row), 2, new QTableWidgetItem(sensors[i].lastSeen.time().toString("hh:mm"))); - ++row; + expandedStates[item->text(0)] = item->isExpanded(); } } + + clear(); + + QMap groupItems; + + QStringList headerLabels = {"Sensor", "Value", "Time"}; + setHeaderLabels(headerLabels); + + QList ungroupedItems; + + for(const Sensor& sensor : sensors) + { + if(!showHidden_ && sensor.hidden) + continue; + + QString itemString = QString::number(sensor.field); + + if(sensor.type == Sensor::TYPE_DOOR) + { + if(static_cast(sensor.field)) + itemString = "\"Open\""; + else + itemString = "\"Closed\""; + } + else if(sensor.type == Sensor::TYPE_AUDIO_OUTPUT) + { + if(static_cast(sensor.field)) + itemString = "\"Playing\""; + else + itemString = "\"Silent\""; + } + else if(!sensor.getUnit().isEmpty()) + { + itemString.append(" "); + itemString.append(sensor.getUnit()); + } + + SensorListItem* sensorItem = new SensorListItem( + sensor.name + (sensor.hidden ? " (H)" : ""), sensor); + sensorItem->setText(0, sensor.name + (sensor.hidden ? " (H)" : "")); + sensorItem->setText(1, itemString); + if(sensor.type <= 128) + sensorItem->setText(2, sensor.lastSeen.time().toString("hh:mm")); + + if(sensor.groupName.isEmpty()) + { + ungroupedItems.append(sensorItem); + } + else + { + QTreeWidgetItem* groupItem; + auto it = groupItems.find(sensor.groupName); + if(it == groupItems.end()) + { + groupItem = new QTreeWidgetItem(this); + groupItem->setText(0, sensor.groupName); + bool wasExpanded = expandedStates.value(sensor.groupName, false); + groupItem->setExpanded(wasExpanded); + groupItems[sensor.groupName] = groupItem; + } + else + { + groupItem = it.value(); + } + groupItem->addChild(sensorItem); + } + } + + for(SensorListItem* item : ungroupedItems) + { + addTopLevelItem(item); + } sortItems(0, Qt::AscendingOrder); - resizeColumnsToContents(); + + for(auto it = groupItems.begin(); it != groupItems.end(); ++it) + { + it.value()->sortChildren(0, Qt::AscendingOrder); + } + + for(int i = 0; i < columnCount() && i < columnWidths.size(); ++i) + setColumnWidth(i, columnWidths.at(i)); } const Sensor& SensorListWidget::getSensorForIndex(const QModelIndex &index) { - return static_cast(item(index.row(), 0))->getSensor(); + QTreeWidgetItem* item = itemFromIndex(index); + if(item && item->type() == 1001) + return static_cast(item)->getSensor(); + static Sensor dummy; + return dummy; } void SensorListWidget::setShowHidden(const bool showHidden) @@ -111,7 +168,8 @@ const Sensor& SensorListItem::getSensor() } SensorListItem::SensorListItem(const QString& text, const Sensor& sensor): - QTableWidgetItem(text, 1001), sensor(sensor) + QTreeWidgetItem(1001), sensor(sensor) { + setText(0, text); } diff --git a/src/ui/sensorlistwidget.h b/src/ui/sensorlistwidget.h index 7039ef7..a15bd03 100644 --- a/src/ui/sensorlistwidget.h +++ b/src/ui/sensorlistwidget.h @@ -1,9 +1,9 @@ #pragma once -#include +#include #include #include "sensors/sensor.h" -class SensorListItem : public QTableWidgetItem +class SensorListItem : public QTreeWidgetItem { Sensor sensor; @@ -12,7 +12,7 @@ public: SensorListItem(const QString& text, const Sensor& sensor); }; -class SensorListWidget : public QTableWidget +class SensorListWidget : public QTreeWidget { Q_OBJECT @@ -32,5 +32,5 @@ public slots: void sensorsChanged(std::vector sensors); private slots: - void onDoubleClick(const QModelIndex &index); + void onDoubleClick(QTreeWidgetItem *item, int column); };