decouple VVimIndicator and VVimCmdLineEdit

This commit is contained in:
Le Tan 2018-01-19 19:29:34 +08:00
parent eb25aec5b5
commit b927a525e2
15 changed files with 590 additions and 404 deletions

View File

@ -107,7 +107,8 @@ SOURCES += main.cpp\
vwaitingwidget.cpp \ vwaitingwidget.cpp \
utils/vwebutils.cpp \ utils/vwebutils.cpp \
vlineedit.cpp \ vlineedit.cpp \
vcart.cpp vcart.cpp \
vvimcmdlineedit.cpp
HEADERS += vmainwindow.h \ HEADERS += vmainwindow.h \
vdirectorytree.h \ vdirectorytree.h \
@ -201,7 +202,8 @@ HEADERS += vmainwindow.h \
vwaitingwidget.h \ vwaitingwidget.h \
utils/vwebutils.h \ utils/vwebutils.h \
vlineedit.h \ vlineedit.h \
vcart.h vcart.h \
vvimcmdlineedit.h
RESOURCES += \ RESOURCES += \
vnote.qrc \ vnote.qrc \

View File

@ -100,3 +100,8 @@ void VEditOperations::setVimMode(VimMode p_mode)
m_vim->setMode(p_mode); m_vim->setMode(p_mode);
} }
} }
VVim *VEditOperations::getVim() const
{
return m_vim;
}

View File

@ -47,6 +47,8 @@ public:
// Set Vim mode if not NULL. // Set Vim mode if not NULL.
void setVimMode(VimMode p_mode); void setVimMode(VimMode p_mode);
VVim *getVim() const;
signals: signals:
// Want to display a template message in status bar. // Want to display a template message in status bar.
void statusMessage(const QString &p_msg); void statusMessage(const QString &p_msg);

View File

@ -970,3 +970,12 @@ void VEditor::setVimMode(VimMode p_mode)
m_editOps->setVimMode(p_mode); m_editOps->setVimMode(p_mode);
} }
} }
VVim *VEditor::getVim() const
{
if (m_editOps) {
return m_editOps->getVim();
}
return NULL;
}

View File

@ -142,6 +142,8 @@ public:
void setVimMode(VimMode p_mode); void setVimMode(VimMode p_mode);
VVim *getVim() const;
virtual QString getContent() const = 0; virtual QString getContent() const = 0;
// @p_modified: if true, delete the whole content and insert the new content. // @p_modified: if true, delete the whole content and insert the new content.

View File

@ -195,3 +195,44 @@ void VEditTab::handleFileOrDirectoryChange(bool p_isFile, UpdateAction p_act)
Q_UNUSED(p_isFile); Q_UNUSED(p_isFile);
Q_UNUSED(p_act); Q_UNUSED(p_act);
} }
void VEditTab::handleVimCmdCommandCancelled()
{
}
void VEditTab::handleVimCmdCommandFinished(VVim::CommandLineType p_type, const QString &p_cmd)
{
Q_UNUSED(p_type);
Q_UNUSED(p_cmd);
}
void VEditTab::handleVimCmdCommandChanged(VVim::CommandLineType p_type, const QString &p_cmd)
{
Q_UNUSED(p_type);
Q_UNUSED(p_cmd);
}
QString VEditTab::handleVimCmdRequestNextCommand(VVim::CommandLineType p_type, const QString &p_cmd)
{
Q_UNUSED(p_type);
Q_UNUSED(p_cmd);
return QString();
}
QString VEditTab::handleVimCmdRequestPreviousCommand(VVim::CommandLineType p_type, const QString &p_cmd)
{
Q_UNUSED(p_type);
Q_UNUSED(p_cmd);
return QString();
}
QString VEditTab::handleVimCmdRequestRegister(int p_key, int p_modifiers)
{
Q_UNUSED(p_key);
Q_UNUSED(p_modifiers);
return QString();
}

View File

@ -118,6 +118,18 @@ public slots:
// Enter edit mode // Enter edit mode
virtual void editFile() = 0; virtual void editFile() = 0;
virtual void handleVimCmdCommandCancelled();
virtual void handleVimCmdCommandFinished(VVim::CommandLineType p_type, const QString &p_cmd);
virtual void handleVimCmdCommandChanged(VVim::CommandLineType p_type, const QString &p_cmd);
virtual QString handleVimCmdRequestNextCommand(VVim::CommandLineType p_type, const QString &p_cmd);
virtual QString handleVimCmdRequestPreviousCommand(VVim::CommandLineType p_type, const QString &p_cmd);
virtual QString handleVimCmdRequestRegister(int p_key, int p_modifiers);
protected: protected:
void wheelEvent(QWheelEvent *p_event) Q_DECL_OVERRIDE; void wheelEvent(QWheelEvent *p_event) Q_DECL_OVERRIDE;
@ -180,6 +192,9 @@ signals:
// Request to close itself. // Request to close itself.
void closeRequested(VEditTab *p_tab); void closeRequested(VEditTab *p_tab);
// Request main window to show Vim cmd line.
void triggerVimCmd(VVim::CommandLineType p_type);
private slots: private slots:
// Called when app focus changed. // Called when app focus changed.
void handleFocusChanged(QWidget *p_old, QWidget *p_now); void handleFocusChanged(QWidget *p_old, QWidget *p_now);

