mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
support more Vim key bindings
1. Esc or Ctrl + [ to clear selection or exit Vim mode; 2. v to enter visual mode; 3. y to copy selected text; "y2j" is not supported yet; Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
parent
58c8506855
commit
76bd2c7d64
@ -13,7 +13,7 @@ VAvatar::VAvatar(QWidget *p_parent)
|
|||||||
resize(m_diameter, m_diameter);
|
resize(m_diameter, m_diameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VAvatar::paintEvent(QPaintEvent *p_event)
|
void VAvatar::paintEvent(QPaintEvent * /*p_event*/)
|
||||||
{
|
{
|
||||||
int diameter = width();
|
int diameter = width();
|
||||||
int x = diameter / 2;
|
int x = diameter / 2;
|
||||||
|
@ -11,7 +11,7 @@ class VEdit;
|
|||||||
class QMimeData;
|
class QMimeData;
|
||||||
class QKeyEvent;
|
class QKeyEvent;
|
||||||
|
|
||||||
enum class KeyState { Normal = 0, Vim };
|
enum class KeyState { Normal = 0, Vim, VimVisual};
|
||||||
|
|
||||||
class VEditOperations: public QObject
|
class VEditOperations: public QObject
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QClipboard>
|
||||||
#include "vmdeditoperations.h"
|
#include "vmdeditoperations.h"
|
||||||
#include "dialog/vinsertimagedialog.h"
|
#include "dialog/vinsertimagedialog.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
@ -173,11 +175,16 @@ bool VMdEditOperations::insertImage()
|
|||||||
// Will modify m_pendingKey.
|
// Will modify m_pendingKey.
|
||||||
bool VMdEditOperations::shouldTriggerVimMode(QKeyEvent *p_event)
|
bool VMdEditOperations::shouldTriggerVimMode(QKeyEvent *p_event)
|
||||||
{
|
{
|
||||||
if (m_keyState == KeyState::Vim) {
|
int modifiers = p_event->modifiers();
|
||||||
|
int key = p_event->key();
|
||||||
|
if (key == Qt::Key_Escape ||
|
||||||
|
(key == Qt::Key_BracketLeft && modifiers == Qt::ControlModifier)) {
|
||||||
|
return false;
|
||||||
|
} else if (m_keyState == KeyState::Vim || m_keyState == KeyState::VimVisual) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (p_event->modifiers() == (Qt::ControlModifier | Qt::AltModifier)) {
|
if (modifiers == (Qt::ControlModifier | Qt::AltModifier)) {
|
||||||
switch (p_event->key()) {
|
switch (key) {
|
||||||
// Should add one item for each supported Ctrl+ALT+<Key> Vim binding.
|
// Should add one item for each supported Ctrl+ALT+<Key> Vim binding.
|
||||||
case Qt::Key_H:
|
case Qt::Key_H:
|
||||||
case Qt::Key_J:
|
case Qt::Key_J:
|
||||||
@ -198,6 +205,8 @@ bool VMdEditOperations::shouldTriggerVimMode(QKeyEvent *p_event)
|
|||||||
case Qt::Key_E:
|
case Qt::Key_E:
|
||||||
case Qt::Key_B:
|
case Qt::Key_B:
|
||||||
case Qt::Key_G:
|
case Qt::Key_G:
|
||||||
|
case Qt::Key_V:
|
||||||
|
case Qt::Key_Y:
|
||||||
case Qt::Key_Dollar:
|
case Qt::Key_Dollar:
|
||||||
case Qt::Key_AsciiCircum:
|
case Qt::Key_AsciiCircum:
|
||||||
{
|
{
|
||||||
@ -207,9 +216,9 @@ bool VMdEditOperations::shouldTriggerVimMode(QKeyEvent *p_event)
|
|||||||
m_pendingKey.clear();
|
m_pendingKey.clear();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (p_event->modifiers() ==
|
} else if (modifiers ==
|
||||||
(Qt::ControlModifier | Qt::ShiftModifier | Qt::AltModifier)) {
|
(Qt::ControlModifier | Qt::ShiftModifier | Qt::AltModifier)) {
|
||||||
switch (p_event->key()) {
|
switch (key) {
|
||||||
case Qt::Key_G:
|
case Qt::Key_G:
|
||||||
case Qt::Key_Dollar:
|
case Qt::Key_Dollar:
|
||||||
case Qt::Key_AsciiCircum:
|
case Qt::Key_AsciiCircum:
|
||||||
@ -299,6 +308,21 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Qt::Key_BracketLeft:
|
||||||
|
{
|
||||||
|
if (p_event->modifiers() != Qt::ControlModifier) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Ctrl+[, Fall through.
|
||||||
|
}
|
||||||
|
case Qt::Key_Escape:
|
||||||
|
{
|
||||||
|
if (handleKeyEsc(p_event)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -544,11 +568,29 @@ bool VMdEditOperations::handleKeyW(QKeyEvent *p_event)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VMdEditOperations::handleKeyEsc(QKeyEvent *p_event)
|
||||||
|
{
|
||||||
|
// Esc, clear any Vim mode, clear selection.
|
||||||
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
|
cursor.clearSelection();
|
||||||
|
m_editor->setTextCursor(cursor);
|
||||||
|
|
||||||
|
m_pendingTimer->stop();
|
||||||
|
m_keyState = KeyState::Normal;
|
||||||
|
m_pendingKey.clear();
|
||||||
|
|
||||||
|
p_event->accept();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool VMdEditOperations::handleKeyPressVim(QKeyEvent *p_event)
|
bool VMdEditOperations::handleKeyPressVim(QKeyEvent *p_event)
|
||||||
{
|
{
|
||||||
int modifiers = p_event->modifiers();
|
int modifiers = p_event->modifiers();
|
||||||
bool ctrlAlt = modifiers == (Qt::ControlModifier | Qt::AltModifier);
|
bool ctrlAlt = modifiers == (Qt::ControlModifier | Qt::AltModifier);
|
||||||
bool ctrlShiftAlt = modifiers == (Qt::ControlModifier | Qt::ShiftModifier | Qt::AltModifier);
|
bool ctrlShiftAlt = modifiers == (Qt::ControlModifier | Qt::ShiftModifier | Qt::AltModifier);
|
||||||
|
bool visualMode = m_keyState == KeyState::VimVisual;
|
||||||
|
QTextCursor::MoveMode mode = visualMode ? QTextCursor::KeepAnchor
|
||||||
|
: QTextCursor::MoveAnchor;
|
||||||
|
|
||||||
switch (p_event->key()) {
|
switch (p_event->key()) {
|
||||||
// Ctrl and Shift may be sent out first.
|
// Ctrl and Shift may be sent out first.
|
||||||
@ -583,9 +625,11 @@ bool VMdEditOperations::handleKeyPressVim(QKeyEvent *p_event)
|
|||||||
// Move cursor <repeat> characters left/Down/Up/Right.
|
// Move cursor <repeat> characters left/Down/Up/Right.
|
||||||
int repeat = keySeqToNumber(m_pendingKey);
|
int repeat = keySeqToNumber(m_pendingKey);
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
cursor.movePosition(op, QTextCursor::MoveAnchor,
|
cursor.movePosition(op, mode, repeat == 0 ? 1 : repeat);
|
||||||
repeat == 0 ? 1 : repeat);
|
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursor(cursor);
|
||||||
|
if (visualMode) {
|
||||||
|
goto pending;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -640,9 +684,11 @@ bool VMdEditOperations::handleKeyPressVim(QKeyEvent *p_event)
|
|||||||
if (repeat == 0) {
|
if (repeat == 0) {
|
||||||
repeat = 1;
|
repeat = 1;
|
||||||
}
|
}
|
||||||
cursor.movePosition(QTextCursor::NextWord, QTextCursor::MoveAnchor,
|
cursor.movePosition(QTextCursor::NextWord, mode, repeat);
|
||||||
repeat);
|
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursor(cursor);
|
||||||
|
if (visualMode) {
|
||||||
|
goto pending;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -660,18 +706,20 @@ bool VMdEditOperations::handleKeyPressVim(QKeyEvent *p_event)
|
|||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
int pos = cursor.position();
|
int pos = cursor.position();
|
||||||
// First move to the end of current word.
|
// First move to the end of current word.
|
||||||
cursor.movePosition(QTextCursor::EndOfWord);
|
cursor.movePosition(QTextCursor::EndOfWord, mode);
|
||||||
if (cursor.position() != pos) {
|
if (cursor.position() != pos) {
|
||||||
// We did move.
|
// We did move.
|
||||||
repeat--;
|
repeat--;
|
||||||
}
|
}
|
||||||
if (repeat) {
|
if (repeat) {
|
||||||
cursor.movePosition(QTextCursor::NextWord, QTextCursor::MoveAnchor,
|
cursor.movePosition(QTextCursor::NextWord, mode, repeat);
|
||||||
repeat);
|
cursor.movePosition(QTextCursor::EndOfWord, mode);
|
||||||
cursor.movePosition(QTextCursor::EndOfWord);
|
|
||||||
}
|
}
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursor(cursor);
|
||||||
|
if (visualMode) {
|
||||||
|
goto pending;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -689,18 +737,19 @@ bool VMdEditOperations::handleKeyPressVim(QKeyEvent *p_event)
|
|||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
int pos = cursor.position();
|
int pos = cursor.position();
|
||||||
// First move to the start of current word.
|
// First move to the start of current word.
|
||||||
cursor.movePosition(QTextCursor::StartOfWord);
|
cursor.movePosition(QTextCursor::StartOfWord, mode);
|
||||||
if (cursor.position() != pos) {
|
if (cursor.position() != pos) {
|
||||||
// We did move.
|
// We did move.
|
||||||
repeat--;
|
repeat--;
|
||||||
}
|
}
|
||||||
if (repeat) {
|
if (repeat) {
|
||||||
cursor.movePosition(QTextCursor::PreviousWord,
|
cursor.movePosition(QTextCursor::PreviousWord, mode, repeat);
|
||||||
QTextCursor::MoveAnchor,
|
|
||||||
repeat);
|
|
||||||
}
|
}
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursor(cursor);
|
||||||
|
if (visualMode) {
|
||||||
|
goto pending;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -710,8 +759,11 @@ bool VMdEditOperations::handleKeyPressVim(QKeyEvent *p_event)
|
|||||||
if (modifiers == Qt::NoModifier || ctrlAlt) {
|
if (modifiers == Qt::NoModifier || ctrlAlt) {
|
||||||
if (keySeqToNumber(m_pendingKey) == 0) {
|
if (keySeqToNumber(m_pendingKey) == 0) {
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
cursor.movePosition(QTextCursor::StartOfLine);
|
cursor.movePosition(QTextCursor::StartOfLine, mode);
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursor(cursor);
|
||||||
|
if (visualMode) {
|
||||||
|
goto pending;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
m_pendingKey.append("0");
|
m_pendingKey.append("0");
|
||||||
goto pending;
|
goto pending;
|
||||||
@ -726,8 +778,11 @@ bool VMdEditOperations::handleKeyPressVim(QKeyEvent *p_event)
|
|||||||
if (m_pendingKey.isEmpty()) {
|
if (m_pendingKey.isEmpty()) {
|
||||||
// Go to end of line.
|
// Go to end of line.
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
cursor.movePosition(QTextCursor::EndOfLine);
|
cursor.movePosition(QTextCursor::EndOfLine, mode);
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursor(cursor);
|
||||||
|
if (visualMode) {
|
||||||
|
goto pending;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -743,17 +798,20 @@ bool VMdEditOperations::handleKeyPressVim(QKeyEvent *p_event)
|
|||||||
QString text = block.text();
|
QString text = block.text();
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
if (text.trimmed().isEmpty()) {
|
if (text.trimmed().isEmpty()) {
|
||||||
cursor.movePosition(QTextCursor::StartOfLine);
|
cursor.movePosition(QTextCursor::StartOfLine, mode);
|
||||||
} else {
|
} else {
|
||||||
cursor.movePosition(QTextCursor::StartOfLine);
|
cursor.movePosition(QTextCursor::StartOfLine, mode);
|
||||||
int pos = cursor.positionInBlock();
|
int pos = cursor.positionInBlock();
|
||||||
while (pos < text.size() && text[pos].isSpace()) {
|
while (pos < text.size() && text[pos].isSpace()) {
|
||||||
cursor.movePosition(QTextCursor::NextWord);
|
cursor.movePosition(QTextCursor::NextWord, mode);
|
||||||
pos = cursor.positionInBlock();
|
pos = cursor.positionInBlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursor(cursor);
|
||||||
|
if (visualMode) {
|
||||||
|
goto pending;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -767,28 +825,62 @@ bool VMdEditOperations::handleKeyPressVim(QKeyEvent *p_event)
|
|||||||
m_pendingKey.append("g");
|
m_pendingKey.append("g");
|
||||||
goto pending;
|
goto pending;
|
||||||
} else if (m_pendingKey.size() == 1 && m_pendingKey.at(0) == "g") {
|
} else if (m_pendingKey.size() == 1 && m_pendingKey.at(0) == "g") {
|
||||||
|
m_pendingKey.clear();
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
cursor.movePosition(QTextCursor::Start);
|
cursor.movePosition(QTextCursor::Start, mode);
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursor(cursor);
|
||||||
|
if (visualMode) {
|
||||||
|
goto pending;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (modifiers == Qt::ShiftModifier || ctrlShiftAlt) {
|
} else if (modifiers == Qt::ShiftModifier || ctrlShiftAlt) {
|
||||||
// G, go to a certain line or the end of document.
|
// G, go to a certain line or the end of document.
|
||||||
int lineNum = keySeqToNumber(m_pendingKey);
|
int lineNum = keySeqToNumber(m_pendingKey);
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
qDebug() << "G:" << lineNum;
|
|
||||||
if (lineNum == 0) {
|
if (lineNum == 0) {
|
||||||
cursor.movePosition(QTextCursor::End);
|
cursor.movePosition(QTextCursor::End, mode);
|
||||||
} else {
|
} else {
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->document();
|
||||||
QTextBlock block = doc->findBlockByNumber(lineNum - 1);
|
QTextBlock block = doc->findBlockByNumber(lineNum - 1);
|
||||||
if (block.isValid()) {
|
if (block.isValid()) {
|
||||||
cursor.setPosition(block.position());
|
cursor.setPosition(block.position(), mode);
|
||||||
} else {
|
} else {
|
||||||
// Go beyond the document.
|
// Go beyond the document.
|
||||||
cursor.movePosition(QTextCursor::End);
|
cursor.movePosition(QTextCursor::End, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursor(cursor);
|
||||||
|
if (visualMode) {
|
||||||
|
goto pending;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Qt::Key_V:
|
||||||
|
{
|
||||||
|
if (modifiers == Qt::NoModifier || ctrlAlt) {
|
||||||
|
// V to enter visual mode.
|
||||||
|
if (m_pendingKey.isEmpty() && m_keyState != KeyState::VimVisual) {
|
||||||
|
m_keyState = KeyState::VimVisual;
|
||||||
|
goto pending;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Qt::Key_Y:
|
||||||
|
{
|
||||||
|
// TODO: support y2j.
|
||||||
|
if (modifiers == Qt::NoModifier || ctrlAlt) {
|
||||||
|
if (m_pendingKey.isEmpty()) {
|
||||||
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
|
if (cursor.hasSelection()) {
|
||||||
|
QString text = cursor.selectedText();
|
||||||
|
QClipboard *clipboard = QApplication::clipboard();
|
||||||
|
clipboard->setText(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -798,9 +890,15 @@ bool VMdEditOperations::handleKeyPressVim(QKeyEvent *p_event)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_pendingTimer->stop();
|
||||||
|
if (m_keyState == KeyState::VimVisual) {
|
||||||
|
// Clear the visual selection.
|
||||||
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
|
cursor.clearSelection();
|
||||||
|
m_editor->setTextCursor(cursor);
|
||||||
|
}
|
||||||
m_keyState = KeyState::Normal;
|
m_keyState = KeyState::Normal;
|
||||||
m_pendingKey.clear();
|
m_pendingKey.clear();
|
||||||
m_pendingTimer->stop();
|
|
||||||
p_event->accept();
|
p_event->accept();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -833,7 +931,8 @@ void VMdEditOperations::pendingTimerTimeout()
|
|||||||
{
|
{
|
||||||
qDebug() << "key pending timer timeout";
|
qDebug() << "key pending timer timeout";
|
||||||
int modifiers = QGuiApplication::keyboardModifiers();
|
int modifiers = QGuiApplication::keyboardModifiers();
|
||||||
if (modifiers == (Qt::ControlModifier | Qt::AltModifier)) {
|
if (modifiers == (Qt::ControlModifier | Qt::AltModifier)
|
||||||
|
|| m_keyState == KeyState::VimVisual) {
|
||||||
m_pendingTimer->start();
|
m_pendingTimer->start();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ private:
|
|||||||
bool handleKeyI(QKeyEvent *p_event);
|
bool handleKeyI(QKeyEvent *p_event);
|
||||||
bool handleKeyU(QKeyEvent *p_event);
|
bool handleKeyU(QKeyEvent *p_event);
|
||||||
bool handleKeyW(QKeyEvent *p_event);
|
bool handleKeyW(QKeyEvent *p_event);
|
||||||
|
bool handleKeyEsc(QKeyEvent *p_event);
|
||||||
bool handleKeyPressVim(QKeyEvent *p_event);
|
bool handleKeyPressVim(QKeyEvent *p_event);
|
||||||
bool shouldTriggerVimMode(QKeyEvent *p_event);
|
bool shouldTriggerVimMode(QKeyEvent *p_event);
|
||||||
int keySeqToNumber(const QList<QString> &p_seq);
|
int keySeqToNumber(const QList<QString> &p_seq);
|
||||||
|
@ -76,7 +76,7 @@ void VOutline::expandTree()
|
|||||||
expandAll();
|
expandAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VOutline::handleCurItemChanged(QTreeWidgetItem *p_curItem, QTreeWidgetItem *p_preItem)
|
void VOutline::handleCurItemChanged(QTreeWidgetItem *p_curItem, QTreeWidgetItem * /*p_preItem*/)
|
||||||
{
|
{
|
||||||
if (!p_curItem) {
|
if (!p_curItem) {
|
||||||
return;
|
return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user