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 <tamlokveer@gmail.com>
This commit is contained in:
Le Tan 2016-12-17 14:22:50 +08:00
parent 87c668bc67
commit d2f61bc605
7 changed files with 323 additions and 29 deletions

View File

@ -1,21 +1,38 @@
#include <QTextCursor> #include <QTextCursor>
#include <QTextDocument> #include <QTextDocument>
#include <QFontMetrics>
#include "vedit.h" #include "vedit.h"
#include "veditoperations.h" #include "veditoperations.h"
#include "vconfigmanager.h"
extern VConfigManager vconfig;
VEditOperations::VEditOperations(VEdit *p_editor, VFile *p_file) 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()); QTextCursor cursor = m_editor->textCursor();
cursor.setPosition(m_editor->textCursor().position()); cursor.insertText(p_text);
cursor.insertText(text); m_editor->setTextCursor(cursor);
} }
VEditOperations::~VEditOperations() 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(), ' ');
}
} }

View File

@ -2,10 +2,14 @@
#define VEDITOPERATIONS_H #define VEDITOPERATIONS_H
#include <QPointer> #include <QPointer>
#include <QString>
#include "vfile.h" #include "vfile.h"
class VEdit; class VEdit;
class QMimeData; class QMimeData;
class QKeyEvent;
enum class KeyState { Normal = 0, Vim };
class VEditOperations class VEditOperations
{ {
@ -15,11 +19,19 @@ public:
virtual bool insertImageFromMimeData(const QMimeData *source) = 0; virtual bool insertImageFromMimeData(const QMimeData *source) = 0;
virtual bool insertURLFromMimeData(const QMimeData *source) = 0; virtual bool insertURLFromMimeData(const QMimeData *source) = 0;
virtual bool insertImage() = 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: protected:
void insertTextAtCurPos(const QString &text); void insertTextAtCurPos(const QString &p_text);
VEdit *m_editor; VEdit *m_editor;
QPointer<VFile> m_file; QPointer<VFile> m_file;
bool m_expandTab;
QString m_tabSpaces;
KeyState m_keyState;
}; };
#endif // VEDITOPERATIONS_H #endif // VEDITOPERATIONS_H

View File

@ -130,6 +130,7 @@ void VMainWindow::initActions()
newNoteAct = new QAction(QIcon(":/resources/icons/create_note_tb.svg"), newNoteAct = new QAction(QIcon(":/resources/icons/create_note_tb.svg"),
tr("New &Note"), this); tr("New &Note"), this);
newNoteAct->setStatusTip(tr("Create a note in current directory")); newNoteAct->setStatusTip(tr("Create a note in current directory"));
newNoteAct->setShortcut(QKeySequence("Ctrl+N"));
connect(newNoteAct, &QAction::triggered, connect(newNoteAct, &QAction::triggered,
fileList, &VFileList::newFile); fileList, &VFileList::newFile);
@ -219,12 +220,14 @@ void VMainWindow::initEditActions()
editNoteAct = new QAction(QIcon(":/resources/icons/edit_note.svg"), editNoteAct = new QAction(QIcon(":/resources/icons/edit_note.svg"),
tr("&Edit"), this); tr("&Edit"), this);
editNoteAct->setStatusTip(tr("Edit current note")); editNoteAct->setStatusTip(tr("Edit current note"));
editNoteAct->setShortcut(QKeySequence("Ctrl+W"));
connect(editNoteAct, &QAction::triggered, connect(editNoteAct, &QAction::triggered,
editArea, &VEditArea::editFile); editArea, &VEditArea::editFile);
discardExitAct = new QAction(QIcon(":/resources/icons/discard_exit.svg"), discardExitAct = new QAction(QIcon(":/resources/icons/discard_exit.svg"),
tr("Discard Changes And Exit"), this); tr("Discard Changes And Exit"), this);
discardExitAct->setStatusTip(tr("Discard changes and exit edit mode")); discardExitAct->setStatusTip(tr("Discard changes and exit edit mode"));
discardExitAct->setShortcut(QKeySequence("Ctrl+Q"));
connect(discardExitAct, &QAction::triggered, connect(discardExitAct, &QAction::triggered,
editArea, &VEditArea::readFile); editArea, &VEditArea::readFile);
@ -235,6 +238,7 @@ void VMainWindow::initEditActions()
tr("Save Changes And Exit"), this); tr("Save Changes And Exit"), this);
saveExitAct->setStatusTip(tr("Save changes and exit edit mode")); saveExitAct->setStatusTip(tr("Save changes and exit edit mode"));
saveExitAct->setMenu(exitEditMenu); saveExitAct->setMenu(exitEditMenu);
saveExitAct->setShortcut(QKeySequence("Ctrl+R"));
connect(saveExitAct, &QAction::triggered, connect(saveExitAct, &QAction::triggered,
editArea, &VEditArea::saveAndReadFile); editArea, &VEditArea::saveAndReadFile);
@ -269,6 +273,7 @@ void VMainWindow::initViewActions()
tr("Expand"), this); tr("Expand"), this);
expandViewAct->setStatusTip(tr("Expand the edit area")); expandViewAct->setStatusTip(tr("Expand the edit area"));
expandViewAct->setCheckable(true); expandViewAct->setCheckable(true);
expandViewAct->setShortcut(QKeySequence("Ctrl+E"));
expandViewAct->setMenu(panelMenu); expandViewAct->setMenu(panelMenu);
connect(expandViewAct, &QAction::triggered, connect(expandViewAct, &QAction::triggered,
this, &VMainWindow::expandPanelView); this, &VMainWindow::expandPanelView);

