forked from qt-creator/qt-creator
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 <jaroslaw.kobus@qt.io>
This commit is contained in:
@@ -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);
|
||||
|
@@ -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<Qt::SortOrder> order)
|
||||
{
|
||||
@@ -35,6 +36,19 @@ static QIcon iconForSorted(std::optional<Qt::SortOrder> 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<ColumnInfo> &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
|
||||
|
@@ -19,6 +19,8 @@ public:
|
||||
int width = 0;
|
||||
std::optional<Qt::SortOrder> sortOrder = std::nullopt;
|
||||
bool sortable = false;
|
||||
bool filterable = false;
|
||||
std::optional<QString> 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<ToggleMode> m_maybeToggle = std::nullopt;
|
||||
bool m_withShift = false;
|
||||
int m_lastToggleLogicalPos = -1;
|
||||
QList<ColumnInfo> m_columnInfoList;
|
||||
|
Reference in New Issue
Block a user