forked from qt-creator/qt-creator
Axivion: Handle filter for columns
Change-Id: I352d1e27e873b1fef593c9e5250c28a8aef56df4 Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
@@ -389,6 +389,8 @@ IssuesWidget::IssuesWidget(QWidget *parent)
|
||||
m_headerView->setSectionsMovable(true);
|
||||
connect(m_headerView, &IssueHeaderView::sortTriggered,
|
||||
this, &IssuesWidget::onSearchParameterChanged);
|
||||
connect(m_headerView, &IssueHeaderView::filterChanged,
|
||||
this, &IssuesWidget::onSearchParameterChanged);
|
||||
m_issuesView->setHeader(m_headerView);
|
||||
m_issuesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
m_issuesView->enableColumnHiding();
|
||||
@@ -716,6 +718,13 @@ IssueListSearch IssuesWidget::searchFromUi() const
|
||||
+ (pair.second == Qt::AscendingOrder ? " asc" : " desc"));
|
||||
}
|
||||
search.sort = sort;
|
||||
QMap<QString, QString> filter;
|
||||
const QList<QPair<int, QString>> currentFilterColumns = m_headerView->currentFilterColumns();
|
||||
for (const auto &pair : currentFilterColumns) {
|
||||
QTC_ASSERT((ulong)pair.first < m_currentTableInfo->columns.size(), return search);
|
||||
filter.insert("filter_" + m_currentTableInfo->columns.at(pair.first).key, pair.second);
|
||||
}
|
||||
search.filter = filter;
|
||||
return search;
|
||||
}
|
||||
|
||||
|
@@ -198,6 +198,10 @@ QUrlQuery IssueListSearch::toUrlQuery(QueryMode mode) const
|
||||
query.addQueryItem("computeTotalRowCount", "true");
|
||||
if (!sort.isEmpty())
|
||||
query.addQueryItem("sort", sort);
|
||||
if (!filter.isEmpty()) {
|
||||
for (auto f = filter.cbegin(), end = filter.cend(); f != end; ++f)
|
||||
query.addQueryItem(f.key(), f.value());
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <utils/id.h>
|
||||
|
||||
#include <QHash>
|
||||
#include <QMap>
|
||||
#include <QUrl>
|
||||
#include <QVersionNumber>
|
||||
|
||||
@@ -41,6 +42,7 @@ struct IssueListSearch
|
||||
QString owner;
|
||||
QString filter_path;
|
||||
QString sort;
|
||||
QMap<QString, QString> filter;
|
||||
int offset = 0;
|
||||
int limit = DefaultSearchLimit;
|
||||
bool computeTotalRowCount = false;
|
||||
|
@@ -3,16 +3,125 @@
|
||||
|
||||
#include "issueheaderview.h"
|
||||
|
||||
#include <utils/icon.h>
|
||||
#include "axiviontr.h"
|
||||
|
||||
#include <utils/fancylineedit.h>
|
||||
#include <utils/icon.h>
|
||||
#include <utils/layoutbuilder.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPushButton>
|
||||
#include <QToolButton>
|
||||
|
||||
namespace Axivion::Internal {
|
||||
|
||||
constexpr int IconSize = 16;
|
||||
constexpr int InnerMargin = 4;
|
||||
|
||||
static QString infoText()
|
||||
{
|
||||
return Tr::tr("Allows for filters combined with & as logical AND, | as logical OR and "
|
||||
"! as logical NOT. The filters may contain * to match sequences of "
|
||||
"arbitrary characters. If a single filter is quoted with double quotes "
|
||||
"it will be matched on the complete string. Some filter characters "
|
||||
"require quoting of the filter expression with double quotes. If inside "
|
||||
"double quotes you need to escape \" and \\ with a backslash.\n"
|
||||
"Some examples:\n\n"
|
||||
"a matches issues where the value contains the letter 'a'\n"
|
||||
"\"abc\" matches issues where the value is exactly 'abc'\n"
|
||||
"!abc matches issues whose value does not contain 'abc'\n"
|
||||
"(ab | cd) & !ef matches issues with values containing 'ab' or 'cd' but not 'ef'\n"
|
||||
"\"\" matches issues having an empty value in this column\n"
|
||||
"!\"\" matches issues having any non-empty value in this column");
|
||||
}
|
||||
class FilterPopupWidget : public QFrame
|
||||
{
|
||||
public:
|
||||
FilterPopupWidget(QWidget *parent, const QString &filter)
|
||||
: QFrame(parent)
|
||||
{
|
||||
setWindowFlags(Qt::Popup);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
Qt::FocusPolicy origPolicy = parent->focusPolicy();
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
parent->setFocusPolicy(origPolicy);
|
||||
setFocusProxy(parent);
|
||||
|
||||
auto infoButton = new QToolButton(this);
|
||||
infoButton->setIcon(Utils::Icons::INFO.icon());
|
||||
infoButton->setCheckable(true);
|
||||
infoButton->setChecked(true);
|
||||
m_lineEdit = new Utils::FancyLineEdit(this);
|
||||
m_lineEdit->setClearButtonEnabled(true);
|
||||
m_lineEdit->setText(filter);
|
||||
// TODO add some pre-check for validity of the expression
|
||||
// or handle invalid filter exception correctly
|
||||
auto apply = new QPushButton(Tr::tr("Apply"), this);
|
||||
auto infoLabel = new QLabel(infoText());
|
||||
infoLabel->setWordWrap(true);
|
||||
|
||||
using namespace Layouting;
|
||||
Column layout {
|
||||
Row { infoButton, m_lineEdit, apply},
|
||||
infoLabel
|
||||
};
|
||||
layout.attachTo(this);
|
||||
|
||||
adjustSize();
|
||||
setFixedWidth(size().width());
|
||||
|
||||
const auto onApply = [this] {
|
||||
QTC_ASSERT(m_lineEdit, return);
|
||||
if (m_applyHook)
|
||||
m_applyHook(m_lineEdit->text());
|
||||
close();
|
||||
};
|
||||
connect(infoButton, &QToolButton::toggled, this, [this, infoLabel](bool checked){
|
||||
QTC_ASSERT(infoLabel, return);
|
||||
infoLabel->setVisible(checked);
|
||||
adjustSize();
|
||||
});
|
||||
connect(m_lineEdit, &Utils::FancyLineEdit::editingFinished,
|
||||
this, [this, apply, onApply] {
|
||||
if (m_lineEdit->hasFocus() || apply->hasFocus()) // avoid triggering for focus lost
|
||||
onApply();
|
||||
else
|
||||
close();
|
||||
});
|
||||
connect(apply, &QPushButton::clicked, this, onApply);
|
||||
}
|
||||
|
||||
void setOnApply(const std::function<void(const QString &)> &hook) { m_applyHook = hook; }
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override
|
||||
{
|
||||
QWidget::showEvent(event);
|
||||
if (!event->spontaneous())
|
||||
m_lineEdit->setFocus(Qt::PopupFocusReason);
|
||||
}
|
||||
|
||||
void resizeEvent(QResizeEvent *event) override
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
if (m_handleResizeEvent) { // ignore the first resize event (first layout)
|
||||
const int oldHeight = event->oldSize().height();
|
||||
const int newHeight = event->size().height();
|
||||
const QPoint position = pos();
|
||||
move(position.x(), position.y() + oldHeight - newHeight);
|
||||
}
|
||||
m_handleResizeEvent = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_handleResizeEvent = false;
|
||||
Utils::FancyLineEdit *m_lineEdit = nullptr;
|
||||
std::function<void(const QString &)> m_applyHook;
|
||||
};
|
||||
|
||||
static QIcon iconForSorted(std::optional<Qt::SortOrder> order)
|
||||
{
|
||||
static const Utils::Icon UNSORTED(
|
||||
@@ -68,6 +177,17 @@ QList<QPair<int, Qt::SortOrder>> IssueHeaderView::currentSortColumns() const
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<QPair<int, QString>> IssueHeaderView::currentFilterColumns() const
|
||||
{
|
||||
QList<QPair<int, QString>> result;
|
||||
for (int i = 0, end = m_columnInfoList.size(); i < end; ++i) {
|
||||
const ColumnInfo ci = m_columnInfoList.at(i);
|
||||
if (ci.filter.has_value())
|
||||
result.append({i, ci.filter.value()});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void IssueHeaderView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
@@ -125,6 +245,21 @@ void IssueHeaderView::mouseReleaseEvent(QMouseEvent *event)
|
||||
} 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..
|
||||
const auto onApply = [this, logical](const QString &txt) {
|
||||
if (txt.isEmpty())
|
||||
m_columnInfoList[logical].filter.reset();
|
||||
else
|
||||
m_columnInfoList[logical].filter.emplace(txt);
|
||||
headerDataChanged(Qt::Horizontal, logical, logical);
|
||||
emit filterChanged();
|
||||
};
|
||||
auto popup = new FilterPopupWidget(this, m_columnInfoList.at(logical).filter.value_or(""));
|
||||
popup->setOnApply(onApply);
|
||||
const int right = sectionViewportPosition(logical) + sectionSize(logical);
|
||||
const QSize size = popup->sizeHint();
|
||||
popup->move(mapToGlobal(QPoint{x() + right - size.width(),
|
||||
this->y() - size.height()}));
|
||||
popup->show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -27,8 +27,10 @@ public:
|
||||
void setColumnInfoList(const QList<ColumnInfo> &infos);
|
||||
|
||||
QList<QPair<int, Qt::SortOrder>> currentSortColumns() const;
|
||||
QList<QPair<int, QString>> currentFilterColumns() const;
|
||||
|
||||
signals:
|
||||
void filterChanged();
|
||||
void sortTriggered();
|
||||
|
||||
protected:
|
||||
|
Reference in New Issue
Block a user