From 086376086c7e1cd09a0304a28fecab277ea6ab29 Mon Sep 17 00:00:00 2001 From: Spiros Date: Wed, 23 Apr 2025 14:35:54 +0300 Subject: [PATCH 01/15] Mockup grid view --- dist/status/Disk_Bad.svg | 250 ++++++++++++++++++++++++++++++++++ dist/status/Disk_Good.svg | 251 ++++++++++++++++++++++++++++++++++ dist/status/Disk_Unknown.svg | 252 +++++++++++++++++++++++++++++++++++ dist/status/Disk_Warning.svg | 252 +++++++++++++++++++++++++++++++++++ include/gridview.h | 35 +++++ include/mainwindow.h | 2 + src/gridview.cpp | 146 ++++++++++++++++++++ src/mainwindow.cpp | 18 ++- src/mainwindow.ui | 10 ++ src/resources.qrc | 4 + src/utils.cpp | 15 ++- 11 files changed, 1232 insertions(+), 3 deletions(-) create mode 100644 dist/status/Disk_Bad.svg create mode 100644 dist/status/Disk_Good.svg create mode 100644 dist/status/Disk_Unknown.svg create mode 100644 dist/status/Disk_Warning.svg create mode 100644 include/gridview.h create mode 100644 src/gridview.cpp diff --git a/dist/status/Disk_Bad.svg b/dist/status/Disk_Bad.svg new file mode 100644 index 0000000..ef0cbb1 --- /dev/null +++ b/dist/status/Disk_Bad.svg @@ -0,0 +1,250 @@ + + + + diff --git a/dist/status/Disk_Good.svg b/dist/status/Disk_Good.svg new file mode 100644 index 0000000..3130366 --- /dev/null +++ b/dist/status/Disk_Good.svg @@ -0,0 +1,251 @@ + + diff --git a/dist/status/Disk_Unknown.svg b/dist/status/Disk_Unknown.svg new file mode 100644 index 0000000..16c29bd --- /dev/null +++ b/dist/status/Disk_Unknown.svg @@ -0,0 +1,252 @@ + + diff --git a/dist/status/Disk_Warning.svg b/dist/status/Disk_Warning.svg new file mode 100644 index 0000000..7fe1d31 --- /dev/null +++ b/dist/status/Disk_Warning.svg @@ -0,0 +1,252 @@ + + diff --git a/include/gridview.h b/include/gridview.h new file mode 100644 index 0000000..aa83d50 --- /dev/null +++ b/include/gridview.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include + +class GridView : public QWidget { + Q_OBJECT + +public: + explicit GridView(QWidget *parent = nullptr); + +protected: + void resizeEvent(QResizeEvent *) override; + +private: + struct DiskItem { + QString name; + QString category; + QString icon; + }; + + QString searchQuery; + QScrollArea *scrollArea; + QWidget *gridContainer; + QGridLayout *gridLayout; + QPushButton *selectedButton; + QString bgColor, borderColor, hoverColor, selectedColor; + + QList disks; + + void extracted(const QVector &filteredApps, int &cols, int &row, int &col); + void populateGrid(); +}; diff --git a/include/mainwindow.h b/include/mainwindow.h index b25ba71..1d87d15 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -46,6 +46,8 @@ private slots: void on_actionASCII_View_triggered(); + void on_actionGrid_View_triggered(); + private: Ui::MainWindow *ui; QSettings settings; diff --git a/src/gridview.cpp b/src/gridview.cpp new file mode 100644 index 0000000..f68481d --- /dev/null +++ b/src/gridview.cpp @@ -0,0 +1,146 @@ +#include "gridview.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int gridLayoutSpacing = 10; +int iconButtonSize = 64; + +GridView::GridView(QWidget *parent) : QWidget(parent) { + setWindowTitle(tr("Grid View")); + resize(600, 300); + + QVBoxLayout *mainLayout = new QVBoxLayout(this); + + QComboBox *searchField = new QComboBox(); + searchField->setEditable(true); + connect(searchField->lineEdit(), &QLineEdit::textChanged, this, [this](const QString &text) { + this->searchQuery = text; + populateGrid(); + }); + mainLayout->addWidget(searchField); + + QPalette palette = this->palette(); + bgColor = palette.color(QPalette::Base).name(); + borderColor = palette.color(QPalette::Mid).name(); + hoverColor = palette.color(QPalette::Highlight).name(); + selectedColor = palette.color(QPalette::Highlight).name(); + + QFrame *gridFrame = new QFrame(); + gridFrame->setStyleSheet(QString( + "QFrame { background-color: %1; border-radius: 5px; }" + ).arg(bgColor, borderColor)); + + scrollArea = new QScrollArea(); + scrollArea->setWidgetResizable(true); + scrollArea->setFrameStyle(QFrame::NoFrame); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea->setWidget(gridFrame); + mainLayout->addWidget(scrollArea); + + gridContainer = new QWidget(); + gridLayout = new QGridLayout(gridContainer); + gridLayout->setAlignment(Qt::AlignTop | Qt::AlignHCenter); + gridLayout->setSpacing(gridLayoutSpacing); + gridFrame->setLayout(gridLayout); + disks = { + {"/dev/sda", "40C", "Bad"}, + {"/dev/sdb", "40C", "Good"}, + {"/dev/sdc", "40C", "Unknown"}, + {"/dev/sdd", "40C", "Warning"} + }; + populateGrid(); + + setLayout(mainLayout); +} + +void GridView::resizeEvent(QResizeEvent *) { + populateGrid(); +} + +void GridView::populateGrid() { + selectedButton = nullptr; + QLayoutItem *child; + while ((child = gridLayout->takeAt(0)) != nullptr) { + delete child->widget(); + delete child; + } + + int width = scrollArea->viewport()->width(); + int columnsFormula = (width + gridLayoutSpacing) / (iconButtonSize + gridLayoutSpacing); + int cols = qMax(1, columnsFormula - 1); + int row = 0, col = 0; + + + QVector filteredDisks; + filteredDisks.reserve(disks.size()); + + for (auto it = disks.cbegin(); it != disks.cend(); ++it) { + const DiskItem &disk = *it; + if (searchQuery.isEmpty() || + disk.name.contains(searchQuery, Qt::CaseInsensitive) || + disk.category.contains(searchQuery, Qt::CaseInsensitive)) { + filteredDisks.append(disk); + } + } + + extracted(filteredDisks, cols, row, col); +} + +void GridView::extracted(const QVector &filteredDisks, int &cols, int &row, int &col) { + for (int i = 0; i < filteredDisks.size(); ++i) { + const auto &disk = filteredDisks[i]; + QWidget *diskWidget = new QWidget(); + QVBoxLayout *diskLayout = new QVBoxLayout(diskWidget); + diskLayout->setAlignment(Qt::AlignCenter); + diskLayout->setContentsMargins(0, 0, 0, 0); + + QPushButton *iconButton = new QPushButton(); + QString iconPath = QString(":/icons/Disk_%1.svg").arg(disk.icon); + iconButton->setIcon(QIcon(iconPath)); + iconButton->setIconSize(QSize(48, 48)); + iconButton->setFixedSize(iconButtonSize, iconButtonSize); + iconButton->setStyleSheet( + QString("QPushButton { border: 2px solid transparent; " + "border-radius: 10px; }" + "QPushButton:hover { background-color: %1; }" + "QPushButton:pressed { border: 2px solid %2; }") + .arg(hoverColor, selectedColor)); + + QLabel *nameLabel = new QLabel(disk.name); + nameLabel->setAlignment(Qt::AlignCenter); + QLabel *categoryLabel = new QLabel(disk.category); + categoryLabel->setAlignment(Qt::AlignCenter); + categoryLabel->setStyleSheet("font-size: 10px; color: gray;"); + + connect(iconButton, &QPushButton::clicked, this, [this, iconButton]() { + if (selectedButton) { + selectedButton->setStyleSheet(iconButton->styleSheet()); + } + selectedButton = iconButton; + selectedButton->setStyleSheet( + QString( + "QPushButton { border: 2px solid %1; background-color: %2; }") + .arg(selectedColor, hoverColor)); + }); + + diskLayout->addWidget(iconButton, 0, Qt::AlignCenter); + diskLayout->addWidget(nameLabel, 0, Qt::AlignCenter); + diskLayout->addWidget(categoryLabel, 0, Qt::AlignCenter); + diskWidget->setLayout(diskLayout); + gridLayout->addWidget(diskWidget, row, col); + + col++; + if (col >= cols) { + col = 0; + row++; + } + } +} diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4d9fb71..a2fbdd6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4,6 +4,7 @@ #include "statusdot.h" #include "custombutton.h" #include "jsonparser.h" +#include "gridview.h" #include #include @@ -244,7 +245,12 @@ void MainWindow::updateUI() } } else if (isScsi) { QJsonObject scsiErrorCounterLog = localObj.value("scsi_error_counter_log").toObject(); - for (const QString key : {"read", "write", "verify"}) { + static const QString keys[] = { + QStringLiteral("read"), + QStringLiteral("write"), + QStringLiteral("verify") + }; + for (const QString& key : keys) { if (scsiErrorCounterLog.value(key).toObject().value("total_uncorrected_errors").toInt() != 0) { caution = true; } @@ -1372,7 +1378,7 @@ void MainWindow::on_actionASCII_View_triggered() QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Close, asciiViewDialog); connect(buttonBox, &QDialogButtonBox::rejected, asciiViewDialog, &QDialog::close); - connect(buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [binaryData, this, deviceNodePath]() { + connect(buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, this, [binaryData, this, deviceNodePath]() { QString filePath = QFileDialog::getSaveFileName(this, tr("Save Binary Data"), deviceNodePath.section('/', -1) + ".bin", tr("Binary Files (*.bin);;All Files (*)")); if (!filePath.isEmpty()) { QFile file(filePath); @@ -1395,3 +1401,11 @@ void MainWindow::on_actionASCII_View_triggered() asciiViewDialog->exec(); } + +void MainWindow::on_actionGrid_View_triggered() +{ + auto *gridView = new GridView(nullptr); + gridView->setAttribute(Qt::WA_DeleteOnClose); + gridView->show(); +} + diff --git a/src/mainwindow.ui b/src/mainwindow.ui index d7c18f3..2b7d47b 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -534,6 +534,8 @@ Disk + + @@ -651,6 +653,14 @@ ASCII View + + + + + + Grid View + + diff --git a/src/resources.qrc b/src/resources.qrc index 3fd7313..5dc5003 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -1,5 +1,9 @@ ../dist/QDiskInfo.svg + ../dist/status/Disk_Good.svg + ../dist/status/Disk_Bad.svg + ../dist/status/Disk_Warning.svg + ../dist/status/Disk_Unknown.svg diff --git a/src/utils.cpp b/src/utils.cpp index 2295803..f1f9e05 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -9,6 +9,7 @@ #include #include #include +#include void utils::clearButtonGroup(QButtonGroup* buttonGroup, QHBoxLayout* horizontalLayout, QSpacerItem* buttonStretch, QMenu* menuDisk) { @@ -19,7 +20,19 @@ void utils::clearButtonGroup(QButtonGroup* buttonGroup, QHBoxLayout* horizontalL } horizontalLayout->removeItem(buttonStretch); delete buttonStretch; - menuDisk->clear(); + + // Dirty hack to remove only the radio buttons + QList actions = menuDisk->actions(); + bool foundSeparator = false; + + for (int i = 0; i < actions.size(); ++i) { + QAction* action = actions[i]; + if (foundSeparator) { + menuDisk->removeAction(action); + } else if (action->isSeparator()) { + foundSeparator = true; + } + } } QString utils::getSmartctlPath() { From 49c43a002cae94d37f17c338e5a261033f648cd6 Mon Sep 17 00:00:00 2001 From: Spiros Date: Wed, 23 Apr 2025 14:44:39 +0300 Subject: [PATCH 02/15] Fix CustomButton doesn't get highlighted when clicking a radio button --- src/mainwindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a2fbdd6..49d79cc 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -335,6 +335,7 @@ void MainWindow::updateUI() connect(diskAction, &QAction::triggered, this, [=]() { updateWindow(); + button->setChecked(true); }); if (firstTime) { From 91240c40b56624744af6564632b54792545ae2de Mon Sep 17 00:00:00 2001 From: Spiros Date: Wed, 23 Apr 2025 15:16:12 +0300 Subject: [PATCH 03/15] Fix issue where disksGroup actions were not being cleared --- src/mainwindow.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 49d79cc..166e87d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -177,6 +177,13 @@ void MainWindow::updateUI() bool firstTime = true; globalIsNvme = false; + QList oldActions = disksGroup->actions(); + for (QAction *action : std::as_const(oldActions)) { + disksGroup->removeAction(action); + menuDisk->removeAction(action); + delete action; + } + for (int i = 0; i < devices.size(); ++i) { QJsonObject device = devices[i].toObject(); QString deviceName = device["name"].toString(); From 7904b3d9382378bcf8dced246db21febee9b329c Mon Sep 17 00:00:00 2001 From: Spiros Date: Wed, 23 Apr 2025 15:20:55 +0300 Subject: [PATCH 04/15] Fix red circle on Bad icon --- dist/status/Disk_Bad.svg | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/dist/status/Disk_Bad.svg b/dist/status/Disk_Bad.svg index ef0cbb1..5e1956e 100644 --- a/dist/status/Disk_Bad.svg +++ b/dist/status/Disk_Bad.svg @@ -3,7 +3,7 @@ viewBox="0 0 48 48" version="1.1" id="svg40" - sodipodi:docname="QDiskInfo_Bad.svg" + sodipodi:docname="Disk_Bad.svg" inkscape:version="1.4 (e7c3feb100, 2024-10-09)" xml:space="preserve" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" @@ -32,15 +32,15 @@ inkscape:pagecheckerboard="false" inkscape:deskcolor="#d1d1d1" showgrid="false" - inkscape:zoom="11.166667" - inkscape:cx="21.044775" - inkscape:cy="17.776119" + inkscape:zoom="22.333334" + inkscape:cx="30.694029" + inkscape:cy="7.0522386" inkscape:window-width="1600" inkscape:window-height="1172" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" - inkscape:current-layer="g1" /> - +