From 86b179143da0b619e7bf5ef9edb503f75d6b4b55 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 7 Aug 2024 15:49:23 +0200 Subject: [PATCH] Axivion: Prepare filtering of columns This adds a filter icon on the column header if the column is marked as filterable. Beside this the header view now only adjusts the width when necessary. Change-Id: Ib25bc7ba64689209b113cd5a7b4d4e8a615b3282 Reviewed-by: Jarek Kobus --- src/plugins/axivion/axivionoutputpane.cpp | 1 + src/plugins/axivion/issueheaderview.cpp | 92 ++++++++++++++++++----- src/plugins/axivion/issueheaderview.h | 5 +- 3 files changed, 77 insertions(+), 21 deletions(-) diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index 297f27e3138..5a3c0430c01 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -477,6 +477,7 @@ void IssuesWidget::updateTable() hiddenColumns << column.key; IssueHeaderView::ColumnInfo info; info.sortable = column.canSort; + info.filterable = column.canFilter; info.width = column.width; columnInfos.append(info); alignments << alignmentFromString(column.alignment); diff --git a/src/plugins/axivion/issueheaderview.cpp b/src/plugins/axivion/issueheaderview.cpp index fd5375288d9..fd22766a6e6 100644 --- a/src/plugins/axivion/issueheaderview.cpp +++ b/src/plugins/axivion/issueheaderview.cpp @@ -10,7 +10,8 @@ namespace Axivion::Internal { -constexpr int ICON_SIZE = 16; +constexpr int IconSize = 16; +constexpr int InnerMargin = 4; static QIcon iconForSorted(std::optional order) { @@ -35,6 +36,19 @@ static QIcon iconForSorted(std::optional order) return order.value() == Qt::AscendingOrder ? sortedAsc : sortedDesc; } +static QIcon iconForFilter(bool isActive) +{ + const Utils::Icon INACTIVE( + {{":/utils/images/filtericon.png", Utils::Theme::IconsDisabledColor}}, + Utils::Icon::MenuTintedStyle); + const Utils::Icon ACTIVE( + {{":/utils/images/filtericon.png", Utils::Theme::PaletteText}}, + Utils::Icon::MenuTintedStyle); + static const QIcon inactive = INACTIVE.icon(); + static const QIcon active = ACTIVE.icon(); + return isActive ? active : inactive; +} + void IssueHeaderView::setColumnInfoList(const QList &infos) { m_columnInfoList = infos; @@ -62,11 +76,26 @@ void IssueHeaderView::mousePressEvent(QMouseEvent *event) if (y > 1 && y < height() - 2) { // TODO improve const int pos = position.x(); const int logical = logicalIndexAt(pos); + QTC_ASSERT(logical > -1 && logical < m_columnInfoList.size(), + QHeaderView::mousePressEvent(event); return); m_lastToggleLogicalPos = logical; const int margin = style()->pixelMetric(QStyle::PM_HeaderGripMargin, nullptr, this); - const int end = sectionViewportPosition(logical) + sectionSize(logical) - margin; - const int start = end - ICON_SIZE; - m_maybeToggleSort = start < pos && end > pos; + const int lastIconEnd = sectionViewportPosition(logical) + sectionSize(logical) - margin; + const int lastIconStart = lastIconEnd - IconSize; + const int firstIconStart = lastIconStart - InnerMargin - IconSize; + + const ColumnInfo info = m_columnInfoList.at(logical); + if (info.sortable && info.filterable) { + if (firstIconStart < pos && firstIconStart + IconSize > pos) + m_maybeToggle.emplace(Sort); + else if (lastIconStart < pos && lastIconEnd > pos) + m_maybeToggle.emplace(Filter); + else + m_maybeToggle.reset(); + } else { + if (lastIconStart < pos && lastIconEnd > pos) + m_maybeToggle.emplace(info.sortable ? Sort : Filter); + } m_withShift = event->modifiers() == Qt::ShiftModifier; } } @@ -75,10 +104,11 @@ void IssueHeaderView::mousePressEvent(QMouseEvent *event) void IssueHeaderView::mouseReleaseEvent(QMouseEvent *event) { - bool dontSkip = !m_dragging && m_maybeToggleSort; + bool dontSkip = !m_dragging && m_maybeToggle; + const int toggleMode = m_maybeToggle ? m_maybeToggle.value() : -1; bool withShift = m_withShift && event->modifiers() == Qt::ShiftModifier; m_dragging = false; - m_maybeToggleSort = false; + m_maybeToggle.reset(); m_withShift = false; if (dontSkip) { @@ -87,11 +117,14 @@ void IssueHeaderView::mouseReleaseEvent(QMouseEvent *event) const int logical = logicalIndexAt(position.x()); if (logical == m_lastToggleLogicalPos && logical > -1 && logical < m_columnInfoList.size()) { - if (m_columnInfoList.at(logical).sortable) { // ignore non-sortable + if (toggleMode == Sort && m_columnInfoList.at(logical).sortable) { // ignore non-sortable if (y < height() / 2) // TODO improve onToggleSort(logical, Qt::AscendingOrder, withShift); else onToggleSort(logical, Qt::DescendingOrder, withShift); + } else if (toggleMode == Filter && m_columnInfoList.at(logical).filterable) { + // TODO we need some popup for text input (entering filter expression) + // apply them to the columninfo, and use them for the search.. } } } @@ -142,13 +175,20 @@ void IssueHeaderView::onToggleSort(int index, Qt::SortOrder order, bool multi) QSize IssueHeaderView::sectionSizeFromContents(int logicalIndex) const { const QSize oldSize = QHeaderView::sectionSizeFromContents(logicalIndex); - const QSize newSize = logicalIndex < m_columnInfoList.size() - ? QSize(qMax(m_columnInfoList.at(logicalIndex).width, oldSize.width()), oldSize.height()) - : oldSize; + QTC_ASSERT(logicalIndex > -1 && logicalIndex < m_columnInfoList.size(), return oldSize); + const ColumnInfo ci = m_columnInfoList.at(logicalIndex); + const QSize newSize = QSize(qMax(ci.width, oldSize.width()), oldSize.height()); + + if (!ci.filterable && !ci.sortable) + return newSize; const int margin = style()->pixelMetric(QStyle::PM_HeaderGripMargin, nullptr, this); - // add icon size and margin (default resize handle margin) - return QSize{newSize.width() + ICON_SIZE + margin, qMax(newSize.height(), ICON_SIZE)}; + // compute additional space for needed icon(s) + int additionalWidth = IconSize + margin; + if (ci.filterable && ci.sortable) + additionalWidth += IconSize + InnerMargin; + + return QSize{newSize.width() + additionalWidth, qMax(newSize.height(), IconSize)}; } void IssueHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const @@ -159,23 +199,35 @@ void IssueHeaderView::paintSection(QPainter *painter, const QRect &rect, int log if (logicalIndex < 0 || logicalIndex >= m_columnInfoList.size()) return; const ColumnInfo info = m_columnInfoList.at(logicalIndex); - if (!info.sortable) + if (!info.sortable && !info.filterable) return; + const int offset = qMax((rect.height() - IconSize), 0) / 2; const int margin = style()->pixelMetric(QStyle::PM_HeaderGripMargin, nullptr, this); - const QIcon icon = iconForSorted(m_currentSortIndexes.contains(logicalIndex) ? info.sortOrder - : std::nullopt); - const int offset = qMax((rect.height() - ICON_SIZE), 0) / 2; - const int left = rect.left() + rect.width() - ICON_SIZE - margin; - const QRect iconRect(left, offset, ICON_SIZE, ICON_SIZE); - const QRect clearRect(left, 0, ICON_SIZE + margin, rect.height()); + const int lastIconLeft = rect.left() + rect.width() - IconSize - margin; + const int firstIconLeft = lastIconLeft - InnerMargin - IconSize; + + const bool bothIcons = info.sortable && info.filterable; + const QRect clearRect(bothIcons ? firstIconLeft : lastIconLeft, 0, + bothIcons ? IconSize + InnerMargin + IconSize + margin + : IconSize + margin, rect.height()); painter->save(); QStyleOptionHeader opt; initStyleOption(&opt); opt.rect = clearRect; style()->drawControl(QStyle::CE_Header, &opt, painter, this); painter->restore(); - icon.paint(painter, iconRect); + QRect iconRect(lastIconLeft, offset, IconSize, IconSize); + if (info.filterable) { + const QIcon icon = iconForFilter(info.filter.has_value()); + icon.paint(painter, iconRect); + iconRect.setRect(firstIconLeft, offset, IconSize, IconSize); + } + if (info.sortable) { + const QIcon icon = iconForSorted(m_currentSortIndexes.contains(logicalIndex) ? info.sortOrder + : std::nullopt); + icon.paint(painter, iconRect); + } } } // namespace Axivion::Internal diff --git a/src/plugins/axivion/issueheaderview.h b/src/plugins/axivion/issueheaderview.h index e8fef8f62fa..23c7a85174b 100644 --- a/src/plugins/axivion/issueheaderview.h +++ b/src/plugins/axivion/issueheaderview.h @@ -19,6 +19,8 @@ public: int width = 0; std::optional sortOrder = std::nullopt; bool sortable = false; + bool filterable = false; + std::optional filter = std::nullopt; }; explicit IssueHeaderView(QWidget *parent = nullptr) : QHeaderView(Qt::Horizontal, parent) {} @@ -39,7 +41,8 @@ protected: private: void onToggleSort(int index, Qt::SortOrder order, bool multi); bool m_dragging = false; - bool m_maybeToggleSort = false; + enum ToggleMode {Sort, Filter}; + std::optional m_maybeToggle = std::nullopt; bool m_withShift = false; int m_lastToggleLogicalPos = -1; QList m_columnInfoList;