Cabana: improve binary view (#26525)

* improve

* set minimum width

* click signal to open&hide form

* disable vertical header's click
This commit is contained in:
Dean Lee 2022-11-17 07:51:05 +08:00 committed by GitHub
parent 2cd1571f4a
commit 66edb15b8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 54 additions and 67 deletions

View File

@ -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<const Signal *> BinaryView::getOverlappingSignals() const {
return overlapping;
}
std::tuple<int, int, bool> 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);

View File

@ -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<const Signal*> 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<int, int, bool> 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;
};

View File

@ -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;

View File

@ -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<QString, CanData> * msgs) {
void DetailWidget::showFormClicked() {
auto s = qobject_cast<SignalEdit *>(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);
}

View File

@ -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);

View File

@ -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) {