JS8Call-Improved master
Loading...
Searching...
No Matches
SemiSortableHeader.h
Go to the documentation of this file.
1
5
6#ifndef JS8CALL_SEMISORTABLEHEADER_H
7#define JS8CALL_SEMISORTABLEHEADER_H
8
9#include <QHeaderView>
10#include <QIcon>
11#include <QMouseEvent>
12#include <QPainter>
13#include <QPointer>
14#include <QSet>
15#include <QStyleOptionHeader>
16#include <QTableWidget>
17#include <QVariant>
18
19
28class SemiSortableHeader : public QHeaderView {
29
30public:
36 explicit SemiSortableHeader(Qt::Orientation ori, QWidget *parent = nullptr)
37 : QHeaderView(ori, parent) {
38 setSectionsClickable(true);
39 setSortIndicatorShown(true);
40 setSortIndicatorClearable(false);
41 setMouseTracking(true);
42 }
43
48 void attachTo(QTableWidget *table) {
49 table_ = table;
50 table_->setHorizontalHeader(this);
51
52 // Keep sorting enabled so the style draws the indicator
53 table_->setSortingEnabled(true);
54
55 // Initialize to a sensible default: first sortable column, descending
56 sortCol_ = firstSortableSection();
57 sortOrder_ = Qt::DescendingOrder;
58
59 if (sortCol_ >= 0) {
60 QSignalBlocker b(this);
61 setSortIndicator(sortCol_, sortOrder_);
62 }
63
64 connect(this, &QHeaderView::sectionClicked, this,
65 &SemiSortableHeader::onSectionClicked, Qt::UniqueConnection);
66 }
67
72 void setNonSortableColumns(const QSet<int> &cols) {
73 nonSortable_ = cols;
74 normalizeSortIfNeeded();
75 viewport()->update();
76 }
77
81 void addNonSortableColumn(int col) {
82 if (col >= 0) {
83 nonSortable_.insert(col);
84 normalizeSortIfNeeded();
85 viewport()->update();
86 }
87 }
88
93 nonSortable_.remove(col);
94 normalizeSortIfNeeded();
95 viewport()->update();
96 }
97
102 bool isSortableColumn(int col) const {
103 return col >= 0 && !nonSortable_.contains(col);
104 }
105
106private slots:
111 void onSectionClicked(int column) {
112 if (!table_)
113 return;
114 if (!isSortableColumn(column))
115 return;
116
117 if (column == sortCol_) {
118 sortOrder_ = (sortOrder_ == Qt::AscendingOrder) ? Qt::DescendingOrder
119 : Qt::AscendingOrder;
120 } else {
121 sortCol_ = column;
122 sortOrder_ = Qt::AscendingOrder;
123 }
124
125 // Update indicator without re-triggering click logic
126 {
127 QSignalBlocker b(this);
128 setSortIndicator(sortCol_, sortOrder_);
129 }
130
131 // Prevent Qt's built-in sorting from running while we sort
132 table_->setSortingEnabled(false);
133 table_->sortItems(sortCol_, sortOrder_);
134 table_->setSortingEnabled(true);
135 }
136
137protected:
141 void paintSection(QPainter *painter, const QRect &rect,
142 int logicalIndex) const override {
143 // Normal columns: keep Qt default painting
144 if (isSortableColumn(logicalIndex)) {
145 QHeaderView::paintSection(painter, rect, logicalIndex);
146 return;
147 }
148
149 // Dead columns: custom paint (no hover/pressed, no sort arrow), but keep
150 // label
151 QStyleOptionHeader opt;
152 initStyleOption(&opt);
153
154 opt.rect = rect;
155 opt.section = logicalIndex;
156
157 if (auto *m = model()) {
158 opt.text = m->headerData(logicalIndex, orientation(), Qt::DisplayRole)
159 .toString();
160
161 const QVariant align =
162 m->headerData(logicalIndex, orientation(), Qt::TextAlignmentRole);
163 if (align.isValid())
164 opt.textAlignment = Qt::Alignment(align.toInt());
165
166 const QVariant deco =
167 m->headerData(logicalIndex, orientation(), Qt::DecorationRole);
168 if (deco.isValid())
169 opt.icon = deco.value<QIcon>();
170 }
171
172 // Section position affects borders/separators
173 const int v = visualIndex(logicalIndex);
174 const int n = count();
175 if (n <= 1)
176 opt.position = QStyleOptionHeader::OnlyOneSection;
177 else if (v == 0)
178 opt.position = QStyleOptionHeader::Beginning;
179 else if (v == n - 1)
180 opt.position = QStyleOptionHeader::End;
181 else
182 opt.position = QStyleOptionHeader::Middle;
183
184 opt.state &= ~QStyle::State_MouseOver;
185 opt.state &= ~QStyle::State_Sunken;
186
187 // Ensure no sort arrow is painted on dead columns
188 opt.sortIndicator = QStyleOptionHeader::None;
189
190 style()->drawControl(QStyle::CE_Header, &opt, painter, this);
191 }
192
196 void mousePressEvent(QMouseEvent *e) override {
197 const int col = logicalIndexAt(e->pos());
198 if (!isSortableColumn(col)) {
199 e->accept(); // swallow: no indicator change, no click signal
200 return;
201 }
202 QHeaderView::mousePressEvent(e);
203 }
204
205private:
206 int firstSortableSection() const {
207 for (int logical = 0; logical < count(); ++logical) {
208 if (isSortableColumn(logical))
209 return logical;
210 }
211 return -1; // none sortable
212 }
213
214 void normalizeSortIfNeeded() {
215 // If current sort column became non-sortable, move to first sortable
216 if (sortCol_ >= 0 && !isSortableColumn(sortCol_)) {
217 sortCol_ = firstSortableSection();
218 sortOrder_ = Qt::DescendingOrder;
219 if (sortCol_ >= 0) {
220 QSignalBlocker b(this);
221 setSortIndicator(sortCol_, sortOrder_);
222 } else {
223 QSignalBlocker b(this);
224 setSortIndicator(-1, Qt::AscendingOrder); // clear indicator
225 }
226 }
227 }
228
229private:
230 QPointer<QTableWidget> table_;
231 QSet<int> nonSortable_;
232
233 int sortCol_ = -1;
234 Qt::SortOrder sortOrder_ = Qt::AscendingOrder;
235};
236
237#endif // JS8CALL_SEMISORTABLEHEADER_H
bool isSortableColumn(int col) const
Check whether a column is sortable.
Definition SemiSortableHeader.h:102
void removeNonSortableColumn(int col)
Remove non-sortable marking from a column.
Definition SemiSortableHeader.h:92
void addNonSortableColumn(int col)
Mark a specific column as non-sortable.
Definition SemiSortableHeader.h:81
SemiSortableHeader(Qt::Orientation ori, QWidget *parent=nullptr)
Construct the header with the given orientation.
Definition SemiSortableHeader.h:36
void setNonSortableColumns(const QSet< int > &cols)
Set the set of non-sortable columns.
Definition SemiSortableHeader.h:72
void attachTo(QTableWidget *table)
Attach this header to a QTableWidget and initialize sorting state.
Definition SemiSortableHeader.h:48
void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const override
Custom painting to avoid drawing sort indicators for non-sortable columns.
Definition SemiSortableHeader.h:141
void mousePressEvent(QMouseEvent *e) override
Intercept mouse presses to ignore clicks on non-sortable columns.
Definition SemiSortableHeader.h:196