View File

@ -28,7 +28,7 @@ VMdEdit::VMdEdit(VFile *p_file, QWidget *p_parent)
connect(this, &VMdEdit::cursorPositionChanged, connect(this, &VMdEdit::cursorPositionChanged,
this, &VMdEdit::updateCurHeader); this, &VMdEdit::updateCurHeader);
updateTabSettings(); m_editOps->updateTabSettings();
updateFontAndPalette(); updateFontAndPalette();
} }
@ -38,21 +38,9 @@ void VMdEdit::updateFontAndPalette()
setPalette(vconfig.getMdEditPalette()); 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() void VMdEdit::beginEdit()
{ {
updateTabSettings(); m_editOps->updateTabSettings();
updateFontAndPalette(); updateFontAndPalette();
setFont(vconfig.getMdEditFont()); setFont(vconfig.getMdEditFont());
@ -93,10 +81,7 @@ void VMdEdit::reloadFile()
void VMdEdit::keyPressEvent(QKeyEvent *event) void VMdEdit::keyPressEvent(QKeyEvent *event)
{ {
if ((event->key() == Qt::Key_Tab) && m_expandTab) { if (m_editOps->handleKeyPressEvent(event)) {
QTextCursor cursor(document());
cursor.setPosition(textCursor().position());
cursor.insertText(m_tabSpaces);
return; return;
} }
VEdit::keyPressEvent(event); VEdit::keyPressEvent(event);
@ -381,6 +366,10 @@ void VMdEdit::updateImagePreviewBlock(int p_block, const QString &p_image)
return; return;
} }
QTextCursor cursor(block); 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(); QTextImageFormat format = cursor.charFormat().toImageFormat();
Q_ASSERT(format.isValid()); Q_ASSERT(format.isValid());
QString curPath = format.property(ImagePath).toString(); QString curPath = format.property(ImagePath).toString();
@ -418,7 +407,6 @@ QString VMdEdit::toPlainTextWithoutImg() const
} }
start = removeObjectReplacementLine(text, index); start = removeObjectReplacementLine(text, index);
} while (start < text.size()); } while (start < text.size());
qDebug() << text;
return text; return text;
} }

View File

@ -44,7 +44,6 @@ protected:
private: private:
void updateFontAndPalette(); void updateFontAndPalette();
void updateTabSettings();
void initInitImages(); void initInitImages();
void clearUnusedImages(); void clearUnusedImages();
// p_text[p_index] is QChar::ObjectReplacementCharacter. Remove the line containing it. // p_text[p_index] is QChar::ObjectReplacementCharacter. Remove the line containing it.
@ -68,8 +67,6 @@ private:
HGMarkdownHighlighter *m_mdHighlighter; HGMarkdownHighlighter *m_mdHighlighter;
QVector<QString> m_insertedImages; QVector<QString> m_insertedImages;
QVector<QString> m_initImages; QVector<QString> m_initImages;
bool m_expandTab;
QString m_tabSpaces;
QVector<VHeader> m_headers; QVector<VHeader> m_headers;
}; };

View File

@ -7,6 +7,8 @@
#include <QImageReader> #include <QImageReader>
#include <QDir> #include <QDir>
#include <QMessageBox> #include <QMessageBox>
#include <QKeyEvent>
#include <QTextCursor>
#include "vmdeditoperations.h" #include "vmdeditoperations.h"
#include "dialog/vinsertimagedialog.h" #include "dialog/vinsertimagedialog.h"
#include "utils/vutils.h" #include "utils/vutils.h"
@ -162,3 +164,266 @@ bool VMdEditOperations::insertImage()
} }
return true; 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;
}

View File

@ -15,11 +15,21 @@ public:
bool insertImageFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE; bool insertImageFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
bool insertURLFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE; bool insertURLFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
bool insertImage() Q_DECL_OVERRIDE; bool insertImage() Q_DECL_OVERRIDE;
bool handleKeyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
private: private:
bool insertImageFromURL(const QUrl &imageUrl); bool insertImageFromURL(const QUrl &imageUrl);
void insertImageFromPath(const QString &title, const QString &path, const QString &oriImagePath); void insertImageFromPath(const QString &title, const QString &path, const QString &oriImagePath);
void insertImageFromQImage(const QString &title, const QString &path, const QImage &image); 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 #endif // VMDEDITOPERATIONS_H