View File

@ -20,6 +20,7 @@
#include "vexporter.h" #include "vexporter.h"
#include "vmdtab.h" #include "vmdtab.h"
#include "vvimindicator.h" #include "vvimindicator.h"
#include "vvimcmdlineedit.h"
#include "vtabindicator.h" #include "vtabindicator.h"
#include "dialog/vupdater.h" #include "dialog/vupdater.h"
#include "vorphanfile.h" #include "vorphanfile.h"
@ -240,6 +241,8 @@ void VMainWindow::setupUI()
setCentralWidget(m_mainSplitter); setCentralWidget(m_mainSplitter);
initVimCmd();
m_vimIndicator = new VVimIndicator(this); m_vimIndicator = new VVimIndicator(this);
m_vimIndicator->hide(); m_vimIndicator->hide();
@ -247,6 +250,7 @@ void VMainWindow::setupUI()
m_tabIndicator->hide(); m_tabIndicator->hide();
// Create and show the status bar // Create and show the status bar
statusBar()->addPermanentWidget(m_vimCmd);
statusBar()->addPermanentWidget(m_vimIndicator); statusBar()->addPermanentWidget(m_vimIndicator);
statusBar()->addPermanentWidget(m_tabIndicator); statusBar()->addPermanentWidget(m_tabIndicator);
@ -1835,7 +1839,25 @@ void VMainWindow::updateActionsStateFromTab(const VEditTab *p_tab)
void VMainWindow::handleAreaTabStatusUpdated(const VEditTabInfo &p_info) void VMainWindow::handleAreaTabStatusUpdated(const VEditTabInfo &p_info)
{ {
if (m_curTab != p_info.m_editTab) {
if (m_curTab) {
if (m_vimCmd->isVisible()) {
m_curTab->handleVimCmdCommandCancelled();
}
// Disconnect the trigger signal from edit tab.
disconnect(m_curTab, 0, m_vimCmd, 0);
}
m_curTab = p_info.m_editTab; m_curTab = p_info.m_editTab;
if (m_curTab) {
connect(m_curTab, &VEditTab::triggerVimCmd,
m_vimCmd, &VVimCmdLineEdit::reset);
}
m_vimCmd->hide();
}
if (m_curTab) { if (m_curTab) {
m_curFile = m_curTab->getFile(); m_curFile = m_curTab->getFile();
} else { } else {
@ -2376,7 +2398,7 @@ void VMainWindow::updateStatusInfo(const VEditTabInfo &p_info)
void VMainWindow::handleVimStatusUpdated(const VVim *p_vim) void VMainWindow::handleVimStatusUpdated(const VVim *p_vim)
{ {
m_vimIndicator->update(p_vim, m_curTab); m_vimIndicator->update(p_vim);
if (!p_vim || !m_curTab || !m_curTab->isEditMode()) { if (!p_vim || !m_curTab || !m_curTab->isEditMode()) {
m_vimIndicator->hide(); m_vimIndicator->hide();
} else { } else {
@ -2826,3 +2848,80 @@ void VMainWindow::customShortcut()
this); this);
dialog.exec(); dialog.exec();
} }
void VMainWindow::initVimCmd()
{
m_vimCmd = new VVimCmdLineEdit(this);
m_vimCmd->setProperty("VimCommandLine", true);
connect(m_vimCmd, &VVimCmdLineEdit::commandCancelled,
this, [this]() {
if (m_curTab) {
m_curTab->focusTab();
}
// NOTICE: should not hide before setting focus to edit tab.
m_vimCmd->hide();
if (m_curTab) {
m_curTab->handleVimCmdCommandCancelled();
}
});
connect(m_vimCmd, &VVimCmdLineEdit::commandFinished,
this, [this](VVim::CommandLineType p_type, const QString &p_cmd) {
if (m_curTab) {
m_curTab->focusTab();
}
m_vimCmd->hide();
// Hide the cmd line edit before execute the command.
// If we execute the command first, we will get Chinese input
// method enabled after returning to read mode.
if (m_curTab) {
m_curTab->handleVimCmdCommandFinished(p_type, p_cmd);
}
});
connect(m_vimCmd, &VVimCmdLineEdit::commandChanged,
this, [this](VVim::CommandLineType p_type, const QString &p_cmd) {
if (m_curTab) {
m_curTab->handleVimCmdCommandChanged(p_type, p_cmd);
}
});
connect(m_vimCmd, &VVimCmdLineEdit::requestNextCommand,
this, [this](VVim::CommandLineType p_type, const QString &p_cmd) {
if (m_curTab) {
QString cmd = m_curTab->handleVimCmdRequestNextCommand(p_type, p_cmd);
if (!cmd.isNull()) {
m_vimCmd->setCommand(cmd);
} else {
m_vimCmd->restoreUserLastInput();
}
}
});
connect(m_vimCmd, &VVimCmdLineEdit::requestPreviousCommand,
this, [this](VVim::CommandLineType p_type, const QString &p_cmd) {
if (m_curTab) {
QString cmd = m_curTab->handleVimCmdRequestPreviousCommand(p_type, p_cmd);
if (!cmd.isNull()) {
m_vimCmd->setCommand(cmd);
}
}
});
connect(m_vimCmd, &VVimCmdLineEdit::requestRegister,
this, [this](int p_key, int p_modifiers){
if (m_curTab) {
QString val = m_curTab->handleVimCmdRequestRegister(p_key, p_modifiers);
if (!val.isEmpty()) {
m_vimCmd->setText(m_vimCmd->text() + val);
}
}
});
m_vimCmd->hide();
}

View File

@ -30,6 +30,7 @@ class VNotebookSelector;
class VFindReplaceDialog; class VFindReplaceDialog;
class VCaptain; class VCaptain;
class VVimIndicator; class VVimIndicator;
class VVimCmdLineEdit;
class VTabIndicator; class VTabIndicator;
class VSingleInstanceGuard; class VSingleInstanceGuard;
class QTimer; class QTimer;
@ -213,6 +214,8 @@ private:
void initEditorStyleMenu(QMenu *p_emnu); void initEditorStyleMenu(QMenu *p_emnu);
void updateWindowTitle(const QString &str); void updateWindowTitle(const QString &str);
void initVimCmd();
// Update state of actions according to @p_tab. // Update state of actions according to @p_tab.
void updateActionsStateFromTab(const VEditTab *p_tab); void updateActionsStateFromTab(const VEditTab *p_tab);
@ -311,7 +314,11 @@ private:
VCart *m_cart; VCart *m_cart;
VFindReplaceDialog *m_findReplaceDialog; VFindReplaceDialog *m_findReplaceDialog;
VVimCmdLineEdit *m_vimCmd;
VVimIndicator *m_vimIndicator; VVimIndicator *m_vimIndicator;
VTabIndicator *m_tabIndicator; VTabIndicator *m_tabIndicator;
// SinglePanel, TwoPanels, CompactMode. // SinglePanel, TwoPanels, CompactMode.

View File

@ -472,6 +472,15 @@ void VMdTab::setupMarkdownEditor()
connect(m_editor, &VMdEditor::requestTextToHtml, connect(m_editor, &VMdEditor::requestTextToHtml,
this, &VMdTab::textToHtmlViaWebView); this, &VMdTab::textToHtmlViaWebView);
if (m_editor->getVim()) {
connect(m_editor->getVim(), &VVim::commandLineTriggered,
this, [this](VVim::CommandLineType p_type) {
if (m_isEditMode) {
emit triggerVimCmd(p_type);
}
});
}
enableHeadingSequence(m_enableHeadingSequence); enableHeadingSequence(m_enableHeadingSequence);
m_editor->reloadFile(); m_editor->reloadFile();
m_stacks->addWidget(m_editor); m_stacks->addWidget(m_editor);
@ -1030,3 +1039,77 @@ void VMdTab::textToHtmlViaWebView(const QString &p_text)
m_document->textToHtmlAsync(p_text); m_document->textToHtmlAsync(p_text);
} }
void VMdTab::handleVimCmdCommandCancelled()
{
if (m_isEditMode) {
VVim *vim = getEditor()->getVim();
if (vim) {
vim->processCommandLineCancelled();
}
}
}
void VMdTab::handleVimCmdCommandFinished(VVim::CommandLineType p_type, const QString &p_cmd)
{
if (m_isEditMode) {
VVim *vim = getEditor()->getVim();
if (vim) {
vim->processCommandLine(p_type, p_cmd);
}
}
}
void VMdTab::handleVimCmdCommandChanged(VVim::CommandLineType p_type, const QString &p_cmd)
{
Q_UNUSED(p_type);
Q_UNUSED(p_cmd);
if (m_isEditMode) {
VVim *vim = getEditor()->getVim();
if (vim) {
vim->processCommandLineChanged(p_type, p_cmd);
}
}
}
QString VMdTab::handleVimCmdRequestNextCommand(VVim::CommandLineType p_type, const QString &p_cmd)
{
Q_UNUSED(p_type);
Q_UNUSED(p_cmd);
if (m_isEditMode) {
VVim *vim = getEditor()->getVim();
if (vim) {
return vim->getNextCommandHistory(p_type, p_cmd);
}
}
return QString();
}
QString VMdTab::handleVimCmdRequestPreviousCommand(VVim::CommandLineType p_type, const QString &p_cmd)
{
Q_UNUSED(p_type);
Q_UNUSED(p_cmd);
if (m_isEditMode) {
VVim *vim = getEditor()->getVim();
if (vim) {
return vim->getPreviousCommandHistory(p_type, p_cmd);
}
}
return QString();
}
QString VMdTab::handleVimCmdRequestRegister(int p_key, int p_modifiers)
{
Q_UNUSED(p_key);
Q_UNUSED(p_modifiers);
if (m_isEditMode) {
VVim *vim = getEditor()->getVim();
if (vim) {
return vim->readRegister(p_key, p_modifiers);
}
}
return QString();
}

View File

@ -91,6 +91,18 @@ public slots:
// Enter edit mode. // Enter edit mode.
void editFile() Q_DECL_OVERRIDE; void editFile() Q_DECL_OVERRIDE;
void handleVimCmdCommandCancelled();
void handleVimCmdCommandFinished(VVim::CommandLineType p_type, const QString &p_cmd);
void handleVimCmdCommandChanged(VVim::CommandLineType p_type, const QString &p_cmd);
QString handleVimCmdRequestNextCommand(VVim::CommandLineType p_type, const QString &p_cmd);
QString handleVimCmdRequestPreviousCommand(VVim::CommandLineType p_type, const QString &p_cmd);
QString handleVimCmdRequestRegister(int p_key, int p_modifiers);
protected: protected:
void writeBackupFile() Q_DECL_OVERRIDE; void writeBackupFile() Q_DECL_OVERRIDE;

232
src/vvimcmdlineedit.cpp Normal file
View File

@ -0,0 +1,232 @@
#include "vvimcmdlineedit.h"
#include <QInputMethod>
#include <QGuiApplication>
#include "vpalette.h"
extern VPalette *g_palette;
VVimCmdLineEdit::VVimCmdLineEdit(QWidget *p_parent)
: VLineEdit(p_parent), m_type(VVim::CommandLineType::Invalid),
m_registerPending(false), m_enableInputMethod(true)
{
// When user delete all the text, cancel command input.
connect(this, &VVimCmdLineEdit::textChanged,
this, [this](const QString &p_text){
if (p_text.isEmpty()) {
emit commandCancelled();
} else {
emit commandChanged(m_type, p_text.right(p_text.size() - 1));
}
});
connect(this, &VVimCmdLineEdit::textEdited,
this, [this](const QString &p_text){
if (p_text.size() < 2) {
m_userLastInput.clear();
} else {
m_userLastInput = p_text.right(p_text.size() - 1);
}
});
m_originStyleSheet = styleSheet();
}
QString VVimCmdLineEdit::getCommand() const
{
QString tx = text();
if (tx.size() < 2) {
return "";
} else {
return tx.right(tx.size() - 1);
}
}
QString VVimCmdLineEdit::commandLineTypeLeader(VVim::CommandLineType p_type)
{
QString leader;
switch (p_type) {
case VVim::CommandLineType::Command:
leader = ":";
break;
case VVim::CommandLineType::SearchForward:
leader = "/";
break;
case VVim::CommandLineType::SearchBackward:
leader = "?";
break;
case VVim::CommandLineType::Invalid:
leader.clear();
break;
default:
Q_ASSERT(false);
break;
}
return leader;
}
void VVimCmdLineEdit::reset(VVim::CommandLineType p_type)
{
m_type = p_type;
m_userLastInput.clear();
setCommand("");
show();
setFocus();
setInputMethodEnabled(p_type != VVim::CommandLineType::Command);
}
void VVimCmdLineEdit::setInputMethodEnabled(bool p_enabled)
{
if (m_enableInputMethod != p_enabled) {
m_enableInputMethod = p_enabled;
QInputMethod *im = QGuiApplication::inputMethod();
im->reset();
// Ask input method to query current state, which will call inputMethodQuery().
im->update(Qt::ImEnabled);
}
}
QVariant VVimCmdLineEdit::inputMethodQuery(Qt::InputMethodQuery p_query) const
{
if (p_query == Qt::ImEnabled) {
return m_enableInputMethod;
}
return VLineEdit::inputMethodQuery(p_query);
}
// See if @p_modifiers is Control which is different on macOs and Windows.
static bool isControlModifier(int p_modifiers)
{
#if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
return p_modifiers == Qt::MetaModifier;
#else
return p_modifiers == Qt::ControlModifier;
#endif
}
void VVimCmdLineEdit::keyPressEvent(QKeyEvent *p_event)
{
int key = p_event->key();
int modifiers = p_event->modifiers();
if (m_registerPending) {
// Ctrl and Shift may be sent out first.
if (key == Qt::Key_Control || key == Qt::Key_Shift || key == Qt::Key_Meta) {
goto exit;
}
// Expecting a register name.
emit requestRegister(key, modifiers);
p_event->accept();
setRegisterPending(false);
return;
}
if ((key == Qt::Key_Return && modifiers == Qt::NoModifier)
|| (key == Qt::Key_Enter && modifiers == Qt::KeypadModifier)) {
// Enter, complete the command line input.
p_event->accept();
emit commandFinished(m_type, getCommand());
return;
} else if (key == Qt::Key_Escape
|| (key == Qt::Key_BracketLeft && isControlModifier(modifiers))) {
// Exit command line input.
setText(commandLineTypeLeader(m_type));
p_event->accept();
emit commandCancelled();
return;
}
switch (key) {
case Qt::Key_U:
if (isControlModifier(modifiers)) {
// Ctrl+U, delete all user input.
setText(commandLineTypeLeader(m_type));
p_event->accept();
return;
}
break;
case Qt::Key_N:
if (!isControlModifier(modifiers)) {
break;
}
// Ctrl+N, request next command.
// Fall through.
case Qt::Key_Down:
{
emit requestNextCommand(m_type, getCommand());
p_event->accept();
return;
}
case Qt::Key_P:
if (!isControlModifier(modifiers)) {
break;
}
// Ctrl+P, request previous command.
// Fall through.
case Qt::Key_Up:
{
emit requestPreviousCommand(m_type, getCommand());
p_event->accept();
return;
}
case Qt::Key_R:
{
if (isControlModifier(modifiers)) {
// Ctrl+R, insert the content of a register.
setRegisterPending(true);
p_event->accept();
return;
}
}
default:
break;
}
exit:
VLineEdit::keyPressEvent(p_event);
}
void VVimCmdLineEdit::focusOutEvent(QFocusEvent *p_event)
{
if (p_event->reason() != Qt::ActiveWindowFocusReason) {
emit commandCancelled();
}
}
void VVimCmdLineEdit::setCommand(const QString &p_cmd)
{
setText(commandLineTypeLeader(m_type) + p_cmd);
}
void VVimCmdLineEdit::restoreUserLastInput()
{
setCommand(m_userLastInput);
}
void VVimCmdLineEdit::setRegisterPending(bool p_pending)
{
if (p_pending && !m_registerPending) {
// Going to pending.
setStyleSheet(QString("background: %1;").arg(g_palette->color("vim_indicator_cmd_edit_pending_bg")));
} else if (!p_pending && m_registerPending) {
// Leaving pending.
setStyleSheet(m_originStyleSheet);
}
m_registerPending = p_pending;
}

74
src/vvimcmdlineedit.h Normal file
View File

@ -0,0 +1,74 @@
#ifndef VVIMCMDLINEEDIT_H
#define VVIMCMDLINEEDIT_H
#include "vlineedit.h"
#include "utils/vvim.h"
class QKeyEvent;
class QFocusEvent;
class VVimCmdLineEdit : public VLineEdit
{
Q_OBJECT
public:
explicit VVimCmdLineEdit(QWidget *p_parent = 0);
void reset(VVim::CommandLineType p_type);
// Set the command to @p_cmd with leader unchanged.
void setCommand(const QString &p_cmd);
// Get the command.
QString getCommand() const;
void restoreUserLastInput();
QVariant inputMethodQuery(Qt::InputMethodQuery p_query) const Q_DECL_OVERRIDE;
signals:
// User has finished the input and the command is ready to execute.
void commandFinished(VVim::CommandLineType p_type, const QString &p_cmd);
// User cancelled the input.
void commandCancelled();
// User request the next command in the history.
void requestNextCommand(VVim::CommandLineType p_type, const QString &p_cmd);
// User request the previous command in the history.
void requestPreviousCommand(VVim::CommandLineType p_type, const QString &p_cmd);
// Emit when the input text changed.
void commandChanged(VVim::CommandLineType p_type, const QString &p_cmd);
// Emit when expecting to read a register.
void requestRegister(int p_key, int p_modifiers);
protected:
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
void focusOutEvent(QFocusEvent *p_event) Q_DECL_OVERRIDE;
private:
// Return the leader of @p_type.
QString commandLineTypeLeader(VVim::CommandLineType p_type);
void setRegisterPending(bool p_pending);
void setInputMethodEnabled(bool p_enabled);
VVim::CommandLineType m_type;
// The latest command user input.
QString m_userLastInput;
// Whether we are expecting a register name to read.
bool m_registerPending;
QString m_originStyleSheet;
// Whether enable input method.
bool m_enableInputMethod;
};
#endif // VVIMCMDLINEEDIT_H

View File

@ -12,7 +12,6 @@
#include "vconfigmanager.h" #include "vconfigmanager.h"
#include "vbuttonwithwidget.h" #include "vbuttonwithwidget.h"
#include "vedittab.h"
#include "vpalette.h" #include "vpalette.h"
extern VConfigManager *g_config; extern VConfigManager *g_config;
@ -27,80 +26,6 @@ VVimIndicator::VVimIndicator(QWidget *p_parent)
void VVimIndicator::setupUI() void VVimIndicator::setupUI()
{ {
m_cmdLineEdit = new VVimCmdLineEdit(this);
m_cmdLineEdit->setProperty("VimCommandLine", true);
connect(m_cmdLineEdit, &VVimCmdLineEdit::commandCancelled,
this, [this](){
if (m_editTab) {
m_editTab->focusTab();
}
// NOTICE: m_cmdLineEdit should not hide itself before setting
// focus to edit tab.
m_cmdLineEdit->hide();
if (m_vim) {
m_vim->processCommandLineCancelled();
}
});
connect(m_cmdLineEdit, &VVimCmdLineEdit::commandFinished,
this, [this](VVim::CommandLineType p_type, const QString &p_cmd){
if (m_editTab) {
m_editTab->focusTab();
}
m_cmdLineEdit->hide();
// Hide the cmd line edit before execute the command.
// If we execute the command first, we will get Chinese input
// method enabled after returning to read mode.
if (m_vim) {
m_vim->processCommandLine(p_type, p_cmd);
}
});
connect(m_cmdLineEdit, &VVimCmdLineEdit::commandChanged,
this, [this](VVim::CommandLineType p_type, const QString &p_cmd){
if (m_vim) {
m_vim->processCommandLineChanged(p_type, p_cmd);
}
});
connect(m_cmdLineEdit, &VVimCmdLineEdit::requestNextCommand,
this, [this](VVim::CommandLineType p_type, const QString &p_cmd){
if (m_vim) {
QString cmd = m_vim->getNextCommandHistory(p_type, p_cmd);
if (!cmd.isNull()) {
m_cmdLineEdit->setCommand(cmd);
} else {
m_cmdLineEdit->restoreUserLastInput();
}
}
});
connect(m_cmdLineEdit, &VVimCmdLineEdit::requestPreviousCommand,
this, [this](VVim::CommandLineType p_type, const QString &p_cmd){
if (m_vim) {
QString cmd = m_vim->getPreviousCommandHistory(p_type, p_cmd);
if (!cmd.isNull()) {
m_cmdLineEdit->setCommand(cmd);
}
}
});
connect(m_cmdLineEdit, &VVimCmdLineEdit::requestRegister,
this, [this](int p_key, int p_modifiers){
if (m_vim) {
QString val = m_vim->readRegister(p_key, p_modifiers);
if (!val.isEmpty()) {
m_cmdLineEdit->setText(m_cmdLineEdit->text() + val);
}
}
});
m_cmdLineEdit->hide();
m_modeLabel = new QLabel(this); m_modeLabel = new QLabel(this);
m_modeLabel->setProperty("VimIndicatorModeLabel", true); m_modeLabel->setProperty("VimIndicatorModeLabel", true);
@ -146,8 +71,6 @@ void VVimIndicator::setupUI()
m_keyLabel->setMinimumWidth(metric.width('A') * 5); m_keyLabel->setMinimumWidth(metric.width('A') * 5);
QHBoxLayout *mainLayout = new QHBoxLayout(this); QHBoxLayout *mainLayout = new QHBoxLayout(this);
mainLayout->addStretch();
mainLayout->addWidget(m_cmdLineEdit);
mainLayout->addWidget(m_modeLabel); mainLayout->addWidget(m_modeLabel);
mainLayout->addWidget(m_regBtn); mainLayout->addWidget(m_regBtn);
mainLayout->addWidget(m_markBtn); mainLayout->addWidget(m_markBtn);
@ -242,27 +165,10 @@ static void fillTreeItemsWithRegisters(QTreeWidget *p_tree,
p_tree->resizeColumnToContents(1); p_tree->resizeColumnToContents(1);
} }
void VVimIndicator::update(const VVim *p_vim, const VEditTab *p_editTab) void VVimIndicator::update(const VVim *p_vim)
{ {
m_editTab = const_cast<VEditTab *>(p_editTab);
if (m_vim != p_vim) {
// Disconnect from previous Vim.
if (m_vim) {
disconnect(m_vim.data(), 0, this, 0);
}
m_vim = const_cast<VVim *>(p_vim); m_vim = const_cast<VVim *>(p_vim);
if (m_vim) {
// Connect signal.
connect(m_vim.data(), &VVim::commandLineTriggered,
this, &VVimIndicator::triggerCommandLine);
m_cmdLineEdit->hide();
}
}
if (!m_vim) { if (!m_vim) {
m_cmdLineEdit->hide();
return; return;
} }
@ -334,232 +240,3 @@ void VVimIndicator::updateMarksTree(QWidget *p_widget)
const QMap<QChar, VVim::Mark> &marks = m_vim->getMarks().getMarks(); const QMap<QChar, VVim::Mark> &marks = m_vim->getMarks().getMarks();
fillTreeItemsWithMarks(markTree, marks); fillTreeItemsWithMarks(markTree, marks);
} }
void VVimIndicator::triggerCommandLine(VVim::CommandLineType p_type)
{
m_cmdLineEdit->reset(p_type);
}
VVimCmdLineEdit::VVimCmdLineEdit(QWidget *p_parent)
: VLineEdit(p_parent), m_type(VVim::CommandLineType::Invalid),
m_registerPending(false), m_enableInputMethod(true)
{
// When user delete all the text, cancel command input.
connect(this, &VVimCmdLineEdit::textChanged,
this, [this](const QString &p_text){
if (p_text.isEmpty()) {
emit commandCancelled();
} else {
emit commandChanged(m_type, p_text.right(p_text.size() - 1));
}
});
connect(this, &VVimCmdLineEdit::textEdited,
this, [this](const QString &p_text){
if (p_text.size() < 2) {
m_userLastInput.clear();
} else {
m_userLastInput = p_text.right(p_text.size() - 1);
}
});
m_originStyleSheet = styleSheet();
}
QString VVimCmdLineEdit::getCommand() const
{
QString tx = text();
if (tx.size() < 2) {
return "";
} else {
return tx.right(tx.size() - 1);
}
}
QString VVimCmdLineEdit::commandLineTypeLeader(VVim::CommandLineType p_type)
{
QString leader;
switch (p_type) {
case VVim::CommandLineType::Command:
leader = ":";
break;
case VVim::CommandLineType::SearchForward:
leader = "/";
break;
case VVim::CommandLineType::SearchBackward:
leader = "?";
break;
case VVim::CommandLineType::Invalid:
leader.clear();
break;
default:
Q_ASSERT(false);
break;
}
return leader;
}
void VVimCmdLineEdit::reset(VVim::CommandLineType p_type)
{
m_type = p_type;
m_userLastInput.clear();
setCommand("");
show();
setFocus();
setInputMethodEnabled(p_type != VVim::CommandLineType::Command);
}
void VVimCmdLineEdit::setInputMethodEnabled(bool p_enabled)
{
if (m_enableInputMethod != p_enabled) {
m_enableInputMethod = p_enabled;
QInputMethod *im = QGuiApplication::inputMethod();
im->reset();
// Ask input method to query current state, which will call inputMethodQuery().
im->update(Qt::ImEnabled);
}
}
QVariant VVimCmdLineEdit::inputMethodQuery(Qt::InputMethodQuery p_query) const
{
if (p_query == Qt::ImEnabled) {
return m_enableInputMethod;
}
return VLineEdit::inputMethodQuery(p_query);
}
// See if @p_modifiers is Control which is different on macOs and Windows.
static bool isControlModifier(int p_modifiers)
{
#if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
return p_modifiers == Qt::MetaModifier;
#else
return p_modifiers == Qt::ControlModifier;
#endif
}
void VVimCmdLineEdit::keyPressEvent(QKeyEvent *p_event)
{
int key = p_event->key();
int modifiers = p_event->modifiers();
if (m_registerPending) {
// Ctrl and Shift may be sent out first.
if (key == Qt::Key_Control || key == Qt::Key_Shift || key == Qt::Key_Meta) {
goto exit;
}
// Expecting a register name.
emit requestRegister(key, modifiers);
p_event->accept();
setRegisterPending(false);
return;
}
if ((key == Qt::Key_Return && modifiers == Qt::NoModifier)
|| (key == Qt::Key_Enter && modifiers == Qt::KeypadModifier)) {
// Enter, complete the command line input.
p_event->accept();
emit commandFinished(m_type, getCommand());
return;
} else if (key == Qt::Key_Escape
|| (key == Qt::Key_BracketLeft && isControlModifier(modifiers))) {
// Exit command line input.
setText(commandLineTypeLeader(m_type));
p_event->accept();
emit commandCancelled();
return;
}
switch (key) {
case Qt::Key_U:
if (isControlModifier(modifiers)) {
// Ctrl+U, delete all user input.
setText(commandLineTypeLeader(m_type));
p_event->accept();
return;
}
break;
case Qt::Key_N:
if (!isControlModifier(modifiers)) {
break;
}
// Ctrl+N, request next command.
// Fall through.
case Qt::Key_Down:
{
emit requestNextCommand(m_type, getCommand());
p_event->accept();
return;
}
case Qt::Key_P:
if (!isControlModifier(modifiers)) {
break;
}
// Ctrl+P, request previous command.
// Fall through.
case Qt::Key_Up:
{
emit requestPreviousCommand(m_type, getCommand());
p_event->accept();
return;
}
case Qt::Key_R:
{
if (isControlModifier(modifiers)) {
// Ctrl+R, insert the content of a register.
setRegisterPending(true);
p_event->accept();
return;
}
}
default:
break;
}
exit:
VLineEdit::keyPressEvent(p_event);
}
void VVimCmdLineEdit::focusOutEvent(QFocusEvent *p_event)
{
if (p_event->reason() != Qt::ActiveWindowFocusReason) {
emit commandCancelled();
}
}
void VVimCmdLineEdit::setCommand(const QString &p_cmd)
{
setText(commandLineTypeLeader(m_type) + p_cmd);
}
void VVimCmdLineEdit::restoreUserLastInput()
{
setCommand(m_userLastInput);
}
void VVimCmdLineEdit::setRegisterPending(bool p_pending)
{
if (p_pending && !m_registerPending) {
// Going to pending.
setStyleSheet(QString("background: %1;").arg(g_palette->color("vim_indicator_cmd_edit_pending_bg")));
} else if (!p_pending && m_registerPending) {
// Leaving pending.
setStyleSheet(m_originStyleSheet);
}
m_registerPending = p_pending;
}

View File

@ -4,78 +4,11 @@
#include <QWidget> #include <QWidget>
#include <QPointer> #include <QPointer>
#include "utils/vvim.h" #include "utils/vvim.h"
#include "vlineedit.h"
class QLabel; class QLabel;
class VButtonWithWidget; class VButtonWithWidget;
class QKeyEvent; class QKeyEvent;
class QFocusEvent; class QFocusEvent;
class VEditTab;
class VVimCmdLineEdit : public VLineEdit
{
Q_OBJECT
public:
explicit VVimCmdLineEdit(QWidget *p_parent = 0);
void reset(VVim::CommandLineType p_type);
// Set the command to @p_cmd with leader unchanged.
void setCommand(const QString &p_cmd);
// Get the command.
QString getCommand() const;
void restoreUserLastInput();
QVariant inputMethodQuery(Qt::InputMethodQuery p_query) const Q_DECL_OVERRIDE;
signals:
// User has finished the input and the command is ready to execute.
void commandFinished(VVim::CommandLineType p_type, const QString &p_cmd);
// User cancelled the input.
void commandCancelled();
// User request the next command in the history.
void requestNextCommand(VVim::CommandLineType p_type, const QString &p_cmd);
// User request the previous command in the history.
void requestPreviousCommand(VVim::CommandLineType p_type, const QString &p_cmd);
// Emit when the input text changed.
void commandChanged(VVim::CommandLineType p_type, const QString &p_cmd);
// Emit when expecting to read a register.
void requestRegister(int p_key, int p_modifiers);
protected:
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
void focusOutEvent(QFocusEvent *p_event) Q_DECL_OVERRIDE;
private:
// Return the leader of @p_type.
QString commandLineTypeLeader(VVim::CommandLineType p_type);
void setRegisterPending(bool p_pending);
void setInputMethodEnabled(bool p_enabled);
VVim::CommandLineType m_type;
// The latest command user input.
QString m_userLastInput;
// Whether we are expecting a register name to read.
bool m_registerPending;
QString m_originStyleSheet;
// Whether enable input method.
bool m_enableInputMethod;
};
class VVimIndicator : public QWidget class VVimIndicator : public QWidget
{ {
@ -85,24 +18,18 @@ public:
explicit VVimIndicator(QWidget *p_parent = 0); explicit VVimIndicator(QWidget *p_parent = 0);
// Update indicator according to @p_vim. // Update indicator according to @p_vim.
void update(const VVim *p_vim, const VEditTab *p_editTab); void update(const VVim *p_vim);
private slots: private slots:
void updateRegistersTree(QWidget *p_widget); void updateRegistersTree(QWidget *p_widget);
void updateMarksTree(QWidget *p_widget); void updateMarksTree(QWidget *p_widget);
// Vim request to trigger command line.
void triggerCommandLine(VVim::CommandLineType p_type);
private: private:
void setupUI(); void setupUI();
QString modeToString(VimMode p_mode) const; QString modeToString(VimMode p_mode) const;
// Command line input.
VVimCmdLineEdit *m_cmdLineEdit;
// Indicate the mode. // Indicate the mode.
QLabel *m_modeLabel; QLabel *m_modeLabel;
@ -116,7 +43,6 @@ private:
QLabel *m_keyLabel; QLabel *m_keyLabel;
QPointer<VVim> m_vim; QPointer<VVim> m_vim;
QPointer<VEditTab> m_editTab;
}; };
#endif // VVIMINDICATOR_H #endif // VVIMINDICATOR_H