From fb6881eeaaebcb873c3fa97099d2a7b74f5ad12c Mon Sep 17 00:00:00 2001 From: Daniele Baggio Date: Wed, 18 Mar 2026 10:31:22 +0100 Subject: [PATCH] settings and render for setUseColorsByCanId --- canframemodel.cpp | 55 ++++++++++++++++++++++---------- canframemodel.h | 2 ++ mainsettingsdialog.cpp | 3 ++ mainwindow.cpp | 5 ++- mainwindow.h | 1 + ui/mainsettingsdialog.ui | 7 ++++ utility.h | 69 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 124 insertions(+), 18 deletions(-) diff --git a/canframemodel.cpp b/canframemodel.cpp index ef2ccf8..a778796 100644 --- a/canframemodel.cpp +++ b/canframemodel.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include "utility.h" @@ -76,6 +78,7 @@ CANFrameModel::CANFrameModel(QObject *parent) overwriteDups = false; filtersPersistDuringClear = false; useHexMode = true; + useColorsByCanId = false; timeStyle = TS_MICROS; timeOffset = 0; needFilterRefresh = false; @@ -101,6 +104,16 @@ void CANFrameModel::setHexMode(bool mode) } } +void CANFrameModel::setUseColorsByCanId(bool mode) +{ + if (useColorsByCanId != mode) + { + this->beginResetModel(); + useColorsByCanId = mode; + this->endResetModel(); + } +} + void CANFrameModel::setTimeStyle(TimeStyle newStyle) { if (timeStyle != newStyle) @@ -401,7 +414,6 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const { QString tempString; CANFrame thisFrame; - static bool rowFlip = false; QVariant ts; if (!index.isValid()) @@ -425,9 +437,31 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const return msg->bgColor; } } - rowFlip = (index.row() % 2); - if (rowFlip) return QApplication::palette().color(QPalette::Base); - else return QApplication::palette().color(QPalette::AlternateBase); + + if (useColorsByCanId) + { + QString frameStr = Utility::formatCANID(thisFrame.frameId(), thisFrame.hasExtendedFrameFormat()); + // Color rows by the value in the "category" column + return QBrush(Utility::colorForString(frameStr)); + } + + return (index.row() % 2) ? + QApplication::palette().color(QPalette::Base) : + QApplication::palette().color(QPalette::AlternateBase); + } + + if (role == Qt::ForegroundRole) + { + if (dbcHandler != nullptr && interpretFrames && !ignoreDBCColors) + { + DBC_MESSAGE *msg = dbcHandler->findMessage(thisFrame); + if (msg != nullptr) + { + return msg->fgColor; + } + } + + return QApplication::palette().color(QPalette::WindowText); } if (role == Qt::TextAlignmentRole) @@ -448,19 +482,6 @@ QVariant CANFrameModel::data(const QModelIndex &index, int role) const } } - if (role == Qt::ForegroundRole) - { - if (dbcHandler != nullptr && interpretFrames && !ignoreDBCColors) - { - DBC_MESSAGE *msg = dbcHandler->findMessage(thisFrame); - if (msg != nullptr) - { - return msg->fgColor; - } - } - return QApplication::palette().color(QPalette::WindowText); - } - if (role == Qt::DisplayRole) { switch (Column(index.column())) { diff --git a/canframemodel.h b/canframemodel.h index 6a6bc54..6f58257 100644 --- a/canframemodel.h +++ b/canframemodel.h @@ -47,6 +47,7 @@ public: bool getInterpretMode(); void setOverwriteMode(bool); void setHexMode(bool); + void setUseColorsByCanId(bool); void setClearMode(bool mode); void setTimeStyle(TimeStyle newStyle); void setIgnoreDBCColors(bool mode); @@ -94,6 +95,7 @@ private: QString timeFormat; TimeStyle timeStyle; bool useHexMode; + bool useColorsByCanId; bool needFilterRefresh; bool ignoreDBCColors; int64_t timeOffset; diff --git a/mainsettingsdialog.cpp b/mainsettingsdialog.cpp index eae6272..9012943 100644 --- a/mainsettingsdialog.cpp +++ b/mainsettingsdialog.cpp @@ -90,7 +90,9 @@ MainSettingsDialog::MainSettingsDialog(QWidget *parent) : ui->cbUseFiltered->setChecked(settings.value("Main/UseFiltered", false).toBool()); ui->cbUseOpenGL->setChecked(settings.value("Main/UseOpenGL", false).toBool()); ui->cbFilterLabeling->setChecked(settings.value("Main/FilterLabeling", true).toBool()); + ui->cbFilterLabeling->setChecked(settings.value("Main/FilterLabeling", true).toBool()); ui->cbIgnoreDBCColors->setChecked(settings.value("Main/IgnoreDBCColors", false).toBool()); + ui->cbColorsByCanId->setChecked(settings.value("Main/ColorsByCanId", false).toBool()); int maxFramesDefault; if (QSysInfo::WordSize > 32) @@ -208,6 +210,7 @@ void MainSettingsDialog::updateSettings() settings.setValue("Main/MaximumFrames", ui->spinMaximumFrames->value()); settings.setValue("Main/BytesPerLine", ui->spinBytesPerLine->value()); settings.setValue("Main/FontFixedWidth", ui->cbFontFixedWidth->isChecked()); + settings.setValue("Main/ColorsByCanId", ui->cbColorsByCanId->isChecked()); settings.sync(); emit updatedSettings(); diff --git a/mainwindow.cpp b/mainwindow.cpp index 9e6fea3..7b0bb63 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -36,7 +36,7 @@ MainWindow::MainWindow(QWidget *parent) : #endif useHex = true; - + useColorsByCanId = false; selfRef = this; this->setWindowTitle("Savvy CAN V" + QString::number(VERSION) + " [Built " + QString(__DATE__) +"]"); @@ -415,6 +415,9 @@ void MainWindow::readUpdateableSettings() model->setHexMode(useHex); Utility::decimalMode = !useHex; + useColorsByCanId = settings.value("Main/ColorsByCanId", false).toBool(); + model->setUseColorsByCanId(useColorsByCanId); + bool tempBool; TimeStyle ts = TS_MICROS; tempBool = settings.value("Main/TimeSeconds", false).toBool(); diff --git a/mainwindow.h b/mainwindow.h index 31df02c..c44ed2f 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -160,6 +160,7 @@ private: int rxFrames; bool inhibitFilterUpdate; bool useHex; + bool useColorsByCanId; bool allowCapture; bool ignoreDBCColors; bool CSVAbsTime; diff --git a/ui/mainsettingsdialog.ui b/ui/mainsettingsdialog.ui index e65e91c..0cb6047 100644 --- a/ui/mainsettingsdialog.ui +++ b/ui/mainsettingsdialog.ui @@ -97,6 +97,13 @@ + + + + Color rows by CAN ID + + + diff --git a/utility.h b/utility.h index 66bd539..1941d83 100644 --- a/utility.h +++ b/utility.h @@ -10,6 +10,8 @@ #include #include #include +#include +#include //#include enum TimeStyle @@ -20,6 +22,44 @@ enum TimeStyle TS_CLOCK }; + +// 32 dark, saturated colors — all designed for readability on white backgrounds. +// Minimum contrast ratio ≥ 4.5:1 against white (WCAG AA). +static constexpr std::array kRowPalette = {{ + 0xFFC62828, // 0 Red 800 + 0xFFE53935, // 1 Red 600 + 0xFFD84315, // 2 Deep Orange 800 + 0xFFEF6C00, // 3 Orange 800 + 0xFFF9A825, // 4 Amber 800 + 0xFF558B2F, // 5 Green 800 + 0xFF2E7D32, // 6 Green 900 + 0xFF00695C, // 7 Teal 800 + 0xFF00838F, // 8 Cyan 800 + 0xFF0277BD, // 9 Blue 800 + 0xFF1565C0, // 10 Blue 900 + 0xFF283593, // 11 Indigo 900 + 0xFF4527A0, // 12 Deep Purple 800 + 0xFF6A1B9A, // 13 Purple 900 + 0xFF880E4F, // 14 Pink 900 + 0xFFAD1457, // 15 Pink 800 + 0xFF37474F, // 16 Blue Grey 800 + 0xFF4E342E, // 17 Brown 800 + 0xFF827717, // 18 Lime 900 + 0xFF1B5E20, // 19 Green 900 alt + 0xFF006064, // 20 Cyan 900 + 0xFF01579B, // 21 Light Blue 900 + 0xFF0D47A1, // 22 Blue 900 alt + 0xFF311B92, // 23 Deep Purple 900 + 0xFF4A148C, // 24 Purple 900 alt + 0xFFB71C1C, // 25 Red 900 + 0xFFBF360C, // 26 Deep Orange 900 + 0xFFE65100, // 27 Orange 900 + 0xFF33691E, // 28 Light Green 900 + 0xFF004D40, // 29 Teal 900 + 0xFF263238, // 30 Blue Grey 900 + 0xFF5D4037, // 31 Brown 700 +}}; + class Utility { public: @@ -344,6 +384,35 @@ public: return result; } + + // FNV-1a 32-bit hash — fast, good distribution, no dependencies. + static quint32 hashString(const QString &s) + { + quint32 hash = 2166136261u; + for (const QChar ch : s) { + hash ^= static_cast(ch.unicode()); + hash *= 16777619u; + } + return hash; + } + + // Returns a background QColor deterministically chosen from the palette. + // The same string always maps to the same color. + static QColor colorForString(const QString &s) + { + const quint32 idx = hashString(s) % static_cast(kRowPalette.size()); + // Use a light tint (alpha ~18%) as the row background so text stays readable. + QColor base(kRowPalette[idx]); + base.setAlpha(46); // ~18% — comfortable on white, still clearly tinted + return base; + } + + // Opaque variant — useful for badges, tags, or legend swatches. + static QColor solidColorForString(const QString &s) + { + const quint32 idx = hashString(s) % static_cast(kRowPalette.size()); + return QColor(kRowPalette[idx]); + } }; #endif // UTILITY_H