diff --git a/src/src.pro b/src/src.pro index 859c5d59..330b627b 100644 --- a/src/src.pro +++ b/src/src.pro @@ -107,7 +107,8 @@ SOURCES += main.cpp\ vwaitingwidget.cpp \ utils/vwebutils.cpp \ vlineedit.cpp \ - vcart.cpp + vcart.cpp \ + vvimcmdlineedit.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -201,7 +202,8 @@ HEADERS += vmainwindow.h \ vwaitingwidget.h \ utils/vwebutils.h \ vlineedit.h \ - vcart.h + vcart.h \ + vvimcmdlineedit.h RESOURCES += \ vnote.qrc \ diff --git a/src/veditoperations.cpp b/src/veditoperations.cpp index e36a236a..2474a0b6 100644 --- a/src/veditoperations.cpp +++ b/src/veditoperations.cpp @@ -100,3 +100,8 @@ void VEditOperations::setVimMode(VimMode p_mode) m_vim->setMode(p_mode); } } + +VVim *VEditOperations::getVim() const +{ + return m_vim; +} diff --git a/src/veditoperations.h b/src/veditoperations.h index 32f92e9e..559d5b53 100644 --- a/src/veditoperations.h +++ b/src/veditoperations.h @@ -47,6 +47,8 @@ public: // Set Vim mode if not NULL. void setVimMode(VimMode p_mode); + VVim *getVim() const; + signals: // Want to display a template message in status bar. void statusMessage(const QString &p_msg); diff --git a/src/veditor.cpp b/src/veditor.cpp index dfceaf90..28b298b3 100644 --- a/src/veditor.cpp +++ b/src/veditor.cpp @@ -970,3 +970,12 @@ void VEditor::setVimMode(VimMode p_mode) m_editOps->setVimMode(p_mode); } } + +VVim *VEditor::getVim() const +{ + if (m_editOps) { + return m_editOps->getVim(); + } + + return NULL; +} diff --git a/src/veditor.h b/src/veditor.h index 740dd734..a8591888 100644 --- a/src/veditor.h +++ b/src/veditor.h @@ -142,6 +142,8 @@ public: void setVimMode(VimMode p_mode); + VVim *getVim() const; + virtual QString getContent() const = 0; // @p_modified: if true, delete the whole content and insert the new content. diff --git a/src/vedittab.cpp b/src/vedittab.cpp index b0b6af37..79c43ff8 100644 --- a/src/vedittab.cpp +++ b/src/vedittab.cpp @@ -195,3 +195,44 @@ void VEditTab::handleFileOrDirectoryChange(bool p_isFile, UpdateAction p_act) Q_UNUSED(p_isFile); 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(); +} diff --git a/src/vedittab.h b/src/vedittab.h index a261d39c..cf70ea99 100644 --- a/src/vedittab.h +++ b/src/vedittab.h @@ -118,6 +118,18 @@ public slots: // Enter edit mode 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: void wheelEvent(QWheelEvent *p_event) Q_DECL_OVERRIDE; @@ -180,6 +192,9 @@ signals: // Request to close itself. void closeRequested(VEditTab *p_tab); + // Request main window to show Vim cmd line. + void triggerVimCmd(VVim::CommandLineType p_type); + private slots: // Called when app focus changed. void handleFocusChanged(QWidget *p_old, QWidget *p_now); diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 8ea10132..6bd474a5 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -20,6 +20,7 @@ #include "vexporter.h" #include "vmdtab.h" #include "vvimindicator.h" +#include "vvimcmdlineedit.h" #include "vtabindicator.h" #include "dialog/vupdater.h" #include "vorphanfile.h" @@ -240,6 +241,8 @@ void VMainWindow::setupUI() setCentralWidget(m_mainSplitter); + initVimCmd(); + m_vimIndicator = new VVimIndicator(this); m_vimIndicator->hide(); @@ -247,6 +250,7 @@ void VMainWindow::setupUI() m_tabIndicator->hide(); // Create and show the status bar + statusBar()->addPermanentWidget(m_vimCmd); statusBar()->addPermanentWidget(m_vimIndicator); statusBar()->addPermanentWidget(m_tabIndicator); @@ -1835,7 +1839,25 @@ void VMainWindow::updateActionsStateFromTab(const VEditTab *p_tab) void VMainWindow::handleAreaTabStatusUpdated(const VEditTabInfo &p_info) { - m_curTab = p_info.m_editTab; + 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; + if (m_curTab) { + connect(m_curTab, &VEditTab::triggerVimCmd, + m_vimCmd, &VVimCmdLineEdit::reset); + } + + m_vimCmd->hide(); + } + if (m_curTab) { m_curFile = m_curTab->getFile(); } else { @@ -2376,7 +2398,7 @@ void VMainWindow::updateStatusInfo(const VEditTabInfo &p_info) 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()) { m_vimIndicator->hide(); } else { @@ -2826,3 +2848,80 @@ void VMainWindow::customShortcut() this); 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(); +} diff --git a/src/vmainwindow.h b/src/vmainwindow.h index 3defb144..56f19118 100644 --- a/src/vmainwindow.h +++ b/src/vmainwindow.h @@ -30,6 +30,7 @@ class VNotebookSelector; class VFindReplaceDialog; class VCaptain; class VVimIndicator; +class VVimCmdLineEdit; class VTabIndicator; class VSingleInstanceGuard; class QTimer; @@ -213,6 +214,8 @@ private: void initEditorStyleMenu(QMenu *p_emnu); void updateWindowTitle(const QString &str); + void initVimCmd(); + // Update state of actions according to @p_tab. void updateActionsStateFromTab(const VEditTab *p_tab); @@ -311,7 +314,11 @@ private: VCart *m_cart; VFindReplaceDialog *m_findReplaceDialog; + + VVimCmdLineEdit *m_vimCmd; + VVimIndicator *m_vimIndicator; + VTabIndicator *m_tabIndicator; // SinglePanel, TwoPanels, CompactMode. diff --git a/src/vmdtab.cpp b/src/vmdtab.cpp index 679246f5..558afceb 100644 --- a/src/vmdtab.cpp +++ b/src/vmdtab.cpp @@ -472,6 +472,15 @@ void VMdTab::setupMarkdownEditor() connect(m_editor, &VMdEditor::requestTextToHtml, 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); m_editor->reloadFile(); m_stacks->addWidget(m_editor); @@ -1030,3 +1039,77 @@ void VMdTab::textToHtmlViaWebView(const QString &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(); +} diff --git a/src/vmdtab.h b/src/vmdtab.h index f2ea34c5..5d378b38 100644 --- a/src/vmdtab.h +++ b/src/vmdtab.h @@ -91,6 +91,18 @@ public slots: // Enter edit mode. 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: void writeBackupFile() Q_DECL_OVERRIDE; diff --git a/src/vvimcmdlineedit.cpp b/src/vvimcmdlineedit.cpp new file mode 100644 index 00000000..4cacd867 --- /dev/null +++ b/src/vvimcmdlineedit.cpp @@ -0,0 +1,232 @@ +#include "vvimcmdlineedit.h" + +#include +#include + +#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; +} diff --git a/src/vvimcmdlineedit.h b/src/vvimcmdlineedit.h new file mode 100644 index 00000000..5181f02d --- /dev/null +++ b/src/vvimcmdlineedit.h @@ -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 diff --git a/src/vvimindicator.cpp b/src/vvimindicator.cpp index 7cd41a9f..ea5ec127 100644 --- a/src/vvimindicator.cpp +++ b/src/vvimindicator.cpp @@ -12,7 +12,6 @@ #include "vconfigmanager.h" #include "vbuttonwithwidget.h" -#include "vedittab.h" #include "vpalette.h" extern VConfigManager *g_config; @@ -27,80 +26,6 @@ VVimIndicator::VVimIndicator(QWidget *p_parent) 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->setProperty("VimIndicatorModeLabel", true); @@ -146,8 +71,6 @@ void VVimIndicator::setupUI() m_keyLabel->setMinimumWidth(metric.width('A') * 5); QHBoxLayout *mainLayout = new QHBoxLayout(this); - mainLayout->addStretch(); - mainLayout->addWidget(m_cmdLineEdit); mainLayout->addWidget(m_modeLabel); mainLayout->addWidget(m_regBtn); mainLayout->addWidget(m_markBtn); @@ -242,27 +165,10 @@ static void fillTreeItemsWithRegisters(QTreeWidget *p_tree, 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(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(p_vim); - if (m_vim) { - // Connect signal. - connect(m_vim.data(), &VVim::commandLineTriggered, - this, &VVimIndicator::triggerCommandLine); - - m_cmdLineEdit->hide(); - } - } - + m_vim = const_cast(p_vim); if (!m_vim) { - m_cmdLineEdit->hide(); return; } @@ -334,232 +240,3 @@ void VVimIndicator::updateMarksTree(QWidget *p_widget) const QMap &marks = m_vim->getMarks().getMarks(); 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; -} diff --git a/src/vvimindicator.h b/src/vvimindicator.h index 4c83ecbb..169db519 100644 --- a/src/vvimindicator.h +++ b/src/vvimindicator.h @@ -4,78 +4,11 @@ #include #include #include "utils/vvim.h" -#include "vlineedit.h" class QLabel; class VButtonWithWidget; class QKeyEvent; 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 { @@ -85,24 +18,18 @@ public: explicit VVimIndicator(QWidget *p_parent = 0); // Update indicator according to @p_vim. - void update(const VVim *p_vim, const VEditTab *p_editTab); + void update(const VVim *p_vim); private slots: void updateRegistersTree(QWidget *p_widget); void updateMarksTree(QWidget *p_widget); - // Vim request to trigger command line. - void triggerCommandLine(VVim::CommandLineType p_type); - private: void setupUI(); QString modeToString(VimMode p_mode) const; - // Command line input. - VVimCmdLineEdit *m_cmdLineEdit; - // Indicate the mode. QLabel *m_modeLabel; @@ -116,7 +43,6 @@ private: QLabel *m_keyLabel; QPointer m_vim; - QPointer m_editTab; }; #endif // VVIMINDICATOR_H