Commit bdff4fcc authored by Waqar Ahmed's avatar Waqar Ahmed Committed by Christoph Cullmann
Browse files

Refactor QuickDialog

- Renamed to "HUDDialog"
- Move filtering etc to base class
- Make it more easily usable for cases where setting up a new class
  isn't worth the effort
parent 2ab18df5
Pipeline #215626 passed with stage
in 4 minutes and 52 seconds
......@@ -151,7 +151,7 @@ private:
};
GotoSymbolHUDDialog::GotoSymbolHUDDialog(KTextEditor::MainWindow *mainWindow, QSharedPointer<LSPClientServer> server)
: QuickDialog(nullptr, mainWindow->window())
: HUDDialog(nullptr, mainWindow->window())
, model(new QStandardItemModel(this))
, mainWindow(mainWindow)
, server(std::move(server))
......@@ -184,9 +184,9 @@ void GotoSymbolHUDDialog::setPaletteToEditorColors()
delegate->setFont(Utils::editorFont());
}
void GotoSymbolHUDDialog::slotReturnPressed()
void GotoSymbolHUDDialog::slotReturnPressed(const QModelIndex &index)
{
auto symbol = m_treeView.currentIndex().data(SymbolInfoRole).value<GotoSymbolItem>();
auto symbol = index.data(SymbolInfoRole).value<GotoSymbolItem>();
if (!symbol.fileUrl.isValid() || symbol.fileUrl.isEmpty()) {
return;
}
......
......@@ -18,7 +18,7 @@ namespace KTextEditor
class MainWindow;
}
class GotoSymbolHUDDialog : public QuickDialog
class GotoSymbolHUDDialog : public HUDDialog
{
public:
GotoSymbolHUDDialog(KTextEditor::MainWindow *mainWindow, QSharedPointer<LSPClientServer> server);
......@@ -26,7 +26,7 @@ public:
void openDialog();
protected Q_SLOTS:
void slotReturnPressed() override final;
void slotReturnPressed(const QModelIndex &index) override final;
private:
void slotTextChanged(const QString &text);
......
......@@ -59,7 +59,7 @@ void BranchCheckoutDialog::onCheckoutDone()
sendMessage(msgStr, warn);
}
void BranchCheckoutDialog::slotReturnPressed()
void BranchCheckoutDialog::slotReturnPressed(const QModelIndex &index)
{
// we cleared the model to checkout new branch
if (m_model->rowCount() == 0) {
......@@ -70,7 +70,7 @@ void BranchCheckoutDialog::slotReturnPressed()
// branch is selected, do actual checkout
if (m_checkingOutFromBranch) {
m_checkingOutFromBranch = false;
const auto fromBranch = m_proxyModel->data(m_treeView.currentIndex(), BranchesDialogModel::CheckoutName).toString();
const auto fromBranch = index.data(BranchesDialogModel::CheckoutName).toString();
m_checkoutBranchName = fromBranch;
m_model->clear();
clearLineEdit();
......@@ -78,8 +78,8 @@ void BranchCheckoutDialog::slotReturnPressed()
return;
}
const auto branch = m_proxyModel->data(m_treeView.currentIndex(), BranchesDialogModel::CheckoutName).toString();
const auto itemType = (BranchesDialogModel::ItemType)m_proxyModel->data(m_treeView.currentIndex(), BranchesDialogModel::ItemTypeRole).toInt();
const auto branch = index.data(BranchesDialogModel::CheckoutName).toString();
const auto itemType = (BranchesDialogModel::ItemType)index.data(BranchesDialogModel::ItemTypeRole).toInt();
if (itemType == BranchesDialogModel::BranchItem) {
QFuture<GitUtils::CheckoutResult> future = QtConcurrent::run(&GitUtils::checkoutBranch, m_projectPath, branch);
......@@ -100,12 +100,6 @@ void BranchCheckoutDialog::slotReturnPressed()
hide();
}
void BranchCheckoutDialog::reselectFirst()
{
QModelIndex index = m_proxyModel->index(0, 0);
m_treeView.setCurrentIndex(index);
}
void BranchCheckoutDialog::createNewBranch(const QString &branch, const QString &fromBranch)
{
if (branch.isEmpty()) {
......
......@@ -17,8 +17,7 @@ public:
void openDialog();
private Q_SLOTS:
void slotReturnPressed() override;
void reselectFirst();
void slotReturnPressed(const QModelIndex &index) override;
void onCheckoutDone();
private:
......
......@@ -28,59 +28,10 @@
#include <drawing_utils.h>
#include <kfts_fuzzy_match.h>
class BranchFilterModel : public QSortFilterProxyModel
class StyleDelegate : public HUDStyleDelegate
{
public:
BranchFilterModel(QObject *parent = nullptr)
: QSortFilterProxyModel(parent)
{
}
Q_SLOT void setFilterString(const QString &string)
{
beginResetModel();
m_pattern = string;
endResetModel();
}
protected:
bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override
{
if (m_pattern.isEmpty()) {
const int l = sourceLeft.data(BranchesDialogModel::OriginalSorting).toInt();
const int r = sourceRight.data(BranchesDialogModel::OriginalSorting).toInt();
return l > r;
}
const int l = sourceLeft.data(BranchesDialogModel::FuzzyScore).toInt();
const int r = sourceRight.data(BranchesDialogModel::FuzzyScore).toInt();
return l < r;
}
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
{
if (m_pattern.isEmpty()) {
return true;
}
int score = 0;
const auto idx = sourceModel()->index(sourceRow, 0, sourceParent);
const QString string = idx.data().toString();
const bool res = kfts::fuzzy_match(m_pattern, string, score);
sourceModel()->setData(idx, score, BranchesDialogModel::FuzzyScore);
return res;
}
private:
QString m_pattern;
};
class StyleDelegate : public QStyledItemDelegate
{
public:
StyleDelegate(QObject *parent = nullptr)
: QStyledItemDelegate(parent)
{
}
using HUDStyleDelegate::HUDStyleDelegate;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
......@@ -123,51 +74,32 @@ public:
painter->save();
// paint background
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight());
} else {
painter->fillRect(option.rect, option.palette.base());
}
auto *style = options.widget->style();
options.text = QString(); // clear old text
options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter, options.widget);
style->drawControl(QStyle::CE_ItemViewItem, &options, painter, options.widget);
// leave space for icon
const int hMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, option.widget);
const int iconWidth = option.decorationSize.width() + (hMargin * 2);
if (itemType == BranchesDialogModel::BranchItem) {
painter->translate(25, 0);
options.rect.adjust(iconWidth, 0, 0, 0);
} else {
options.rect.adjust((hMargin * 2), 0, 0, 0);
}
Utils::paintItemViewText(painter, name, options, formats);
painter->restore();
}
public Q_SLOTS:
void setFilterString(const QString &text)
{
m_filterString = text;
}
private:
QString m_filterString;
};
BranchesDialog::BranchesDialog(QWidget *window, KateProjectPluginView *pluginView, QString projectPath)
: QuickDialog(nullptr, window)
: HUDDialog(nullptr, window)
, m_model(new BranchesDialogModel(this))
, m_projectPath(projectPath)
, m_pluginView(pluginView)
{
m_model = new BranchesDialogModel(this);
m_proxyModel = new BranchFilterModel(this);
m_proxyModel->setSourceModel(m_model);
m_treeView.setModel(m_proxyModel);
auto delegate = new StyleDelegate(this);
connect(&m_lineEdit, &QLineEdit::textChanged, this, [this, delegate](const QString &s) {
static_cast<BranchFilterModel *>(m_proxyModel)->setFilterString(s);
delegate->setFilterString(s);
});
setModel(m_model, FilterType::ScoredFuzzy, 0, Qt::DisplayRole, BranchesDialogModel::FuzzyScore);
setDelegate(new StyleDelegate(this));
}
void BranchesDialog::openDialog(GitUtils::RefType r)
......@@ -181,24 +113,14 @@ void BranchesDialog::openDialog(GitUtils::RefType r)
exec();
}
void BranchesDialog::slotReturnPressed()
void BranchesDialog::slotReturnPressed(const QModelIndex &index)
{
/** We want display role here */
const auto branch = m_proxyModel->data(m_treeView.currentIndex(), Qt::DisplayRole).toString();
const auto itemType = (BranchesDialogModel::ItemType)m_proxyModel->data(m_treeView.currentIndex(), BranchesDialogModel::ItemTypeRole).toInt();
const auto branch = index.data().toString();
const auto itemType = (BranchesDialogModel::ItemType)index.data(BranchesDialogModel::ItemTypeRole).toInt();
Q_ASSERT(itemType == BranchesDialogModel::BranchItem);
m_branch = branch;
Q_EMIT branchSelected(branch);
clearLineEdit();
hide();
}
void BranchesDialog::reselectFirst()
{
QModelIndex index = m_proxyModel->index(0, 0);
m_treeView.setCurrentIndex(index);
}
void BranchesDialog::sendMessage(const QString &plainText, bool warn)
......
......@@ -25,7 +25,7 @@ namespace KTextEditor
class MainWindow;
}
class BranchesDialog : public QuickDialog
class BranchesDialog : public HUDDialog
{
Q_OBJECT
public:
......@@ -41,12 +41,10 @@ Q_SIGNALS:
void branchSelected(const QString &branch);
private Q_SLOTS:
void slotReturnPressed() override;
void reselectFirst();
void slotReturnPressed(const QModelIndex &index) override;
protected:
BranchesDialogModel *m_model;
QSortFilterProxyModel *m_proxyModel;
BranchesDialogModel *const m_model;
QString m_projectPath;
private:
......
......@@ -40,8 +40,6 @@ QVariant BranchesDialogModel::data(const QModelIndex &idx, int role) const
return branch.name;
} else if (role == Role::FuzzyScore) {
return branch.score;
} else if (role == Role::OriginalSorting) {
return branch.dateSort;
} else if (role == Qt::DecorationRole) {
if (branch.itemType == BranchItem) {
static const auto branchIcon = QIcon::fromTheme(QStringLiteral("vcs-branch"));
......@@ -68,15 +66,15 @@ void BranchesDialogModel::refresh(const QVector<GitUtils::Branch> &branches, boo
{
QVector<Branch> temp;
if (checkingOut) {
Branch create{branches.at(0).name, {}, {}, 0, 0, ItemType::CreateBranch};
Branch createFrom{branches.at(1).name, {}, {}, 0, 1, ItemType::CreateBranchFrom};
Branch create{branches.at(0).name, {}, {}, 0, ItemType::CreateBranch};
Branch createFrom{branches.at(1).name, {}, {}, 0, ItemType::CreateBranchFrom};
temp.push_back(create);
temp.push_back(createFrom);
}
int i = checkingOut ? 2 : 0;
for (; i < branches.size(); ++i) {
temp.append({branches.at(i).name, branches.at(i).remote, branches.at(i).type, -1, i, ItemType::BranchItem});
temp.append({branches.at(i).name, branches.at(i).remote, branches.at(i).type, -1, ItemType::BranchItem});
}
beginResetModel();
......
......@@ -16,7 +16,7 @@ class BranchesDialogModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum Role { FuzzyScore = Qt::UserRole + 1, OriginalSorting, CheckoutName, RefType, Creator, ItemTypeRole };
enum Role { FuzzyScore = Qt::UserRole + 1, CheckoutName, RefType, Creator, ItemTypeRole };
enum ItemType { BranchItem, CreateBranch, CreateBranchFrom };
explicit BranchesDialogModel(QObject *parent = nullptr);
......@@ -45,7 +45,6 @@ private:
QString remote;
GitUtils::RefType refType;
int score;
int dateSort;
ItemType itemType;
};
......
......@@ -19,7 +19,7 @@
#include <ktexteditor_utils.h>
PushPullDialog::PushPullDialog(KTextEditor::MainWindow *mainWindow, const QString &repoPath)
: QuickDialog(nullptr, mainWindow->window())
: HUDDialog(nullptr, mainWindow->window())
, m_repo(repoPath)
{
m_lineEdit.setFont(Utils::editorFont());
......@@ -162,7 +162,7 @@ QString PushPullDialog::buildPullString()
return QStringLiteral("git pull %1 %2").arg(QStringLiteral("origin")).arg(br);
}
void PushPullDialog::slotReturnPressed()
void PushPullDialog::slotReturnPressed(const QModelIndex &)
{
if (!m_lineEdit.text().isEmpty()) {
auto args = m_lineEdit.text().split(QLatin1Char(' '));
......
......@@ -8,7 +8,7 @@
#include "quickdialog.h"
class PushPullDialog : public QuickDialog
class PushPullDialog : public HUDDialog
{
Q_OBJECT
public:
......@@ -31,7 +31,7 @@ private:
QStringList m_lastExecutedCommands;
protected Q_SLOTS:
void slotReturnPressed() override;
void slotReturnPressed(const QModelIndex &index) override;
};
#endif // PUSHPULLDIALOG_H
......@@ -35,130 +35,15 @@
#include <gitprocess.h>
constexpr int StashIndexRole = Qt::UserRole + 2;
class StashFilterModel final : public QSortFilterProxyModel
{
public:
StashFilterModel(QObject *parent = nullptr)
: QSortFilterProxyModel(parent)
{
}
Q_SLOT void setFilterString(const QString &string)
{
beginResetModel();
m_pattern = string;
endResetModel();
}
protected:
bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override
{
return sourceLeft.data(FuzzyScore).toInt() < sourceRight.data(FuzzyScore).toInt();
}
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
{
if (m_pattern.isEmpty()) {
return true;
}
int score = 0;
const auto idx = sourceModel()->index(sourceRow, 0, sourceParent);
const QString string = idx.data().toString();
const bool res = kfts::fuzzy_match(m_pattern, string, score);
sourceModel()->setData(idx, score, FuzzyScore);
return res;
}
private:
static constexpr int FuzzyScore = Qt::UserRole + 1;
QString m_pattern;
};
class StyleDelegate : public QStyledItemDelegate
{
public:
StyleDelegate(QObject *parent = nullptr)
: QStyledItemDelegate(parent)
{
}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QStyleOptionViewItem options = option;
initStyleOption(&options, index);
QString name = index.data().toString();
QVector<QTextLayout::FormatRange> formats;
int colon = name.indexOf(QLatin1Char(':'));
QString stashMessage = name.mid(colon + 1, name.length() - (colon + 1));
++colon;
QTextCharFormat bold;
bold.setFontWeight(QFont::Bold);
formats.append({0, colon, bold});
QTextCharFormat fmt;
fmt.setForeground(options.palette.link());
fmt.setFontWeight(QFont::Bold);
auto resFmts = kfts::get_fuzzy_match_formats(m_filterString, stashMessage, colon, fmt);
formats.append(resFmts);
painter->save();
// // paint background
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight());
} else {
painter->fillRect(option.rect, option.palette.base());
}
options.text = QString(); // clear old text
options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter, options.widget);
Utils::paintItemViewText(painter, name, options, formats);
painter->restore();
}
public Q_SLOTS:
void setFilterString(const QString &text)
{
m_filterString = text;
}
private:
QString m_filterString;
};
StashDialog::StashDialog(QWidget *parent, QWidget *window, const QString &gitPath)
: QuickDialog(parent, window)
: HUDDialog(parent, window)
, m_gitPath(gitPath)
{
m_model = new QStandardItemModel(this);
m_proxyModel = new StashFilterModel(this);
m_proxyModel->setSourceModel(m_model);
m_treeView.setModel(m_proxyModel);
StyleDelegate *delegate = new StyleDelegate(this);
m_treeView.setItemDelegateForColumn(0, delegate);
connect(&m_lineEdit, &QLineEdit::textChanged, this, [this, delegate](const QString &string) {
m_proxyModel->setFilterString(string);
delegate->setFilterString(string);
// reselect first
m_treeView.setCurrentIndex(m_proxyModel->index(0, 0));
});
m_proxyModel->setFilterRole(Qt::DisplayRole);
}
void StashDialog::openDialog(StashMode m)
{
m_model->clear();
setStringList({});
switch (m) {
case StashMode::Stash:
......@@ -190,7 +75,20 @@ void StashDialog::openDialog(StashMode m)
exec();
}
void StashDialog::slotReturnPressed()
static QString getStashIndex(const QModelIndex &index)
{
QString s = index.data().toString();
if (s.isEmpty() || !s.startsWith(QStringLiteral("stash@{"))) {
return {};
}
static QRegularExpression re(QStringLiteral("stash@{(.*)}"));
const auto match = re.match(s);
if (!match.hasMatch())
return {};
return match.captured(1);
}
void StashDialog::slotReturnPressed(const QModelIndex &index)
{
switch (m_currentMode) {
case StashMode::Stash:
......@@ -202,17 +100,26 @@ void StashDialog::slotReturnPressed()
case StashMode::StashUntrackIncluded:
stash(false, true);
break;
default:
break;
}
auto stashIndex = getStashIndex(index);
if (stashIndex.isEmpty())
return;
switch (m_currentMode) {
case StashMode::StashApply:
applyStash(m_treeView.currentIndex().data(StashIndexRole).toByteArray());
applyStash(stashIndex);
break;
case StashMode::StashPop:
popStash(m_treeView.currentIndex().data(StashIndexRole).toByteArray());
popStash(stashIndex);
break;
case StashMode::StashDrop:
dropStash(m_treeView.currentIndex().data(StashIndexRole).toByteArray());
dropStash(stashIndex);
break;
case StashMode::ShowStashContent:
showStash(m_treeView.currentIndex().data(StashIndexRole).toByteArray());
showStash(stashIndex);
break;
default:
break;
......@@ -263,39 +170,22 @@ void StashDialog::getStashList()
auto git = gitp({QStringLiteral("stash"), QStringLiteral("list")});
startHostProcess(*git, QProcess::ReadOnly);
QList<QByteArray> stashList;
QStringList stashList;
if (git->waitForStarted() && git->waitForFinished(-1)) {
if (git->exitStatus() == QProcess::NormalExit && git->exitCode() == 0) {
stashList = git->readAllStandardOutput().split('\n');
stashList = QString::fromUtf8(git->readAllStandardOutput()).split(QLatin1Char('\n'));
setStringList(stashList);
} else {
Q_EMIT message(i18n("Failed to get stash list. Error: ") + QString::fromUtf8(git->readAll()), true);
}
}
// format stash@{}: message
for (const auto &stash : qAsConst(stashList)) {
if (!stash.startsWith("stash@{")) {
continue;
}
int brackCloseIdx = stash.indexOf('}', 7);
if (brackCloseIdx < 0) {
continue;
}
QByteArray stashIdx = stash.mid(0, brackCloseIdx + 1);
QStandardItem *item = new QStandardItem(QString::fromUtf8(stash));
item->setData(stashIdx, StashIndexRole);
m_model->appendRow(item);
}
}
void StashDialog::popStash(const QByteArray &index, const QString &command)
void StashDialog::popStash(const QString &index, const QString &command)
{
QStringList args{QStringLiteral("stash"), command};
if (!index.isEmpty()) {
args.append(QString::fromUtf8(index));
args.append(index);
}
auto git = gitp(args);
......@@ -323,23 +213,23 @@ void StashDialog::popStash(const QByteArray &index, const QString &command)
startHostProcess(*git, QProcess::ReadOnly);
}
void StashDialog::applyStash(const QByteArray &index)
void StashDialog::applyStash(const QString &index)
{
popStash(index, QStringLiteral("apply"));
}
void StashDialog::dropStash(const QByteArray &index)
void StashDialog::dropStash(const QString &index)
{
popStash(index, QStringLiteral("drop"));
}
void StashDialog::showStash(const QByteArray &index)
void StashDialog::showStash(const QString &index)
{