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

Kate Command Bar: Initial Commit

parent 42706c4e
......@@ -84,13 +84,15 @@ target_sources(
katemwmodonhddialog.cpp
katepluginmanager.cpp
katerunninginstanceinfo.cpp
katesavemodifieddialog.cpp
katetabbar.cpp
kateviewmanager.cpp
kateviewspace.cpp
katewaiter.cpp
katecommandbar.cpp
commandmodel.cpp
)
# Executable only adds the main definition.
......
#include "commandmodel.h"
#include <QAction>
#include <KLocalizedString>
#include <QDebug>
CommandModel::CommandModel(QObject *parent)
: QAbstractTableModel(parent)
{
}
void CommandModel::refresh(QList<QAction *> actions)
{
QVector<Item> temp;
temp.reserve(actions.size());
for (auto action : actions) {
temp.push_back({action, 0});
}
beginResetModel();
m_rows = std::move(temp);
endResetModel();
}
QVariant CommandModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return {};
auto entry = m_rows[index.row()];
int col = index.column();
switch (role)
{
case Qt::DisplayRole:
if (col == 0)
return KLocalizedString::removeAcceleratorMarker(entry.action->text());
else
return entry.action->shortcut().toString();
case Qt::DecorationRole:
if (col == 0)
return entry.action->icon();
break;
case Qt::TextAlignmentRole:
if (col == 0)
return Qt::AlignLeft;
else
return Qt::AlignRight;
case Qt::UserRole:
{
QVariant v;
v.setValue(entry.action);
return v;
}
case Role::Score:
return entry.score;
}
return {};
}
#ifndef COMMANDMODEL_H
#define COMMANDMODEL_H
#include <QAbstractTableModel>
#include <QVector>
class QAction;
class CommandModel : public QAbstractTableModel
{
struct Item
{
QAction* action;
int score;
};
Q_OBJECT
public:
CommandModel(QObject* parent = nullptr);
enum Role { Score = Qt::UserRole + 1 };
void refresh(QList<QAction*> actions);
int rowCount(const QModelIndex & parent = QModelIndex()) const override
{
if (parent.isValid()) {
return 0;
}
return m_rows.size();
}
int columnCount(const QModelIndex & parent = QModelIndex()) const override
{
Q_UNUSED(parent);
return 2;
}
bool setData(const QModelIndex &index, const QVariant &value, int role) override
{
if (!index.isValid())
return false;
if (role == Role::Score) {
auto row = index.row();
m_rows[row].score = value.toInt();
}
return QAbstractTableModel::setData(index, value, role);
}
QVariant data(const QModelIndex &index, int role) const override;
private:
QVector<Item> m_rows;
};
#endif // COMMANDMODEL_H
#include "katecommandbar.h"
#include "commandmodel.h"
#include <QTreeView>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QDebug>
#include <QAction>
#include <QPropertyAnimation>
#include <QPointer>
#include <QKeyEvent>
#include <QCoreApplication>
#include <QSortFilterProxyModel>
#include <kfts_fuzzy_match.h>
class CommandBarFilterModel : public QSortFilterProxyModel
{
public:
CommandBarFilterModel(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
{
const int l = sourceLeft.data(CommandModel::Score).toInt();
const int r = sourceRight.data(CommandModel::Score).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 actionName = idx.data().toString();
const bool res = kfts::fuzzy_match(m_pattern, actionName, score);
sourceModel()->setData(idx, score, CommandModel::Score);
return res;
}
private:
QString m_pattern;
};
KateCommandBar::KateCommandBar(QWidget *parent)
: QMenu(parent)
{
QVBoxLayout *layout = new QVBoxLayout();
layout->setSpacing(0);
layout->setContentsMargins(4, 4, 4, 4);
setLayout(layout);
m_lineEdit = new QLineEdit(this);
setFocusProxy(m_lineEdit);
layout->addWidget(m_lineEdit);
m_treeView = new QTreeView();
layout->addWidget(m_treeView, 1);
m_treeView->setTextElideMode(Qt::ElideLeft);
m_treeView->setUniformRowHeights(true);
m_model = new CommandModel(this);
m_proxyModel = new CommandBarFilterModel(this);
m_proxyModel->setFilterRole(Qt::DisplayRole);
m_proxyModel->setSortRole(CommandModel::Score);
m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
m_proxyModel->setFilterKeyColumn(0);
connect(m_lineEdit, &QLineEdit::returnPressed, this, &KateCommandBar::slotReturnPressed);
connect(m_lineEdit, &QLineEdit::textChanged, m_proxyModel, &CommandBarFilterModel::setFilterString);
connect(m_lineEdit, &QLineEdit::textChanged, m_proxyModel, [this](){ reselectFirst(); });
m_proxyModel->setSourceModel(m_model);
m_treeView->setSortingEnabled(true);
m_treeView->setModel(m_proxyModel);
m_treeView->installEventFilter(this);
m_lineEdit->installEventFilter(this);
m_treeView->setHeaderHidden(true);
m_treeView->setRootIsDecorated(false);
m_treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_treeView->setSelectionMode(QTreeView::SingleSelection);
setHidden(true);
}
void KateCommandBar::updateBar(QList<QAction *> actions)
{
m_model->refresh(actions);
reselectFirst();
updateViewGeometry();
show();
setFocus();
}
bool KateCommandBar::eventFilter(QObject *obj, QEvent *event)
{
// catch key presses + shortcut overrides to allow to have ESC as application wide shortcut, too, see bug 409856
if (event->type() == QEvent::KeyPress || event->type() == QEvent::ShortcutOverride) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (obj == m_lineEdit) {
const bool forward2list = (keyEvent->key() == Qt::Key_Up) || (keyEvent->key() == Qt::Key_Down) || (keyEvent->key() == Qt::Key_PageUp) || (keyEvent->key() == Qt::Key_PageDown);
if (forward2list) {
QCoreApplication::sendEvent(m_treeView, event);
return true;
}
if (keyEvent->key() == Qt::Key_Escape) {
m_lineEdit->clear();
keyEvent->accept();
hide();
return true;
}
} else {
const bool forward2input = (keyEvent->key() != Qt::Key_Up) && (keyEvent->key() != Qt::Key_Down) && (keyEvent->key() != Qt::Key_PageUp) && (keyEvent->key() != Qt::Key_PageDown) && (keyEvent->key() != Qt::Key_Tab) &&
(keyEvent->key() != Qt::Key_Backtab);
if (forward2input) {
QCoreApplication::sendEvent(m_lineEdit, event);
return true;
}
}
}
// hide on focus out, if neither input field nor list have focus!
else if (event->type() == QEvent::FocusOut && !(m_lineEdit->hasFocus() || m_treeView->hasFocus())) {
m_lineEdit->clear();
hide();
return true;
}
return QWidget::eventFilter(obj, event);
}
void KateCommandBar::slotReturnPressed()
{
auto act = m_proxyModel->data(m_treeView->currentIndex(), Qt::UserRole).value<QAction*>();
if (act)
act->trigger();
m_lineEdit->clear();
hide();
}
void KateCommandBar::reselectFirst()
{
QModelIndex index = m_proxyModel->index(0, 0);
m_treeView->setCurrentIndex(index);
}
void KateCommandBar::updateViewGeometry()
{
m_treeView->resizeColumnToContents(0);
m_treeView->resizeColumnToContents(1);
const QSize centralSize = parentWidget()->size();
// width: 2.4 of editor, height: 1/2 of editor
const QSize viewMaxSize(centralSize.width() / 2.4, centralSize.height() / 2);
const int rowHeight = m_treeView->sizeHintForRow(0) == -1 ? 0 : m_treeView->sizeHintForRow(0);
const int width = viewMaxSize.width();
const QSize viewSize(std::max(300, width), // never go below this
std::min(std::max(rowHeight * m_model->rowCount() + 2, rowHeight * 6), viewMaxSize.height()));
// Position should be central over window
const int xPos = std::max(0, (centralSize.width() - viewSize.width()) / 2);
const int yPos = std::max(0, (centralSize.height() - viewSize.height()) * 1 / 4);
const QPoint p(xPos, yPos);
move(p + parentWidget()->pos());
this->setFixedSize(viewSize);
QPointer<QPropertyAnimation> animation = new QPropertyAnimation(this, "size");
animation->setDuration(150);
animation->setStartValue(this->size());
animation->setEndValue(viewSize);
animation->start();
}
#include <QMenu>
#include <QList>
class QTreeView;
class QLineEdit;
class CommandModel;
class QAction;
class CommandBarFilterModel;
class KateCommandBar : public QMenu
{
Q_OBJECT
public:
KateCommandBar(QWidget* parent = nullptr);
void updateBar(QList<QAction*> actions);
void updateViewGeometry();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private Q_SLOTS:
void slotReturnPressed();
void reselectFirst();
private:
QTreeView* m_treeView;
QLineEdit* m_lineEdit;
CommandModel* m_model;
CommandBarFilterModel* m_proxyModel;
};
......@@ -25,6 +25,7 @@
#include "katesessionsaction.h"
#include "kateupdatedisabler.h"
#include "kateviewspace.h"
#include "katecommandbar.h"
#include <KAboutData>
#include <KActionCollection>
......@@ -252,6 +253,12 @@ void KateMainWindow::setupImportantActions()
actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_O));
connect(a, &QAction::triggered, this, &KateMainWindow::slotQuickOpen);
a->setWhatsThis(i18n("Open a form to quick open documents."));
a = actionCollection()->addAction(QStringLiteral("view_commandbar_open"));
// a->setIcon(QIcon::fromTheme(QStringLiteral("quickopen")));
// a->setText(i18n("&Quick Open"));
actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_I));
connect(a, &QAction::triggered, this, &KateMainWindow::slotCommandBarOpen);
}
void KateMainWindow::setupMainWindow()
......@@ -267,6 +274,8 @@ void KateMainWindow::setupMainWindow()
m_quickOpen = new KateQuickOpen(this);
m_commandBar = new KateCommandBar(this);
m_viewManager = new KateViewManager(m_mainStackedWidget, this);
m_mainStackedWidget->addWidget(m_viewManager);
......@@ -1208,6 +1217,19 @@ void KateMainWindow::slotQuickOpen()
centralWidget()->setFocusProxy(m_quickOpen);
}
void KateMainWindow::slotCommandBarOpen()
{
QList<QAction*> acts;
auto clients = guiFactory()->clients();
for (auto c : clients) {
acts.append(c->actionCollection()->actions());
}
m_commandBar->updateBar(acts);
centralWidget()->setFocusProxy(m_commandBar);
}
QWidget *KateMainWindow::createToolView(KTextEditor::Plugin *plugin, const QString &identifier, KTextEditor::MainWindow::ToolViewPosition pos, const QIcon &icon, const QString &text)
{
return KateMDI::MainWindow::createToolView(plugin, identifier, static_cast<KMultiTabBar::KMultiTabBarPosition>(pos), icon, text);
......
......@@ -43,6 +43,7 @@ class KateViewManager;
class KateMwModOnHdDialog;
class KateQuickOpen;
enum KateQuickOpenModelList : int;
class KateCommandBar;
// Helper layout class to always provide minimum size
class KateContainerStackedLayout : public QStackedLayout
......@@ -171,6 +172,8 @@ public Q_SLOTS:
*/
void slotQuickOpen();
void slotCommandBarOpen();
/**
* Overwrite size hint for better default window sizes
* @return size hint
......@@ -540,6 +543,11 @@ private:
*/
KateQuickOpen *m_quickOpen = nullptr;
/**
* quick command bar to quickly trigger any action
*/
KateCommandBar* m_commandBar = nullptr;
/**
* keeps track of views
*/
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment