From d2f61bc6058928b4de2560be343a7908adb19a1e Mon Sep 17 00:00:00 2001 From: Le Tan Date: Sat, 17 Dec 2016 14:22:50 +0800 Subject: [PATCH] add shortcuts 1. `Ctrl + W`, `Ctrl + R`, `Ctrl + Q`, `Ctrl + S` for edit and view mode; 2. `Ctrl + E` to toggle expand view; 3. `Ctrl + N` to create note in current directory; 4. `Ctrl + H` in edit mode to delete previous char; 5. `Ctrl + W` in edit mode to delete till the start of previous word; 6. `Ctrl + U` in edit mode to delete till the start of current line; 7. `Tab`, `Shift + Tab` to indent or unindent selected lines; 8. `Ctrl + B`, `Ctrl + I` to make selected text bold or italic; Signed-off-by: Le Tan --- src/veditoperations.cpp | 29 ++++- src/veditoperations.h | 14 +- src/vmainwindow.cpp | 5 + src/vmdedit.cpp | 26 +--- src/vmdedit.h | 3 - src/vmdeditoperations.cpp | 265 ++++++++++++++++++++++++++++++++++++++ src/vmdeditoperations.h | 10 ++ 7 files changed, 323 insertions(+), 29 deletions(-) diff --git a/src/veditoperations.cpp b/src/veditoperations.cpp index 226fc1dc..994dc010 100644 --- a/src/veditoperations.cpp +++ b/src/veditoperations.cpp @@ -1,21 +1,38 @@ #include #include +#include #include "vedit.h" #include "veditoperations.h" +#include "vconfigmanager.h" + +extern VConfigManager vconfig; VEditOperations::VEditOperations(VEdit *p_editor, VFile *p_file) - : m_editor(p_editor), m_file(p_file) + : m_editor(p_editor), m_file(p_file), m_expandTab(false), + m_keyState(KeyState::Normal) { + updateTabSettings(); } -void VEditOperations::insertTextAtCurPos(const QString &text) +void VEditOperations::insertTextAtCurPos(const QString &p_text) { - QTextCursor cursor(m_editor->document()); - cursor.setPosition(m_editor->textCursor().position()); - cursor.insertText(text); + QTextCursor cursor = m_editor->textCursor(); + cursor.insertText(p_text); + m_editor->setTextCursor(cursor); } VEditOperations::~VEditOperations() { - +} + +void VEditOperations::updateTabSettings() +{ + if (vconfig.getTabStopWidth() > 0) { + QFontMetrics metrics(vconfig.getMdEditFont()); + m_editor->setTabStopWidth(vconfig.getTabStopWidth() * metrics.width(' ')); + } + m_expandTab = vconfig.getIsExpandTab(); + if (m_expandTab && (vconfig.getTabStopWidth() > 0)) { + m_tabSpaces = QString(vconfig.getTabStopWidth(), ' '); + } } diff --git a/src/veditoperations.h b/src/veditoperations.h index a0b9e431..5899a189 100644 --- a/src/veditoperations.h +++ b/src/veditoperations.h @@ -2,10 +2,14 @@ #define VEDITOPERATIONS_H #include +#include #include "vfile.h" class VEdit; class QMimeData; +class QKeyEvent; + +enum class KeyState { Normal = 0, Vim }; class VEditOperations { @@ -15,11 +19,19 @@ public: virtual bool insertImageFromMimeData(const QMimeData *source) = 0; virtual bool insertURLFromMimeData(const QMimeData *source) = 0; virtual bool insertImage() = 0; + // Return true if @p_event has been handled and no need to be further + // processed. + virtual bool handleKeyPressEvent(QKeyEvent *p_event) = 0; + void updateTabSettings(); protected: - void insertTextAtCurPos(const QString &text); + void insertTextAtCurPos(const QString &p_text); + VEdit *m_editor; QPointer m_file; + bool m_expandTab; + QString m_tabSpaces; + KeyState m_keyState; }; #endif // VEDITOPERATIONS_H diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 6f05d130..5d42b3e6 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -130,6 +130,7 @@ void VMainWindow::initActions() newNoteAct = new QAction(QIcon(":/resources/icons/create_note_tb.svg"), tr("New &Note"), this); newNoteAct->setStatusTip(tr("Create a note in current directory")); + newNoteAct->setShortcut(QKeySequence("Ctrl+N")); connect(newNoteAct, &QAction::triggered, fileList, &VFileList::newFile); @@ -219,12 +220,14 @@ void VMainWindow::initEditActions() editNoteAct = new QAction(QIcon(":/resources/icons/edit_note.svg"), tr("&Edit"), this); editNoteAct->setStatusTip(tr("Edit current note")); + editNoteAct->setShortcut(QKeySequence("Ctrl+W")); connect(editNoteAct, &QAction::triggered, editArea, &VEditArea::editFile); discardExitAct = new QAction(QIcon(":/resources/icons/discard_exit.svg"), tr("Discard Changes And Exit"), this); discardExitAct->setStatusTip(tr("Discard changes and exit edit mode")); + discardExitAct->setShortcut(QKeySequence("Ctrl+Q")); connect(discardExitAct, &QAction::triggered, editArea, &VEditArea::readFile); @@ -235,6 +238,7 @@ void VMainWindow::initEditActions() tr("Save Changes And Exit"), this); saveExitAct->setStatusTip(tr("Save changes and exit edit mode")); saveExitAct->setMenu(exitEditMenu); + saveExitAct->setShortcut(QKeySequence("Ctrl+R")); connect(saveExitAct, &QAction::triggered, editArea, &VEditArea::saveAndReadFile); @@ -269,6 +273,7 @@ void VMainWindow::initViewActions() tr("Expand"), this); expandViewAct->setStatusTip(tr("Expand the edit area")); expandViewAct->setCheckable(true); + expandViewAct->setShortcut(QKeySequence("Ctrl+E")); expandViewAct->setMenu(panelMenu); connect(expandViewAct, &QAction::triggered, this, &VMainWindow::expandPanelView); diff --git a/src/vmdedit.cpp b/src/vmdedit.cpp index bdebf506..bc042608 100644 --- a/src/vmdedit.cpp +++ b/src/vmdedit.cpp @@ -28,7 +28,7 @@ VMdEdit::VMdEdit(VFile *p_file, QWidget *p_parent) connect(this, &VMdEdit::cursorPositionChanged, this, &VMdEdit::updateCurHeader); - updateTabSettings(); + m_editOps->updateTabSettings(); updateFontAndPalette(); } @@ -38,21 +38,9 @@ void VMdEdit::updateFontAndPalette() setPalette(vconfig.getMdEditPalette()); } -void VMdEdit::updateTabSettings() -{ - if (vconfig.getTabStopWidth() > 0) { - QFontMetrics metrics(vconfig.getMdEditFont()); - setTabStopWidth(vconfig.getTabStopWidth() * metrics.width(' ')); - } - m_expandTab = vconfig.getIsExpandTab(); - if (m_expandTab && (vconfig.getTabStopWidth() > 0)) { - m_tabSpaces = QString(vconfig.getTabStopWidth(), ' '); - } -} - void VMdEdit::beginEdit() { - updateTabSettings(); + m_editOps->updateTabSettings(); updateFontAndPalette(); setFont(vconfig.getMdEditFont()); @@ -93,10 +81,7 @@ void VMdEdit::reloadFile() void VMdEdit::keyPressEvent(QKeyEvent *event) { - if ((event->key() == Qt::Key_Tab) && m_expandTab) { - QTextCursor cursor(document()); - cursor.setPosition(textCursor().position()); - cursor.insertText(m_tabSpaces); + if (m_editOps->handleKeyPressEvent(event)) { return; } VEdit::keyPressEvent(event); @@ -381,6 +366,10 @@ void VMdEdit::updateImagePreviewBlock(int p_block, const QString &p_image) return; } QTextCursor cursor(block); + int shift = block.text().indexOf(QChar::ObjectReplacementCharacter); + if (shift > 0) { + cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, shift + 1); + } QTextImageFormat format = cursor.charFormat().toImageFormat(); Q_ASSERT(format.isValid()); QString curPath = format.property(ImagePath).toString(); @@ -418,7 +407,6 @@ QString VMdEdit::toPlainTextWithoutImg() const } start = removeObjectReplacementLine(text, index); } while (start < text.size()); - qDebug() << text; return text; } diff --git a/src/vmdedit.h b/src/vmdedit.h index 78968af6..7eb40c31 100644 --- a/src/vmdedit.h +++ b/src/vmdedit.h @@ -44,7 +44,6 @@ protected: private: void updateFontAndPalette(); - void updateTabSettings(); void initInitImages(); void clearUnusedImages(); // p_text[p_index] is QChar::ObjectReplacementCharacter. Remove the line containing it. @@ -68,8 +67,6 @@ private: HGMarkdownHighlighter *m_mdHighlighter; QVector m_insertedImages; QVector m_initImages; - bool m_expandTab; - QString m_tabSpaces; QVector m_headers; }; diff --git a/src/vmdeditoperations.cpp b/src/vmdeditoperations.cpp index 9b27fbe4..c83135a1 100644 --- a/src/vmdeditoperations.cpp +++ b/src/vmdeditoperations.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "vmdeditoperations.h" #include "dialog/vinsertimagedialog.h" #include "utils/vutils.h" @@ -162,3 +164,266 @@ bool VMdEditOperations::insertImage() } return true; } + +bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event) +{ + switch (p_event->key()) { + case Qt::Key_Tab: + { + if (handleKeyTab(p_event)) { + return true; + } + break; + } + + case Qt::Key_Backtab: + { + if (handleKeyBackTab(p_event)) { + return true; + } + break; + } + + case Qt::Key_H: + { + if (handleKeyH(p_event)) { + return true; + } + break; + } + + case Qt::Key_W: + { + if (handleKeyW(p_event)) { + return true; + } + break; + } + + case Qt::Key_U: + { + if (handleKeyU(p_event)) { + return true; + } + break; + } + + case Qt::Key_B: + { + if (handleKeyB(p_event)) { + return true; + } + break; + } + + case Qt::Key_I: + { + if (handleKeyI(p_event)) { + return true; + } + break; + } + + default: + // Fall through. + break; + } + + m_keyState = KeyState::Normal; + return false; +} + +bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event) +{ + QTextDocument *doc = m_editor->document(); + QString text("\t"); + if (m_expandTab) { + text = m_tabSpaces; + } + + if (p_event->modifiers() == Qt::NoModifier) { + QTextCursor cursor = m_editor->textCursor(); + cursor.beginEditBlock(); + if (cursor.hasSelection()) { + // Indent each selected line. + QTextBlock block = doc->findBlock(cursor.selectionStart()); + QTextBlock endBlock = doc->findBlock(cursor.selectionEnd()); + int endBlockNum = endBlock.blockNumber(); + while (true) { + Q_ASSERT(block.isValid()); + QTextCursor blockCursor(block); + blockCursor.insertText(text); + + if (block.blockNumber() == endBlockNum) { + break; + } + block = block.next(); + } + } else { + // Just insert "tab". + insertTextAtCurPos(text); + } + cursor.endEditBlock(); + } else { + return false; + } + p_event->accept(); + return true; +} + +bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event) +{ + QTextDocument *doc = m_editor->document(); + QTextCursor cursor = m_editor->textCursor(); + QTextBlock block = doc->findBlock(cursor.selectionStart()); + QTextBlock endBlock = doc->findBlock(cursor.selectionEnd()); + int endBlockNum = endBlock.blockNumber(); + cursor.beginEditBlock(); + for (; block.isValid() && block.blockNumber() <= endBlockNum; + block = block.next()) { + QTextCursor blockCursor(block); + QString text = block.text(); + if (text.isEmpty()) { + continue; + } else if (text[0] == '\t') { + blockCursor.deleteChar(); + continue; + } else if (text[0] != ' ') { + continue; + } else { + // Spaces. + if (m_expandTab) { + int width = m_tabSpaces.size(); + for (int i = 0; i < width; ++i) { + if (text[i] == ' ') { + blockCursor.deleteChar(); + } else { + break; + } + } + continue; + } else { + blockCursor.deleteChar(); + continue; + } + } + } + cursor.endEditBlock(); + p_event->accept(); + return true; +} + +bool VMdEditOperations::handleKeyB(QKeyEvent *p_event) +{ + if (p_event->modifiers() == Qt::ControlModifier) { + // Ctrl+B, Bold. + QTextCursor cursor = m_editor->textCursor(); + if (cursor.hasSelection()) { + // Insert ** around the selected text. + int start = cursor.selectionStart(); + int end = cursor.selectionEnd(); + cursor.beginEditBlock(); + cursor.clearSelection(); + cursor.setPosition(start, QTextCursor::MoveAnchor); + cursor.insertText("**"); + cursor.setPosition(end + 2, QTextCursor::MoveAnchor); + cursor.insertText("**"); + cursor.endEditBlock(); + m_editor->setTextCursor(cursor); + } else { + // Insert **** and place cursor in the middle. + cursor.beginEditBlock(); + cursor.insertText("****"); + cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2); + cursor.endEditBlock(); + m_editor->setTextCursor(cursor); + } + + p_event->accept(); + return true; + } + return false; +} + +bool VMdEditOperations::handleKeyH(QKeyEvent *p_event) +{ + if (p_event->modifiers() == Qt::ControlModifier) { + // Ctrl+H, equal to backspace. + QTextCursor cursor = m_editor->textCursor(); + cursor.deletePreviousChar(); + + p_event->accept(); + return true; + } + return false; +} + +bool VMdEditOperations::handleKeyI(QKeyEvent *p_event) +{ + if (p_event->modifiers() == Qt::ControlModifier) { + // Ctrl+I, Italic. + QTextCursor cursor = m_editor->textCursor(); + if (cursor.hasSelection()) { + // Insert * around the selected text. + int start = cursor.selectionStart(); + int end = cursor.selectionEnd(); + cursor.beginEditBlock(); + cursor.clearSelection(); + cursor.setPosition(start, QTextCursor::MoveAnchor); + cursor.insertText("*"); + cursor.setPosition(end + 1, QTextCursor::MoveAnchor); + cursor.insertText("*"); + cursor.endEditBlock(); + m_editor->setTextCursor(cursor); + } else { + // Insert ** and place cursor in the middle. + cursor.beginEditBlock(); + cursor.insertText("**"); + cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1); + cursor.endEditBlock(); + m_editor->setTextCursor(cursor); + } + + p_event->accept(); + return true; + } + return false; +} + +bool VMdEditOperations::handleKeyU(QKeyEvent *p_event) +{ + if (p_event->modifiers() == Qt::ControlModifier) { + // Ctrl+U, delete till the start of line. + QTextCursor cursor = m_editor->textCursor(); + bool ret; + if (cursor.atBlockStart()) { + ret = cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor); + } else { + ret = cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); + } + if (ret) { + cursor.removeSelectedText(); + } + + p_event->accept(); + return true; + } + return false; +} + +bool VMdEditOperations::handleKeyW(QKeyEvent *p_event) +{ + if (p_event->modifiers() == Qt::ControlModifier) { + // Ctrl+W, delete till the start of previous word. + QTextCursor cursor = m_editor->textCursor(); + bool ret = cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor); + if (ret) { + cursor.removeSelectedText(); + } + + p_event->accept(); + return true; + } + return false; +} + diff --git a/src/vmdeditoperations.h b/src/vmdeditoperations.h index 222d1dbd..e3ec0dcb 100644 --- a/src/vmdeditoperations.h +++ b/src/vmdeditoperations.h @@ -15,11 +15,21 @@ public: bool insertImageFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE; bool insertURLFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE; bool insertImage() Q_DECL_OVERRIDE; + bool handleKeyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE; private: bool insertImageFromURL(const QUrl &imageUrl); void insertImageFromPath(const QString &title, const QString &path, const QString &oriImagePath); void insertImageFromQImage(const QString &title, const QString &path, const QImage &image); + + // Key press handlers. + bool handleKeyTab(QKeyEvent *p_event); + bool handleKeyBackTab(QKeyEvent *p_event); + bool handleKeyB(QKeyEvent *p_event); + bool handleKeyH(QKeyEvent *p_event); + bool handleKeyI(QKeyEvent *p_event); + bool handleKeyU(QKeyEvent *p_event); + bool handleKeyW(QKeyEvent *p_event); }; #endif // VMDEDITOPERATIONS_H