From 66edb15b8f558ce81b5a57af4e60fc24e1fc489c Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 17 Nov 2022 07:51:05 +0800 Subject: [PATCH] Cabana: improve binary view (#26525) * improve * set minimum width * click signal to open&hide form * disable vertical header's click --- tools/cabana/binaryview.cc | 94 +++++++++++++++--------------------- tools/cabana/binaryview.h | 5 +- tools/cabana/canmessages.h | 6 --- tools/cabana/detailwidget.cc | 12 ++++- tools/cabana/detailwidget.h | 1 + tools/cabana/signaledit.cc | 3 +- 6 files changed, 54 insertions(+), 67 deletions(-) diff --git a/tools/cabana/binaryview.cc b/tools/cabana/binaryview.cc index 7707316877..dae4591976 100644 --- a/tools/cabana/binaryview.cc +++ b/tools/cabana/binaryview.cc @@ -23,11 +23,13 @@ BinaryView::BinaryView(QWidget *parent) : QTableView(parent) { delegate = new BinaryItemDelegate(this); setItemDelegate(delegate); horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + verticalHeader()->setSectionsClickable(false); horizontalHeader()->hide(); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); setFrameShape(QFrame::NoFrame); + setShowGrid(false); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); setMouseTracking(true); } @@ -46,46 +48,30 @@ void BinaryView::setSelection(const QRect &rect, QItemSelectionModel::SelectionF return; QItemSelection selection; - auto [tl, br] = std::minmax(anchor_index, index); - if ((resize_sig && resize_sig->is_little_endian) || (!resize_sig && index < anchor_index)) { - // little_endian selection - if (tl.row() == br.row()) { - selection.merge({model->index(tl.row(), tl.column()), model->index(tl.row(), br.column())}, flags); - } else { - for (int row = tl.row(); row <= br.row(); ++row) { - int left_col = (row == br.row()) ? br.column() : 0; - int right_col = (row == tl.row()) ? tl.column() : 7; - selection.merge({model->index(row, left_col), model->index(row, right_col)}, flags); - } - } - } else { - // big endian selection - for (int row = tl.row(); row <= br.row(); ++row) { - int left_col = (row == tl.row()) ? tl.column() : 0; - int right_col = (row == br.row()) ? br.column() : 7; - selection.merge({model->index(row, left_col), model->index(row, right_col)}, flags); - } + auto [start, size, is_lb] = getSelection(index); + for (int i = start; i < start + size; ++i) { + auto idx = model->bitIndex(i, is_lb); + selection.merge({idx, idx}, flags); } selectionModel()->select(selection, flags); } void BinaryView::mousePressEvent(QMouseEvent *event) { delegate->setSelectionColor(style()->standardPalette().color(QPalette::Active, QPalette::Highlight)); - if (auto index = indexAt(event->pos()); index.isValid() && index.column() != 8) { + if (auto index = indexAt(event->pos()); index.isValid() && index.column() != 8) { anchor_index = index; auto item = (const BinaryViewModel::Item *)anchor_index.internalPointer(); - if (item && item->sigs.size() > 0) { - int bit_idx = get_bit_index(anchor_index, true); - for (auto s : item->sigs) { - if (bit_idx == s->lsb || bit_idx == s->msb) { - resize_sig = s; - delegate->setSelectionColor(item->bg_color); - break; - } + int bit_idx = get_bit_index(anchor_index, true); + for (auto s : item->sigs) { + if (bit_idx == s->lsb || bit_idx == s->msb) { + anchor_index = model->bitIndex(bit_idx == s->lsb ? s->msb : s->lsb, true); + resize_sig = s; + delegate->setSelectionColor(item->bg_color); + break; } } } - QTableView::mousePressEvent(event); + event->accept(); } void BinaryView::mouseMoveEvent(QMouseEvent *event) { @@ -104,28 +90,14 @@ void BinaryView::mouseReleaseEvent(QMouseEvent *event) { auto release_index = indexAt(event->pos()); if (release_index.isValid() && anchor_index.isValid()) { - if (release_index.column() == 8) { - release_index = model->index(release_index.row(), 7); - } - bool little_endian = (resize_sig && resize_sig->is_little_endian) || (!resize_sig && release_index < anchor_index); - int release_bit_idx = get_bit_index(release_index, little_endian); - int archor_bit_idx = get_bit_index(anchor_index, little_endian); - if (resize_sig) { - auto [sig_from, sig_to] = getSignalRange(resize_sig); - int start_bit, end_bit; - if (archor_bit_idx == sig_from) { - std::tie(start_bit, end_bit) = std::minmax(release_bit_idx, sig_to); - if (start_bit >= sig_from && start_bit <= sig_to) - start_bit = std::min(start_bit + 1, sig_to); - } else { - std::tie(start_bit, end_bit) = std::minmax(release_bit_idx, sig_from); - if (end_bit >= sig_from && end_bit <= sig_to) - end_bit = std::max(end_bit - 1, sig_from); - } - emit resizeSignal(resize_sig, start_bit, end_bit - start_bit + 1); + if (selectionModel()->hasSelection()) { + auto [start_bit, size, is_lb] = getSelection(release_index); + resize_sig ? emit resizeSignal(resize_sig, start_bit, size) + : emit addSignal(start_bit, size, is_lb); } else { - auto [sart_bit, end_bit] = std::minmax(archor_bit_idx, release_bit_idx); - emit addSignal(sart_bit, end_bit - sart_bit + 1, little_endian); + auto item = (const BinaryViewModel::Item *)anchor_index.internalPointer(); + if (item && item->sigs.size() > 0) + emit signalClicked(item->sigs.back()); } } clearSelection(); @@ -156,6 +128,17 @@ QSet BinaryView::getOverlappingSignals() const { return overlapping; } +std::tuple BinaryView::getSelection(QModelIndex index) { + if (index.column() == 8) { + index = model->index(index.row(), 7); + } + bool is_lb = (resize_sig && resize_sig->is_little_endian) || (!resize_sig && index < anchor_index); + int cur_bit_idx = get_bit_index(index, is_lb); + int anchor_bit_idx = get_bit_index(anchor_index, is_lb); + auto [start_bit, end_bit] = std::minmax(cur_bit_idx, anchor_bit_idx); + return {start_bit, end_bit - start_bit + 1, is_lb}; +} + // BinaryViewModel void BinaryViewModel::setMessage(const QString &message_id) { @@ -203,7 +186,7 @@ void BinaryViewModel::updateState() { char hex[3] = {'\0'}; for (int i = 0; i < std::min(binary.size(), row_count); ++i) { for (int j = 0; j < column_count - 1; ++j) { - items[i * column_count + j].val = QChar((binary[i] >> (7 - j)) & 1 ? '1' : '0'); + items[i * column_count + j].val = ((binary[i] >> (7 - j)) & 1) != 0 ? '1' : '0'; } hex[0] = toHex(binary[i] >> 4); hex[1] = toHex(binary[i] & 0xf); @@ -250,17 +233,16 @@ void BinaryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op painter->save(); // background - bool hover = item->sigs.contains(bin_view->hoveredSignal()); - QColor bg_color = hover ? hoverColor(item->bg_color) : item->bg_color; if (option.state & QStyle::State_Selected) { - bg_color = selection_color; + painter->fillRect(option.rect, selection_color); + } else if (!bin_view->selectionModel()->hasSelection() || !item->sigs.contains(bin_view->resize_sig)) { + painter->fillRect(option.rect, item->bg_color); } - painter->fillRect(option.rect, bg_color); // text if (index.column() == 8) { // hex column painter->setFont(hex_font); - } else if (hover) { + } else if (option.state & QStyle::State_Selected || (!bin_view->resize_sig && item->sigs.contains(bin_view->hovered_sig))) { painter->setPen(Qt::white); } painter->drawText(option.rect, Qt::AlignCenter, item->val); diff --git a/tools/cabana/binaryview.h b/tools/cabana/binaryview.h index 2d6fc5c18b..0ea111d917 100644 --- a/tools/cabana/binaryview.h +++ b/tools/cabana/binaryview.h @@ -32,6 +32,7 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const { return {}; } int rowCount(const QModelIndex &parent = QModelIndex()) const override { return row_count; } int columnCount(const QModelIndex &parent = QModelIndex()) const override { return column_count; } + inline QModelIndex bitIndex(int bit, bool is_lb) const { return index(bit / 8, is_lb ? (7 - bit % 8) : bit % 8); } QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override { return createIndex(row, column, (void *)&items[row * column_count + column]); } @@ -63,15 +64,16 @@ public: void setMessage(const QString &message_id); void highlight(const Signal *sig); QSet getOverlappingSignals() const; - inline const Signal *hoveredSignal() const { return hovered_sig; } inline void updateState() { model->updateState(); } signals: + void signalClicked(const Signal *sig); void signalHovered(const Signal *sig); void addSignal(int start_bit, int size, bool little_endian); void resizeSignal(const Signal *sig, int from, int size); private: + std::tuple getSelection(QModelIndex index); void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; @@ -83,4 +85,5 @@ private: BinaryItemDelegate *delegate; const Signal *resize_sig = nullptr; const Signal *hovered_sig = nullptr; + friend class BinaryItemDelegate; }; diff --git a/tools/cabana/canmessages.h b/tools/cabana/canmessages.h index ff41edad54..4cb0f403a0 100644 --- a/tools/cabana/canmessages.h +++ b/tools/cabana/canmessages.h @@ -80,11 +80,5 @@ inline const QString &getColor(int i) { return SIGNAL_COLORS[i % std::size(SIGNAL_COLORS)]; } -inline QColor hoverColor(const QColor &color) { - QColor c = color.convertTo(QColor::Hsv); - c.setHsv(color.hue(), 180, 180); - return c; -} - // A global pointer referring to the unique CANMessages object extern CANMessages *can; diff --git a/tools/cabana/detailwidget.cc b/tools/cabana/detailwidget.cc index 99cf45f5fa..52ae530a56 100644 --- a/tools/cabana/detailwidget.cc +++ b/tools/cabana/detailwidget.cc @@ -17,6 +17,7 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(charts), QWidget(parent) { undo_stack = new QUndoStack(this); + setMinimumWidth(500); QVBoxLayout *main_layout = new QVBoxLayout(this); main_layout->setContentsMargins(0, 0, 0, 0); main_layout->setSpacing(0); @@ -90,6 +91,7 @@ DetailWidget::DetailWidget(ChartsWidget *charts, QWidget *parent) : charts(chart tab_widget->addTab(history_log, "Logs"); main_layout->addWidget(tab_widget); + QObject::connect(binary_view, &BinaryView::signalClicked, this, &DetailWidget::showForm); QObject::connect(binary_view, &BinaryView::resizeSignal, this, &DetailWidget::resizeSignal); QObject::connect(binary_view, &BinaryView::addSignal, this, &DetailWidget::addSignal); QObject::connect(tab_widget, &QTabWidget::currentChanged, [this]() { updateState(); }); @@ -197,9 +199,15 @@ void DetailWidget::updateState(const QHash * msgs) { void DetailWidget::showFormClicked() { auto s = qobject_cast(sender()); + showForm(s->sig); +} + +void DetailWidget::showForm(const Signal *sig) { setUpdatesEnabled(false); - for (auto f : signal_list) - f->updateForm(f == s && !f->isFormVisible()); + for (auto f : signal_list) { + f->updateForm(f->sig == sig && !f->isFormVisible()); + if (f->sig == sig) scroll->ensureWidgetVisible(f); + } setUpdatesEnabled(true); } diff --git a/tools/cabana/detailwidget.h b/tools/cabana/detailwidget.h index 4346d1c5d5..41fa0edd41 100644 --- a/tools/cabana/detailwidget.h +++ b/tools/cabana/detailwidget.h @@ -28,6 +28,7 @@ public: QUndoStack *undo_stack = nullptr; private: + void showForm(const Signal *sig); void showFormClicked(); void updateChartState(const QString &id, const Signal *sig, bool opened); void showTabBarContextMenu(const QPoint &pt); diff --git a/tools/cabana/signaledit.cc b/tools/cabana/signaledit.cc index 6e4cf2a83a..a0660901cf 100644 --- a/tools/cabana/signaledit.cc +++ b/tools/cabana/signaledit.cc @@ -196,9 +196,8 @@ void SignalEdit::updateForm(bool visible) { } void SignalEdit::signalHovered(const Signal *s) { - auto bg_color = sig == s ? hoverColor(getColor(form_idx)) : QColor(getColor(form_idx)); auto color = sig == s ? "white" : "black"; - color_label->setStyleSheet(QString("color:%1; background-color:%2").arg(color).arg(bg_color.name())); + color_label->setStyleSheet(QString("color:%1; background-color:%2").arg(color).arg(getColor(form_idx))); } void SignalEdit::enterEvent(QEvent *event) {