diff options
Diffstat (limited to '')
| -rw-r--r-- | src/completion/CommandsAdapter.cpp | 6 | ||||
| -rw-r--r-- | src/completion/CommandsModel.cpp | 5 | ||||
| -rw-r--r-- | src/completion/Completer.cpp | 92 | ||||
| -rw-r--r-- | src/completion/CompleterDelegate.cpp | 8 | ||||
| -rw-r--r-- | src/completion/FuzzySearchProxyModel.cpp | 24 | ||||
| -rw-r--r-- | src/completion/TabsAdapter.cpp | 20 | ||||
| -rw-r--r-- | src/completion/TabsModel.cpp | 47 | ||||
| -rw-r--r-- | src/completion/UrlAdapter.cpp | 6 | ||||
| -rw-r--r-- | src/completion/UrlModel.cpp | 8 |
9 files changed, 179 insertions, 37 deletions
diff --git a/src/completion/CommandsAdapter.cpp b/src/completion/CommandsAdapter.cpp index df71661..020c115 100644 --- a/src/completion/CommandsAdapter.cpp +++ b/src/completion/CommandsAdapter.cpp @@ -8,8 +8,8 @@ const QString cmdPrompt = "[exec]"; CommandsAdapter::CommandsAdapter() : Adapter() { - completerInstance = new Completer(this); - completerInstance->setModel(new CommandsModel(this)); + completerInstance = new Completer(); + completerInstance->setSourceModel(new CommandsModel(this)); } Completer *CommandsAdapter::completer() { return completerInstance; } @@ -17,6 +17,6 @@ Completer *CommandsAdapter::completer() { return completerInstance; } QString CommandsAdapter::prompt() { return cmdPrompt; } CommandsAdapter::~CommandsAdapter() { - delete completerInstance->model(); + delete completerInstance->sourceModel(); delete completerInstance; } diff --git a/src/completion/CommandsModel.cpp b/src/completion/CommandsModel.cpp index 0f66233..670c0e8 100644 --- a/src/completion/CommandsModel.cpp +++ b/src/completion/CommandsModel.cpp @@ -16,10 +16,10 @@ QVariant CommandsModel::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole) return item.name; - else if (role == Qt::ToolTipRole) - return item.description; else if (role == Qt::UserRole) return item.description; + else if (role == Qt::AccessibleDescriptionRole) + return item.name + item.description; return QVariant(); } @@ -28,6 +28,7 @@ QHash<int, QByteArray> CommandsModel::roleNames() const { QHash<int, QByteArray> roles; roles[Qt::DisplayRole] = "name"; roles[Qt::UserRole] = "description"; + roles[Qt::AccessibleDescriptionRole] = "full"; return roles; } diff --git a/src/completion/Completer.cpp b/src/completion/Completer.cpp index a38d3f2..27a7b77 100644 --- a/src/completion/Completer.cpp +++ b/src/completion/Completer.cpp @@ -1,8 +1,13 @@ #include <QAbstractItemView> #include <QHeaderView> +#include <QKeyEvent> #include <QLineEdit> #include <QTreeView> #include <QWidget> +#include <QtCore/qabstractitemmodel.h> +#include <QtCore/qitemselectionmodel.h> +#include <QtCore/qnamespace.h> +#include <QtCore/qsortfilterproxymodel.h> #include <QtCore> #include "completion/Completer.hpp" @@ -12,33 +17,70 @@ const char *completerStyles = R"( background-color: #111; color: #fff; border-radius: 0; + width: 100%; )"; -Completer::Completer(QObject *parent) : QCompleter(parent) { - setFilterMode(Qt::MatchContains); - setCaseSensitivity(Qt::CaseInsensitive); - setCompletionRole(Qt::DisplayRole); - setCompletionMode(QCompleter::PopupCompletion); +Completer::Completer() : QWidget() { + view = new QTreeView(this); + viewDelegate = new CompleterDelegate(view); + view->setStyleSheet(completerStyles); + view->setItemDelegate(viewDelegate); + view->setRootIsDecorated(false); + view->setUniformRowHeights(true); + view->header()->hide(); + view->setColumnWidth(1, 500); + view->setModel(&proxyModel); + view->setFocusPolicy(Qt::NoFocus); + view->setEditTriggers(QAbstractItemView::NoEditTriggers); + view->setSelectionMode(QAbstractItemView::SingleSelection); +} + +void Completer::setSourceModel(QAbstractItemModel *model) { + proxyModel.setSourceModel(model); +} + +void Completer::onTextChange(QString text) { + proxyModel.setFilterWildcard(text); +} + +void Completer::setHighlightedRow(uint32_t row) { + viewDelegate->setCurrentRow(row); + view->update(); +} - auto treeView = new QTreeView(); - setPopup(treeView); - treeView->setStyleSheet(completerStyles); - treeView->setItemDelegate(new CompleterDelegate(treeView)); - treeView->setRootIsDecorated(false); - treeView->setUniformRowHeights(true); - treeView->header()->hide(); - treeView->setColumnWidth(1, 300); +void Completer::acceptHighlighted() { + auto index = proxyModel.index(viewDelegate->currentRow(), 0); + auto text = proxyModel.data(index, Qt::DisplayRole).toString(); + emit accepted(text); } -// bool Completer::eventFilter(QObject *watched, QEvent *event) { -// auto isFocusIn = event->type() == QEvent::FocusIn; -// auto isKeyRelease = event->type() == QEvent::KeyRelease; -// -// QLineEdit *lineEdit = qobject_cast<QLineEdit *>(watched); -// bool isInputEmpty = lineEdit && lineEdit->text().isEmpty(); -// -// if ((isFocusIn || isKeyRelease) && isInputEmpty) -// complete(); -// -// return QCompleter::eventFilter(watched, event); -// } +bool Completer::onKeyPressEvent(QKeyEvent *event) { + auto combo = event->keyCombination(); + auto row = viewDelegate->currentRow(); + + // If there are no matches, nothing to handle here + if (proxyModel.rowCount() == 0) + return false; + + if (combo.key() == Qt::Key_Up) { + setHighlightedRow(row <= 0 ? proxyModel.rowCount() - 1 : row - 1); + return true; + } + + if (combo.key() == Qt::Key_Down) { + setHighlightedRow((row + 1) % proxyModel.rowCount()); + return true; + } + + if (combo.key() == Qt::Key_Tab) { + acceptHighlighted(); + return true; + } + + if (combo.key() == Qt::Key_Return) { + acceptHighlighted(); + return false; + } + + return false; +} diff --git a/src/completion/CompleterDelegate.cpp b/src/completion/CompleterDelegate.cpp index a35305f..ce446b2 100644 --- a/src/completion/CompleterDelegate.cpp +++ b/src/completion/CompleterDelegate.cpp @@ -9,13 +9,19 @@ CompleterDelegate::CompleterDelegate(QObject *parent) : QStyledItemDelegate(parent) {} +void CompleterDelegate::setCurrentRow(uint32_t r) { row = r; } + +uint32_t CompleterDelegate::currentRow() { return row; } + void CompleterDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { painter->save(); + bool selected = index.row() == row; + // Style - if (option.state & QStyle::State_Selected) { + if (selected) { painter->fillRect(option.rect, QColor("#aaa")); painter->setPen(Qt::black); } else { diff --git a/src/completion/FuzzySearchProxyModel.cpp b/src/completion/FuzzySearchProxyModel.cpp new file mode 100644 index 0000000..151f60e --- /dev/null +++ b/src/completion/FuzzySearchProxyModel.cpp @@ -0,0 +1,24 @@ +#include <QWidget> +#include <QtCore> + +#include "completion/FuzzySearchProxyModel.hpp" + +bool FuzzySearchProxyModel::filterAcceptsRow( + int sourceRow, const QModelIndex &sourceParent) const { + QAbstractItemModel *model = sourceModel(); + QString filterText = filterRegularExpression().pattern(); + qDebug() << "Searching: " << filterText; + + for (int col = 0; col < model->columnCount(); ++col) { + QModelIndex index = model->index(sourceRow, col, sourceParent); + QString data = model->data(index, Qt::AccessibleDescriptionRole).toString(); + + qDebug() << " against: " << data; + + // TODO: Fuzzy eet up + if (data.contains(filterText, Qt::CaseInsensitive)) { + return true; + } + } + return false; +} diff --git a/src/completion/TabsAdapter.cpp b/src/completion/TabsAdapter.cpp new file mode 100644 index 0000000..4854b7d --- /dev/null +++ b/src/completion/TabsAdapter.cpp @@ -0,0 +1,20 @@ +#include <QWidget> +#include <QtCore> + +#include "completion/Completer.hpp" +#include "completion/TabsAdapter.hpp" +#include "completion/TabsModel.hpp" + +TabsAdapter::TabsAdapter() : Adapter() { + completerInstance = new Completer(); + completerInstance->setSourceModel(new TabsModel(this)); +} + +Completer *TabsAdapter::completer() { return completerInstance; } + +QString TabsAdapter::prompt() { return "[tabs]"; } + +TabsAdapter::~TabsAdapter() { + delete completerInstance->sourceModel(); + delete completerInstance; +} diff --git a/src/completion/TabsModel.cpp b/src/completion/TabsModel.cpp new file mode 100644 index 0000000..37eb1dd --- /dev/null +++ b/src/completion/TabsModel.cpp @@ -0,0 +1,47 @@ +#include <QAbstractListModel> +#include <QWidget> +#include <QtCore> + +#include "completion/TabsModel.hpp" + +TabsModel::TabsModel(QObject *parent) : QAbstractListModel(parent) { + items = { + {.url = "https://ediblemonad.dev", .title = "EdibleMonad website"}, + {.url = "https://lite.duckduckgo.com", .title = "DDG lite"}, + {.url = "https://github.com/trending", .title = "Github trending"}, + }; +} + +QVariant TabsModel::data(const QModelIndex &index, int role) const { + if (!index.isValid() || index.row() >= items.size()) + return QVariant(); + + const Buffer &item = items.at(index.row()); + + if (role == Qt::DisplayRole) + return index.row(); + else if (role == Qt::UserRole) + return item.title; + else if (role == Qt::AccessibleDescriptionRole) + return item.url + item.title; + + return QVariant(); +} + +QHash<int, QByteArray> TabsModel::roleNames() const { + QHash<int, QByteArray> roles; + roles[Qt::DisplayRole] = "url"; + roles[Qt::UserRole] = "title"; + roles[Qt::AccessibleDescriptionRole] = "full"; + return roles; +} + +int TabsModel::rowCount(const QModelIndex &parent) const { + Q_UNUSED(parent); + return items.size(); +} + +int TabsModel::columnCount(const QModelIndex &parent) const { + Q_UNUSED(parent); + return 2; // url + title +} diff --git a/src/completion/UrlAdapter.cpp b/src/completion/UrlAdapter.cpp index 2f1bb08..54caa0d 100644 --- a/src/completion/UrlAdapter.cpp +++ b/src/completion/UrlAdapter.cpp @@ -6,8 +6,8 @@ #include "completion/UrlModel.hpp" UrlAdapter::UrlAdapter() : Adapter() { - completerInstance = new Completer(this); - completerInstance->setModel(new UrlModel(this)); + completerInstance = new Completer(); + completerInstance->setSourceModel(new UrlModel(this)); } Completer *UrlAdapter::completer() { return completerInstance; } @@ -15,6 +15,6 @@ Completer *UrlAdapter::completer() { return completerInstance; } QString UrlAdapter::prompt() { return "[url]"; } UrlAdapter::~UrlAdapter() { - delete completerInstance->model(); + delete completerInstance->sourceModel(); delete completerInstance; } diff --git a/src/completion/UrlModel.cpp b/src/completion/UrlModel.cpp index 7671fc3..8c5d75c 100644 --- a/src/completion/UrlModel.cpp +++ b/src/completion/UrlModel.cpp @@ -1,5 +1,6 @@ #include <QAbstractListModel> #include <QWidget> +#include <QtCore/qnamespace.h> #include <QtCore> #include "completion/UrlModel.hpp" @@ -22,10 +23,10 @@ QVariant UrlModel::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole) return item.url; - else if (role == Qt::ToolTipRole) - return item.description; else if (role == Qt::UserRole) return item.description; + else if (role == Qt::AccessibleDescriptionRole) + return item.url + " " + item.description; return QVariant(); } @@ -34,6 +35,7 @@ QHash<int, QByteArray> UrlModel::roleNames() const { QHash<int, QByteArray> roles; roles[Qt::DisplayRole] = "url"; roles[Qt::UserRole] = "description"; + roles[Qt::AccessibleDescriptionRole] = "full"; return roles; } @@ -44,5 +46,5 @@ int UrlModel::rowCount(const QModelIndex &parent) const { int UrlModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); - return 2; // name + description + return 2; // url + description } |
