From a17cd23a4eb5563db06a9fdcf96282f6f58c5b9c Mon Sep 17 00:00:00 2001 From: Carl Philipp Klemm Date: Wed, 1 Apr 2026 19:40:47 +0200 Subject: [PATCH] Support item tabs based on item group string --- CMakeLists.txt | 2 +- src/actors/actor.cpp | 10 +- src/actors/alarmtime.h | 2 +- src/actors/timeractor.cpp | 8 +- src/actors/timeractor.h | 8 +- src/items/fixeditemsource.cpp | 16 +++- src/items/item.cpp | 96 ++++++++++++++----- src/items/item.h | 70 +++++++++++--- src/items/itemloadersource.cpp | 10 +- src/items/itemsource.h | 2 +- src/items/itemstore.cpp | 36 ++++--- src/items/itemstore.h | 4 +- src/main.cpp | 11 +-- src/microcontroller.cpp | 19 +++- src/service/server.cpp | 13 ++- src/service/service.cpp | 2 - src/service/tcpclient.cpp | 28 +++++- src/ui/actorsettingsdialog.cpp | 5 +- src/ui/itemscrollbox.cpp | 169 ++++++++++++++++++++++++++++++--- src/ui/itemscrollbox.h | 23 ++++- src/ui/itemsettingsdialog.cpp | 66 ++++++++++--- src/ui/itemsettingsdialog.h | 4 +- src/ui/itemsettingsdialog.ui | 14 +++ src/ui/itemwidget.cpp | 24 +++-- src/ui/itemwidget.h | 4 +- src/ui/mainwindow.cpp | 11 ++- src/ui/mainwindow.h | 2 +- src/ui/mainwindow.ui | 2 +- src/ui/relayscrollbox.ui | 47 +-------- 29 files changed, 527 insertions(+), 181 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea99bbe..728bae0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 4.0) project(SHinterface VERSION 1.0 LANGUAGES CXX) # Set C++ standard -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Enable all warnings diff --git a/src/actors/actor.cpp b/src/actors/actor.cpp index 51c3c84..15a9919 100644 --- a/src/actors/actor.cpp +++ b/src/actors/actor.cpp @@ -24,6 +24,7 @@ void Actor::performValueAction(uint8_t value) ItemUpdateRequest request; request.type = ITEM_UPDATE_ACTOR; request.payload = ItemData(QRandomGenerator::global()->generate(), "Item", value); + request.changes.value = true; sigItemUpdate(request); } } @@ -47,9 +48,12 @@ void Actor::makeInactive() QString Actor::actionName() { QString string; - if(triggerValue == 0 ) string = "off"; - else if(triggerValue == 1 ) string = "on"; - else string = "value to " + QString::number(triggerValue); + if(triggerValue == 0 ) + string = "off"; + else if(triggerValue == 1 ) + string = "on"; + else + string = "value to " + QString::number(triggerValue); return string; } diff --git a/src/actors/alarmtime.h b/src/actors/alarmtime.h index 0d65d84..99c0b7e 100644 --- a/src/actors/alarmtime.h +++ b/src/actors/alarmtime.h @@ -39,13 +39,13 @@ public: virtual void load(const QJsonObject& json, const bool preserve = false); uint8_t getRepeat(); + virtual QString getName() const; public slots: void run(); virtual void makeActive(); virtual void makeInactive(); - virtual QString getName() const; void doTick(); void changeTime(const QDateTime& time); void setRepeat(const uint8_t repeat); diff --git a/src/actors/timeractor.cpp b/src/actors/timeractor.cpp index a0a6b1e..d916045 100644 --- a/src/actors/timeractor.cpp +++ b/src/actors/timeractor.cpp @@ -7,11 +7,13 @@ TimerActor::TimerActor(const int timeoutSec, QObject *parent): Actor(parent), ti timer.setSingleShot(true); } -void TimerActor::onValueChanged(uint8_t state) +void TimerActor::onItemUpdated(ItemUpdateRequest update) { - if((state && !triggerValue) || (!state && triggerValue)) + if(update.changes.value && ((update.payload.getValue() && !triggerValue) || (!update.payload.getValue() && triggerValue))) { - if(timer.isActive()) timer.stop(); + qDebug()<<"Timer started"; + if(timer.isActive()) + timer.stop(); timer.setInterval(timeoutMsec_); timer.start(); } diff --git a/src/actors/timeractor.h b/src/actors/timeractor.h index 36290d9..f30ff7c 100644 --- a/src/actors/timeractor.h +++ b/src/actors/timeractor.h @@ -16,15 +16,15 @@ private slots: public slots: - virtual void onValueChanged(uint8_t state); + virtual void onItemUpdated(ItemUpdateRequest update) override; void setTimeout(const int timeoutSec); public: explicit TimerActor(const int timeoutSec = 60, QObject *parent = nullptr); - virtual QString getName() const; + virtual QString getName() const override; int getTimeout(); - virtual void store(QJsonObject& json); - virtual void load(const QJsonObject& json, bool preserve); + virtual void store(QJsonObject& json) override; + virtual void load(const QJsonObject& json, bool preserve) override; }; diff --git a/src/items/fixeditemsource.cpp b/src/items/fixeditemsource.cpp index b4459ff..b166831 100644 --- a/src/items/fixeditemsource.cpp +++ b/src/items/fixeditemsource.cpp @@ -10,5 +10,19 @@ FixedItemSource::FixedItemSource(Microcontroller* micro, QObject *parent): void FixedItemSource::refresh() { - gotItems({powerItem, rgbItem, auxItem}, ITEM_UPDATE_BACKEND); + std::vector requests; + + ItemAddRequest request; + request.type = ITEM_UPDATE_BACKEND; + + request.payload = powerItem; + requests.push_back(request); + + request.payload = rgbItem; + requests.push_back(request); + + request.payload = auxItem; + requests.push_back(request); + + gotItems(requests); } diff --git a/src/items/item.cpp b/src/items/item.cpp index 98e2a1b..6f391c5 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -46,21 +46,49 @@ uint32_t ItemData::id() const void ItemData::store(QJsonObject &json) { - json["Name"] = name_; + storeWithChanges(json, ItemFieldChanges(true)); +} + +void ItemData::storeWithChanges(QJsonObject& json, const ItemFieldChanges& changes) +{ json["ItemId"] = static_cast(itemId_); - json["Value"] = static_cast(value_); - json["GroupName"] = groupName_; + json["ValueType"] = type_; + if(changes.name) + json["Name"] = name_; + if(changes.value) + json["Value"] = static_cast(value_); + if(changes.groupName) + json["GroupName"] = groupName_; } void ItemData::load(const QJsonObject &json, const bool preserve) { + loadWithChanges(json, preserve); +} + +ItemFieldChanges ItemData::loadWithChanges(const QJsonObject& json, const bool preserve) +{ + ItemFieldChanges changes; if(!preserve) { - name_ = json["Name"].toString(name_); + if(json.contains("Name")) + { + name_ = json["Name"].toString(); + changes.name = true; + } + if(json.contains("Value")) + { + value_ = json["Value"].toInt(); + changes.value = true; + } + if(json.contains("GroupName")) + { + groupName_ = json["GroupName"].toString(); + changes.groupName = true; + } itemId_ = static_cast(json["ItemId"].toDouble(0)); - value_ = json["Value"].toInt(); - groupName_ = json["GroupName"].toString("All"); } + return changes; } bool ItemData::getLoaded() const @@ -73,15 +101,23 @@ void ItemData::setLoaded(bool loaded) loaded_ = loaded; } -bool ItemData::hasChanged(const ItemData& other) +bool ItemData::hasChanged(const ItemData& other) const { - if(other != *this) - return false; - if(other.getName() != getName()) + ItemFieldChanges changes(true); + return hasChanged(other, changes); +} + +bool ItemData::hasChanged(const ItemData& other, const ItemFieldChanges& changes) const +{ + if(changes.name && other.getName() != getName()) return true; - if(other.getValue() != getValue()) + if(changes.value && other.getValue() != getValue()) return true; - if(other.getLoaded() != getLoaded()) + if(changes.hidden && other.isHidden() != isHidden()) + return true; + if(changes.groupName && other.getGroupName() != getGroupName()) + return true; + if(changes.actors) return true; return false; } @@ -143,7 +179,6 @@ void Item::store(QJsonObject &json) } } json["Actors"] = actorsArray; - json["ValueType"] = type_; } void Item::load(const QJsonObject &json, const bool preserve) @@ -175,7 +210,14 @@ Item& Item::operator=(const ItemData& other) void Item::requestUpdate(ItemUpdateRequest update) { assert(update.type != ITEM_UPDATE_INVALID); - if(update.type != ITEM_UPDATE_LOADED && value_ == update.payload.getValue()) + assert(!update.changes.isNone()); + + if(update.type == ITEM_UPDATE_LOADED) + { + qDebug()<<__func__<& actor : update.newActors) addActor(actor); @@ -302,14 +348,12 @@ std::shared_ptr Item::loadItem(const QJsonObject& json) return newItem; } -ItemUpdateRequest Item::createValueUpdateRequest(uint8_t value, - item_update_type_t type, +ItemUpdateRequest Item::createValueUpdateRequest(item_update_type_t type, bool withActors) { ItemUpdateRequest update; update.type = type; update.payload = *this; - update.payload.setValueData(value); if(withActors) update.newActors = actors_; return update; diff --git a/src/items/item.h b/src/items/item.h index 1156933..8cf9c4d 100644 --- a/src/items/item.h +++ b/src/items/item.h @@ -23,6 +23,9 @@ typedef enum { ITEM_UPDATE_INVALID } item_update_type_t; +struct ItemFieldChanges; +struct ItemUpdateRequest; + class ItemData { protected: @@ -41,7 +44,7 @@ public: bool loaded = false, bool hidden = false, item_value_type_t type = ITEM_VALUE_BOOL, - QString groupName = "All"); + QString groupName = ""); inline bool operator==(const ItemData& in) const { @@ -54,7 +57,8 @@ public: uint32_t id() const; - bool hasChanged(const ItemData& other); + bool hasChanged(const ItemData& other) const; + bool hasChanged(const ItemData& other, const ItemFieldChanges& changes) const; void setName(QString name); uint8_t getValue() const; void setValueData(uint8_t value); @@ -65,19 +69,13 @@ public: item_value_type_t getValueType(); QString getGroupName() const; void setGroupName(QString groupName); + void storeWithChanges(QJsonObject& json, const ItemFieldChanges& changes); + ItemFieldChanges loadWithChanges(const QJsonObject& json, const bool preserve = false); virtual QString getName() const; virtual void store(QJsonObject& json); virtual void load(const QJsonObject& json, const bool preserve = false); }; -struct ItemUpdateRequest -{ - item_update_type_t type = ITEM_UPDATE_INVALID; - ItemData payload; - std::vector > newActors; -}; - - class Item: public QObject, public ItemData { Q_OBJECT @@ -108,8 +106,7 @@ public: void setActorsActive(bool in); void setOverride(const bool in); bool getOverride(); - ItemUpdateRequest createValueUpdateRequest(uint8_t value, - item_update_type_t type, + ItemUpdateRequest createValueUpdateRequest(item_update_type_t type, bool withActors = false); virtual void store(QJsonObject& json); @@ -122,3 +119,52 @@ protected: }; + +struct ItemFieldChanges +{ + bool name :1; + bool value :1; + bool hidden :1; + bool type :1; + bool groupName :1; + bool actors :1; + ItemFieldChanges(bool defaultVal = false) + { + name = defaultVal; + value = defaultVal; + hidden = defaultVal; + type = defaultVal; + groupName = defaultVal; + actors = false; + } + inline bool isNone() const + { + return !name && !value && !hidden && !type && !groupName && !actors; + } +}; + +struct ItemUpdateRequest +{ + item_update_type_t type = ITEM_UPDATE_INVALID; + ItemData payload; + ItemFieldChanges changes; + std::vector > newActors; +}; + +struct ItemAddRequest +{ + item_update_type_t type = ITEM_UPDATE_INVALID; + std::shared_ptr payload; + ItemFieldChanges changes; + inline ItemUpdateRequest updateRequest() const + { + ItemUpdateRequest update; + update.payload = *payload; + update.type = type; + update.changes = changes; + if(changes.actors) + update.newActors = payload->getActors(); + return update; + } +}; + diff --git a/src/items/itemloadersource.cpp b/src/items/itemloadersource.cpp index a24e5a5..db5478a 100644 --- a/src/items/itemloadersource.cpp +++ b/src/items/itemloadersource.cpp @@ -10,7 +10,7 @@ ItemLoaderSource::ItemLoaderSource(const QJsonObject& json, QObject *parent): void ItemLoaderSource::refresh() { - std::vector> items; + std::vector itemAddRequests; const QJsonArray itemsArray(json["Items"].toArray()); for(int i = 0; i < itemsArray.size(); ++i) { @@ -21,11 +21,15 @@ void ItemLoaderSource::refresh() std::shared_ptr newItem = Item::loadItem(itemObject); if(newItem) { - items.push_back(newItem); qDebug()<<"Loaded item"<getName(); + ItemAddRequest request; + request.type = ITEM_UPDATE_LOADED; + request.payload = newItem; + request.changes = ItemFieldChanges(true); + itemAddRequests.push_back(request); } } - gotItems(items, ITEM_UPDATE_LOADED); + gotItems(itemAddRequests); } void ItemLoaderSource::updateJson(const QJsonObject& json) diff --git a/src/items/itemsource.h b/src/items/itemsource.h index 5c2bba2..1753f75 100644 --- a/src/items/itemsource.h +++ b/src/items/itemsource.h @@ -17,7 +17,7 @@ public slots: virtual void refresh() = 0; signals: - void gotItems(std::vector> items, item_update_type_t updateType); + void gotItems(std::vector items); void requestReplaceItems(std::vector> items); void updateItems(std::vector updates); }; diff --git a/src/items/itemstore.cpp b/src/items/itemstore.cpp index 232d319..1b52bf9 100644 --- a/src/items/itemstore.cpp +++ b/src/items/itemstore.cpp @@ -6,39 +6,38 @@ ItemStore::ItemStore(QObject *parent): QObject(parent) { } -void ItemStore::addItem(const std::shared_ptr& item, item_update_type_t updateType) +void ItemStore::addItem(const ItemAddRequest& item) { + qDebug()<<"Item add request for"<getName()<id(); std::shared_ptr matched = nullptr; for(unsigned i = 0; i < items_.size(); i++ ) { - if(*items_[i] == *item) + if(*items_[i] == *item.payload) { matched = items_[i]; + assert(matched->id() == items_[i]->id()); break; } } if(!matched) { - items_.push_back(std::shared_ptr(item)); - connect(item.get(), &Item::updated, this, &ItemStore::itemUpdateSlot); - qDebug()<<"Item"<getName()<<"added"<<(item->getLoaded() ? "from loaded" : ""); + items_.push_back(item.payload); + connect(item.payload.get(), &Item::updated, this, &ItemStore::itemUpdateSlot); + qDebug()<<"Item"<getName()<<"added"<<(item.payload->getLoaded() ? "from loaded" : ""); itemAdded(std::weak_ptr(items_.back())); } - else + else if(!item.changes.isNone()) { - ItemUpdateRequest request = item->createValueUpdateRequest(item->getValue(), - updateType, - updateType == ITEM_UPDATE_LOADED); - request.newActors = item->getActors(); + qDebug()<<"Item"<getName()<<"was matched with"<getName()<<"and has changes"; + ItemUpdateRequest request = item.updateRequest(); updateItem(request); } } -void ItemStore::addItems(const std::vector>& itemIn, - item_update_type_t updateType) +void ItemStore::addItems(const std::vector& itemIn) { for(unsigned j = 0; j < itemIn.size(); j++) - addItem(itemIn[j], updateType); + addItem(itemIn[j]); } void ItemStore::removeItem(const ItemData& item) @@ -57,8 +56,15 @@ void ItemStore::removeItem(const ItemData& item) void ItemStore::replaceItems(const std::vector>& items) { - qDebug()<<__func__; - addItems(items, ITEM_UPDATE_LOADED); + for(const std::shared_ptr& item : items) + { + ItemAddRequest request; + request.changes = ItemFieldChanges(true); + request.changes.actors = true; + request.type = ITEM_UPDATE_LOADED; + request.payload = item; + addItem(request); + } std::vector deletedItems; for(std::shared_ptr item : items_) { diff --git a/src/items/itemstore.h b/src/items/itemstore.h index b4bdeaa..93d1b9f 100644 --- a/src/items/itemstore.h +++ b/src/items/itemstore.h @@ -39,8 +39,8 @@ signals: public slots: void removeItem(const ItemData& item); - void addItem(const std::shared_ptr& item, item_update_type_t updateType); - void addItems(const std::vector>& itemsIn, item_update_type_t updateType); + void addItem(const ItemAddRequest& item); + void addItems(const std::vector& itemsIn); void replaceItems(const std::vector>& items); void updateItems(const std::vector& updates); void updateItem(const ItemUpdateRequest& update); diff --git a/src/main.cpp b/src/main.cpp index e3a6e03..722f6ed 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -115,12 +115,7 @@ int main(int argc, char *argv[]) QObject::connect(&mainObject.micro, SIGNAL(textRecived(QString)), w, SLOT(changeHeaderLableText(QString))); QObject::connect(w, &MainWindow::sigSetRgb, &mainObject.micro, &Microcontroller::changeRgbColor); QObject::connect(w, &MainWindow::sigSave, &mainObject, [&mainObject, settingsPath](){mainObject.storeToDisk(settingsPath);}); - QObject::connect(w, - &MainWindow::createdItem, - &globalItems, - [](std::shared_ptr item) { - globalItems.addItem(item, ITEM_UPDATE_USER); - }); + QObject::connect(w, &MainWindow::createdItem, &globalItems, &ItemStore::addItem); w->show(); } retVal = a.exec(); @@ -132,9 +127,7 @@ int main(int argc, char *argv[]) { SecondaryMainObject mainObject(parser.value(hostOption), parser.value(portOption).toInt()); MainWindow w(&mainObject); - QObject::connect(&w, &MainWindow::createdItem, &globalItems, [](std::shared_ptr item) { - globalItems.addItem(item, ITEM_UPDATE_USER); - }); + QObject::connect(&w, &MainWindow::createdItem, &globalItems, &ItemStore::addItem); QObject::connect(&w, &MainWindow::sigSave, mainObject.tcpClient, &TcpClient::sendItems); w.show(); diff --git a/src/microcontroller.cpp b/src/microcontroller.cpp index 0bf8690..8005278 100644 --- a/src/microcontroller.cpp +++ b/src/microcontroller.cpp @@ -157,16 +157,31 @@ void Microcontroller::processList(const QString& buffer) else if(buffer.contains("EOL")) { listMode = false; - gotItems(relayList, ITEM_UPDATE_BACKEND); + std::vector requests; + for(const std::shared_ptr& item : relayList) + { + ItemAddRequest request; + request.changes.name = true; + request.changes.value = true; + request.payload = item; + request.type = ITEM_UPDATE_BACKEND; + requests.push_back(request); + } + gotItems(requests); relayList.clear(); } - else listMode = false; + else + { + listMode = false; + } } void Microcontroller::processRelayState(const QString& buffer) { ItemUpdateRequest update; update.type = ITEM_UPDATE_BACKEND; + update.changes.name = true; + update.changes.value = true; update.payload = static_cast(*processRelayLine(buffer)); updateItems({update}); } diff --git a/src/service/server.cpp b/src/service/server.cpp index 8d900a2..13fc7f3 100644 --- a/src/service/server.cpp +++ b/src/service/server.cpp @@ -25,6 +25,7 @@ void Server::processIncomeingJson(const QByteArray& jsonbytes) QJsonArray data = json["Data"].toArray(); bool FullList = json["FullList"].toBool(false); std::vector> items; + std::vector fieldChanges; for(QJsonValueRef itemjson : data) { QJsonObject jsonobject = itemjson.toObject(); @@ -33,6 +34,7 @@ void Server::processIncomeingJson(const QByteArray& jsonbytes) { qDebug()<<"Server got item"<getName(); item->setLoaded(FullList); + fieldChanges.push_back(item->loadWithChanges(jsonobject)); items.push_back(item); } } @@ -43,7 +45,16 @@ void Server::processIncomeingJson(const QByteArray& jsonbytes) } else if(!items.empty()) { - gotItems(items, ITEM_UPDATE_REMOTE); + std::vector updates; + for(size_t i = 0; i < items.size(); i++) + { + ItemUpdateRequest request; + request.payload = *items[i]; + request.changes = fieldChanges[i]; + request.type = ITEM_UPDATE_REMOTE; + updates.push_back(request); + } + updateItems(updates); } } else diff --git a/src/service/service.cpp b/src/service/service.cpp index ae75b7e..a43a313 100644 --- a/src/service/service.cpp +++ b/src/service/service.cpp @@ -63,7 +63,6 @@ void Service::sendItems() sendJson(json); } - void Service::processIncomeingJson(const QByteArray& jsonbytes) { QJsonDocument doc = QJsonDocument::fromJson(jsonbytes); @@ -82,7 +81,6 @@ void Service::processIncomeingJson(const QByteArray& jsonbytes) else if(type == "SensorUpdate") { QJsonArray data = json["Data"].toArray(); - qWarning()<<"Got sensor update with no/empty sensors array"; for(QJsonValueRef sensorjson : data) { QJsonObject jsonobject = sensorjson.toObject(); diff --git a/src/service/tcpclient.cpp b/src/service/tcpclient.cpp index 63729d1..afcb473 100644 --- a/src/service/tcpclient.cpp +++ b/src/service/tcpclient.cpp @@ -34,20 +34,40 @@ void TcpClient::processIncomeingJson(const QByteArray& jsonbytes) if(type == "ItemUpdate") { QJsonArray data = json["Data"].toArray(); + bool FullList = json["FullList"].toBool(false); std::vector> items; + std::vector fieldChanges; for(QJsonValueRef itemjson : data) { QJsonObject jsonobject = itemjson.toObject(); std::shared_ptr item = Item::loadItem(jsonobject); if(item) { - qDebug()<<"Client got item"<getName(); - item->setLoaded(false); + item->setLoaded(FullList); + fieldChanges.push_back(item->loadWithChanges(jsonobject)); items.push_back(item); } } - if(!items.empty()) - gotItems(items, ITEM_UPDATE_REMOTE); + if(FullList && !items.empty()) + { + qDebug()<<"Client replaceing items"; + requestReplaceItems(items); + } + else if(!items.empty()) + { + std::vector itemAddRequests; + qDebug()<<"Client updateing items"; + for(size_t i = 0; i < items.size(); i++) + { + ItemAddRequest request; + request.type = ITEM_UPDATE_REMOTE; + request.payload = items[i]; + request.changes = fieldChanges[i]; + itemAddRequests.push_back(request); + qDebug()<<"Payload"<id(); + } + gotItems(itemAddRequests); + } } else { diff --git a/src/ui/actorsettingsdialog.cpp b/src/ui/actorsettingsdialog.cpp index f89a1a0..ccd17e5 100644 --- a/src/ui/actorsettingsdialog.cpp +++ b/src/ui/actorsettingsdialog.cpp @@ -106,8 +106,11 @@ ActorSettingsDialog::~ActorSettingsDialog() void ActorSettingsDialog::editAsItem() { - ItemSettingsDialog itemSettingsDiag(actor_, this); + setModal(false); + ItemSettingsDialog itemSettingsDiag(actor_, false, this); + itemSettingsDiag.setModal(false); itemSettingsDiag.exec(); + setModal(true); } void ActorSettingsDialog::setEnabled() diff --git a/src/ui/itemscrollbox.cpp b/src/ui/itemscrollbox.cpp index 170a557..55ba900 100644 --- a/src/ui/itemscrollbox.cpp +++ b/src/ui/itemscrollbox.cpp @@ -1,15 +1,16 @@ #include "itemscrollbox.h" #include "ui_relayscrollbox.h" -#include "../items/auxitem.h" -#include "../items/messageitem.h" +#include +#include ItemScrollBox::ItemScrollBox(QWidget *parent) : QWidget(parent), ui(new Ui::RelayScrollBox) { ui->setupUi(this); - QScroller::grabGesture(ui->scrollArea, QScroller::TouchGesture); - QScroller::grabGesture(ui->scrollArea, QScroller::LeftMouseButtonGesture); + + ensureTabExists("All"); + ui->tabWidget->setCurrentIndex(0); } ItemScrollBox::~ItemScrollBox() @@ -23,24 +24,166 @@ void ItemScrollBox::addItem(std::weak_ptr item) { if(workItem->isHidden()) return; - widgets_.push_back(new ItemWidget(item)); - ui->relayWidgetVbox->addWidget(widgets_.back()); - connect(widgets_.back(), &ItemWidget::deleteRequest, this, &ItemScrollBox::deleteRequest); - connect(widgets_.back(), &ItemWidget::deleteRequest, this, &ItemScrollBox::removeItem); + + // Add to "All" tab + widgets_["All"].push_back(new ItemWidget(item, false)); + QWidget* allScrollContent = tabs_["All"].content; + QLayout* layout = allScrollContent->layout(); + layout->removeItem(tabs_["All"].spacer); + layout->addWidget(widgets_["All"].back()); + layout->addItem(tabs_["All"].spacer); + connect(widgets_["All"].back(), &ItemWidget::deleteRequest, this, &ItemScrollBox::deleteRequest); + connect(widgets_["All"].back(), &ItemWidget::deleteRequest, this, &ItemScrollBox::removeItem); + + addItemToTabs(item); + } +} + +void ItemScrollBox::addItemToTabs(std::weak_ptr item) +{ + if(auto workItem = item.lock()) + { + if(workItem->isHidden()) + return; + + QString groupName = workItem->getGroupName(); + if(groupName.isEmpty() || groupName != "All") + { + ensureTabExists(groupName); + ItemWidget* groupWidget = new ItemWidget(item, true); + widgets_[groupName].push_back(groupWidget); + + QWidget* scrollContent = tabs_[groupName].content; + QLayout* groupLayout = scrollContent->layout(); + groupLayout->removeItem(tabs_[groupName].spacer); + groupLayout->addWidget(groupWidget); + groupLayout->addItem(tabs_[groupName].spacer); + + connect(groupWidget, &ItemWidget::deleteRequest, this, &ItemScrollBox::deleteRequest); + connect(groupWidget, &ItemWidget::deleteRequest, this, &ItemScrollBox::removeItem); + + connect(widgets_[groupName].back(), &ItemWidget::deleteRequest, this, &ItemScrollBox::deleteRequest); + connect(widgets_[groupName].back(), &ItemWidget::deleteRequest, this, &ItemScrollBox::removeItem); + } } } void ItemScrollBox::removeItem(const ItemData& item) { - for(unsigned i = 0; i < widgets_.size(); i++) + QString key = "All"; + std::vector& widgets = widgets_[key]; + for(unsigned i = 0; i < widgets.size(); i++) { - if(widgets_[i]->controles(item)) + if(widgets[i]->controles(item)) { - ui->relayWidgetVbox->removeWidget(widgets_[i]); - delete widgets_[i]; - widgets_.erase(widgets_.begin()+i); + QWidget* tabContent = tabs_[key].content; + if(tabContent) + { + QLayout* layout = tabContent->layout(); + if(layout) + layout->removeWidget(widgets[i]); + } + delete widgets[i]; + widgets.erase(widgets.begin()+i); + break; + } + } + + removeItemFromTabs(item); + cleanupEmptyTabs(); +} + +void ItemScrollBox::removeItemFromTabs(const ItemData& item) +{ + for(const QString& key : widgets_.keys()) + { + if(key == "All") + continue; + std::vector& widgets = widgets_[key]; + for(unsigned i = 0; i < widgets.size(); i++) + { + if(widgets[i]->controles(item)) + { + QWidget* tabContent = tabs_[key].content; + if(tabContent) + { + QLayout* layout = tabContent->layout(); + if(layout) + layout->removeWidget(widgets[i]); + } + delete widgets[i]; + widgets.erase(widgets.begin()+i); + break; + } + } + } + + cleanupEmptyTabs(); +} + + +void ItemScrollBox::onItemUpdate(const ItemUpdateRequest& update) +{ + if(!update.changes.groupName) + return; + + for(ItemWidget* widget : widgets_["All"]) + { + if(widget->controles(update.payload)) + { + qDebug()<<"ItemUpdate with group change"; + std::weak_ptr item = widget->getItem(); + removeItemFromTabs(update.payload); + addItemToTabs(item); } } } +void ItemScrollBox::ensureTabExists(const QString& groupName) +{ + if(!tabs_.contains(groupName)) + { + Tab tab; + tab.scroller = new QScrollArea(ui->tabWidget); + tab.scroller->setWidgetResizable(true); + tab.scroller->setFrameShape(QFrame::NoFrame); + tab.scroller->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + tab.content = new QWidget(tab.scroller); + QVBoxLayout* scrollLayout = new QVBoxLayout(tab.content); + scrollLayout->setContentsMargins(0, 0, 0, 0); + tab.content->setLayout(scrollLayout); + tab.content->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + tab.scroller->setWidget(tab.content); + + tab.spacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding); + scrollLayout->addSpacerItem(tab.spacer); + + ui->tabWidget->addTab(tab.scroller, groupName); + tabs_[groupName] = tab; + } +} + +void ItemScrollBox::cleanupEmptyTabs() +{ + for(auto it = tabs_.begin(); it != tabs_.end(); ++it) + { + QString groupName = it.key(); + if(groupName == "All") + continue; + + qDebug()<<__func__<layout()->count(); + + if(it.value().content->layout()->count() <= 1) + { + int index = ui->tabWidget->indexOf(tabs_[groupName].scroller); + if(index >= 0) + ui->tabWidget->removeTab(index); + Tab tab = tabs_.take(groupName); + delete tab.content; + delete tab.scroller; + cleanupEmptyTabs(); + break; + } + } +} diff --git a/src/ui/itemscrollbox.h b/src/ui/itemscrollbox.h index 54d5fe8..4292aa0 100644 --- a/src/ui/itemscrollbox.h +++ b/src/ui/itemscrollbox.h @@ -4,9 +4,9 @@ #include #include #include -#include +#include +#include #include "itemwidget.h" -#include "../items/relay.h" #include "../items/item.h" #include "../items/itemstore.h" @@ -20,7 +20,16 @@ class ItemScrollBox : public QWidget { Q_OBJECT private: - std::vector< ItemWidget* > widgets_; + struct Tab + { + QScrollArea* scroller; + QWidget* content; + QSpacerItem* spacer; + }; + + QMap tabs_; + QMap> widgets_; + Ui::RelayScrollBox *ui; signals: void deleteRequest(const ItemData& item); @@ -36,9 +45,15 @@ public slots: void addItem(std::weak_ptr item); void removeItem(const ItemData& item); + void onItemUpdate(const ItemUpdateRequest& update); private: - Ui::RelayScrollBox *ui; + void ensureTabExists(const QString& groupName); + void cleanupEmptyTabs(); + +private: + void addItemToTabs(std::weak_ptr item); + void removeItemFromTabs(const ItemData& item); }; #endif // RELAYSCROLLBOX_H diff --git a/src/ui/itemsettingsdialog.cpp b/src/ui/itemsettingsdialog.cpp index 08417d3..aa60f01 100644 --- a/src/ui/itemsettingsdialog.cpp +++ b/src/ui/itemsettingsdialog.cpp @@ -1,5 +1,6 @@ #include "itemsettingsdialog.h" #include "ui_itemsettingsdialog.h" +#include "../items/itemstore.h" #include "actorsettingsdialog.h" #include "../actors/alarmtime.h" #include "../actors/sensoractor.h" @@ -13,7 +14,7 @@ #include "itemsettingswidgets/relayitemsettingswidget.h" #include -ItemSettingsDialog::ItemSettingsDialog(std::shared_ptr item, QWidget *parent) : +ItemSettingsDialog::ItemSettingsDialog(std::shared_ptr item, bool noGroup, QWidget *parent) : QDialog(parent), item_(item), ui(new Ui::ItemSettingsDialog) @@ -22,9 +23,17 @@ ItemSettingsDialog::ItemSettingsDialog(std::shared_ptr item, QWidget *pare setModal(false); + if(noGroup) + ui->comboBox_Group->setEnabled(false); + ui->label_name->setText(item_->getName()); ui->checkBox_Override->setChecked(item_->getOverride()); - + + // Setup group combobox with editable mode for creating new groups + ui->comboBox_Group->setEditable(true); + ui->comboBox_Group->addItem("All"); + ui->comboBox_Group->addItems(getExistingGroups()); + ui->comboBox_Group->setCurrentText(item_->getGroupName()); if(std::shared_ptr relay = std::dynamic_pointer_cast(item_)) { @@ -48,6 +57,7 @@ ItemSettingsDialog::ItemSettingsDialog(std::shared_ptr item, QWidget *pare connect(ui->pushButton_remove, &QPushButton::clicked, this, &ItemSettingsDialog::removeActor); connect(ui->pushButton_edit, &QPushButton::clicked, this, &ItemSettingsDialog::editActor); connect(ui->checkBox_Override, &QPushButton::clicked, this, &ItemSettingsDialog::changeOverride); + connect(ui->comboBox_Group, &QComboBox::currentTextChanged, this, &ItemSettingsDialog::changeGroup); ui->tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem("Actor")); @@ -61,10 +71,23 @@ ItemSettingsDialog::ItemSettingsDialog(std::shared_ptr item, QWidget *pare ItemSettingsDialog::~ItemSettingsDialog() { - if(itemSpecificWidget_) delete itemSpecificWidget_; + if(itemSpecificWidget_) + delete itemSpecificWidget_; delete ui; } +void ItemSettingsDialog::changeGroup() +{ + QString newGroup = ui->comboBox_Group->currentText(); + if(newGroup != item_->getGroupName()) + { + ItemUpdateRequest update = item_->createValueUpdateRequest(ITEM_UPDATE_USER); + update.payload.setGroupName(newGroup); + update.changes.groupName = true; + item_->requestUpdate(update); + } +} + void ItemSettingsDialog::changeOverride() { item_->setOverride(ui->checkBox_Override->isChecked()); @@ -166,14 +189,21 @@ void ItemSettingsDialog::editActor() ActorSettingsDialog* dialog; - if(alarmTime) dialog = new ActorSettingsDialog(alarmTime, this); - else if(regulator) dialog = new ActorSettingsDialog(regulator, this); - else if(sensorActor) dialog = new ActorSettingsDialog(sensorActor, this); - else if(timerActor) dialog = new ActorSettingsDialog(timerActor, this); - else if(polynomalActor) dialog = new ActorSettingsDialog(polynomalActor, this); - else if(factorActor) dialog = new ActorSettingsDialog(factorActor, this); - else dialog = new ActorSettingsDialog(actor, this); - dialog->setParent(this); + if(alarmTime) + dialog = new ActorSettingsDialog(alarmTime, this); + else if(regulator) + dialog = new ActorSettingsDialog(regulator, this); + else if(sensorActor) + dialog = new ActorSettingsDialog(sensorActor, this); + else if(timerActor) + dialog = new ActorSettingsDialog(timerActor, this); + else if(polynomalActor) + dialog = new ActorSettingsDialog(polynomalActor, this); + else if(factorActor) + dialog = new ActorSettingsDialog(factorActor, this); + else + dialog = new ActorSettingsDialog(actor, this); + dialog->show(); dialog->exec(); @@ -183,5 +213,19 @@ void ItemSettingsDialog::editActor() ui->tableWidget->item(i, 1)->setText(item_->getActors()[i]->actionName()); ui->tableWidget->item(i, 2)->setText(item_->getActors()[i]->isActive() ? "Y" : "N"); } + delete dialog; } } + +QStringList ItemSettingsDialog::getExistingGroups() +{ + QSet uniqueGroups; + for(const auto& item : *globalItems.getItems()) + { + if(!item->getGroupName().isEmpty() && item->getGroupName() != "All") + { + uniqueGroups.insert(item->getGroupName()); + } + } + return uniqueGroups.values(); +} diff --git a/src/ui/itemsettingsdialog.h b/src/ui/itemsettingsdialog.h index dc2a5af..606261c 100644 --- a/src/ui/itemsettingsdialog.h +++ b/src/ui/itemsettingsdialog.h @@ -19,9 +19,10 @@ class ItemSettingsDialog : public QDialog private: void loadActorList(); + QStringList getExistingGroups(); public: - explicit ItemSettingsDialog(std::shared_ptr item, QWidget *parent = nullptr); + explicit ItemSettingsDialog(std::shared_ptr item, bool noGroup = false, QWidget *parent = nullptr); ~ItemSettingsDialog(); private slots: @@ -30,6 +31,7 @@ private slots: void addActor(); void editActor(); void changeOverride(); + void changeGroup(); private: Ui::ItemSettingsDialog *ui; diff --git a/src/ui/itemsettingsdialog.ui b/src/ui/itemsettingsdialog.ui index 7faeede..c7f4113 100644 --- a/src/ui/itemsettingsdialog.ui +++ b/src/ui/itemsettingsdialog.ui @@ -63,6 +63,20 @@ + + + + + + Group: + + + + + + + + diff --git a/src/ui/itemwidget.cpp b/src/ui/itemwidget.cpp index c91c5e8..de7544e 100644 --- a/src/ui/itemwidget.cpp +++ b/src/ui/itemwidget.cpp @@ -4,10 +4,12 @@ #include #include #include +#include "itemsettingsdialog.h" -ItemWidget::ItemWidget(std::weak_ptr item, QWidget *parent) : +ItemWidget::ItemWidget(std::weak_ptr item, bool noGroupEdit, QWidget *parent) : QWidget(parent), item_(item), + noGroupEdit_(noGroupEdit), ui(new Ui::ItemWidget) { ui->setupUi(this); @@ -40,12 +42,13 @@ ItemWidget::ItemWidget(std::weak_ptr item, QWidget *parent) : connect(ui->pushButton, &QPushButton::clicked, this, &ItemWidget::showSettingsDialog); connect(workingItem.get(), &Item::updated, this, &ItemWidget::onItemUpdated); connect(ui->pushButton_Remove, &QPushButton::clicked, this, &ItemWidget::deleteItem); - } else { disable(); } + + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); } void ItemWidget::deleteItem() @@ -60,7 +63,9 @@ void ItemWidget::moveToValue(int value) { if(auto workingItem = item_.lock()) { - ItemUpdateRequest request = workingItem->createValueUpdateRequest(value, ITEM_UPDATE_USER); + ItemUpdateRequest request = workingItem->createValueUpdateRequest(ITEM_UPDATE_USER); + request.payload.setValueData(value); + request.changes.value = true; workingItem->requestUpdate(request); } else @@ -71,15 +76,7 @@ void ItemWidget::moveToValue(int value) void ItemWidget::moveToState(bool state) { - if(auto workingItem = item_.lock()) - { - ItemUpdateRequest request = workingItem->createValueUpdateRequest(state, ITEM_UPDATE_USER); - workingItem->requestUpdate(request); - } - else - { - disable(); - } + moveToValue(state); } void ItemWidget::disable() @@ -104,8 +101,9 @@ void ItemWidget::showSettingsDialog() { if(auto workingItem = item_.lock()) { - ItemSettingsDialog dialog(workingItem, this); + ItemSettingsDialog dialog(workingItem, noGroupEdit_, this); dialog.exec(); + } else disable(); } diff --git a/src/ui/itemwidget.h b/src/ui/itemwidget.h index 90d5a97..51c642f 100644 --- a/src/ui/itemwidget.h +++ b/src/ui/itemwidget.h @@ -3,7 +3,6 @@ #include #include -#include "itemsettingsdialog.h" #include "../items/item.h" namespace Ui @@ -16,6 +15,7 @@ class ItemWidget : public QWidget Q_OBJECT private: std::weak_ptr item_; + bool noGroupEdit_; void disable(); @@ -30,7 +30,7 @@ private slots: void deleteItem(); public: - explicit ItemWidget(std::weak_ptr item, QWidget *parent = nullptr); + explicit ItemWidget(std::weak_ptr item, bool noGroupEdit = false, QWidget *parent = nullptr); std::weak_ptr getItem(); bool controles(const ItemData& relay); ~ItemWidget(); diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index 79a6b3e..369d3e9 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -24,6 +24,7 @@ MainWindow::MainWindow(MainObject * const mainObject, QWidget *parent) : connect(ui->pushButton_refesh, &QPushButton::clicked, mainObject, &MainObject::refresh); connect(&globalItems, &ItemStore::itemAdded, ui->relayList, &ItemScrollBox::addItem); connect(&globalItems, &ItemStore::itemDeleted, ui->relayList, &ItemScrollBox::removeItem); + connect(&globalItems, &ItemStore::itemUpdated, ui->relayList, &ItemScrollBox::onItemUpdate); for(size_t i = 0; i < globalItems.getItems()->size(); ++i) ui->relayList->addItem(globalItems.getItems()->at(i)); @@ -65,7 +66,7 @@ void MainWindow::showPowerItemDialog() } if(powerItem) { - ItemSettingsDialog diag(std::shared_ptr(powerItem), this); + ItemSettingsDialog diag(std::shared_ptr(powerItem), false, this); diag.show(); diag.exec(); } @@ -79,7 +80,13 @@ void MainWindow::showItemCreationDialog() ItemCreationDialog diag(this); diag.show(); if(diag.exec()) - createdItem(diag.item); + { + ItemAddRequest request; + request.type = ITEM_UPDATE_USER; + request.changes = ItemFieldChanges(true); + request.payload = diag.item; + createdItem(request); + } } void MainWindow::changeHeaderLableText(QString string) diff --git a/src/ui/mainwindow.h b/src/ui/mainwindow.h index 5b04b64..efd9709 100644 --- a/src/ui/mainwindow.h +++ b/src/ui/mainwindow.h @@ -32,7 +32,7 @@ private: signals: void sigSave(); - void createdItem(std::shared_ptr item); + void createdItem(ItemAddRequest request); void sigSetRgb(const QColor color); private slots: diff --git a/src/ui/mainwindow.ui b/src/ui/mainwindow.ui index baa31c8..422862d 100644 --- a/src/ui/mainwindow.ui +++ b/src/ui/mainwindow.ui @@ -94,7 +94,7 @@ - 400 + 350 0 diff --git a/src/ui/relayscrollbox.ui b/src/ui/relayscrollbox.ui index cb0fae2..7ef7dc2 100644 --- a/src/ui/relayscrollbox.ui +++ b/src/ui/relayscrollbox.ui @@ -13,52 +13,15 @@ Form - + - - - QFrame::NoFrame + + + false - - 0 - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOff - - + true - - - - 0 - 0 - 388 - 288 - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - -