diff --git a/src/resources/icons/bold.svg b/src/resources/icons/bold.svg
new file mode 100644
index 00000000..9dd41cae
--- /dev/null
+++ b/src/resources/icons/bold.svg
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/src/resources/icons/inline_code.svg b/src/resources/icons/inline_code.svg
new file mode 100644
index 00000000..b98f898e
--- /dev/null
+++ b/src/resources/icons/inline_code.svg
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/src/resources/icons/italic.svg b/src/resources/icons/italic.svg
new file mode 100644
index 00000000..da96923d
--- /dev/null
+++ b/src/resources/icons/italic.svg
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/src/resources/icons/strikethrough.svg b/src/resources/icons/strikethrough.svg
new file mode 100644
index 00000000..2ae29a98
--- /dev/null
+++ b/src/resources/icons/strikethrough.svg
@@ -0,0 +1,8 @@
+
+
\ No newline at end of file
diff --git a/src/resources/icons/underline.svg b/src/resources/icons/underline.svg
new file mode 100644
index 00000000..2ef3453f
--- /dev/null
+++ b/src/resources/icons/underline.svg
@@ -0,0 +1,8 @@
+
+
\ No newline at end of file
diff --git a/src/utils/vvim.cpp b/src/utils/vvim.cpp
index 4aaa0d9b..80d090c8 100644
--- a/src/utils/vvim.cpp
+++ b/src/utils/vvim.cpp
@@ -4241,8 +4241,8 @@ int VVim::blockCountOfPageStep() const
void VVim::selectionToVisualMode(bool p_hasText)
{
if (p_hasText && m_mode == VimMode::Normal) {
- // Enter visual mode.
- setMode(VimMode::Visual);
+ // Enter visual mode without clearing the selection.
+ setMode(VimMode::Visual, false);
}
}
diff --git a/src/vconstants.h b/src/vconstants.h
index 6317dceb..f3f58624 100644
--- a/src/vconstants.h
+++ b/src/vconstants.h
@@ -34,4 +34,11 @@ namespace DirConfig
}
static const QString c_emptyHeaderName = "[EMPTY]";
+
+enum class TextDecoration { None,
+ Bold,
+ Italic,
+ Underline,
+ Strikethrough,
+ InlineCode };
#endif
diff --git a/src/vedit.cpp b/src/vedit.cpp
index f5e90227..460f9866 100644
--- a/src/vedit.cpp
+++ b/src/vedit.cpp
@@ -826,3 +826,10 @@ void VEdit::setInputMethodEnabled(bool p_enabled)
im->update(Qt::ImEnabled);
}
}
+
+void VEdit::decorateText(TextDecoration p_decoration)
+{
+ if (m_editOps) {
+ m_editOps->decorateText(p_decoration);
+ }
+}
diff --git a/src/vedit.h b/src/vedit.h
index d21f2cee..ffa85007 100644
--- a/src/vedit.h
+++ b/src/vedit.h
@@ -92,6 +92,9 @@ public:
void setInputMethodEnabled(bool p_enabled);
+ // Insert decoration markers or decorate selected text.
+ void decorateText(TextDecoration p_decoration);
+
signals:
// Request VEditTab to save and exit edit mode.
void saveAndRead();
diff --git a/src/veditoperations.h b/src/veditoperations.h
index f349e5f0..cc6b2371 100644
--- a/src/veditoperations.h
+++ b/src/veditoperations.h
@@ -30,6 +30,9 @@ public:
// Request to propogate Vim status.
void requestUpdateVimStatus();
+ // Insert decoration markers or decorate selected text.
+ virtual void decorateText(TextDecoration p_decoration) {Q_UNUSED(p_decoration);};
+
signals:
// Want to display a template message in status bar.
void statusMessage(const QString &p_msg);
diff --git a/src/vedittab.h b/src/vedittab.h
index 73d66021..116c814b 100644
--- a/src/vedittab.h
+++ b/src/vedittab.h
@@ -68,6 +68,9 @@ public:
// Request current tab to propogate its status about Vim.
virtual void requestUpdateVimStatus() = 0;
+ // Insert decoration markers or decorate selected text.
+ virtual void decorateText(TextDecoration p_decoration) {Q_UNUSED(p_decoration);};
+
public slots:
// Enter edit mode
virtual void editFile() = 0;
diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp
index 7f5863e2..6e31648a 100644
--- a/src/vmainwindow.cpp
+++ b/src/vmainwindow.cpp
@@ -175,6 +175,7 @@ void VMainWindow::initToolBar()
{
initFileToolBar();
initViewToolBar();
+ initEditToolBar();
}
void VMainWindow::initViewToolBar()
@@ -210,6 +211,90 @@ void VMainWindow::initViewToolBar()
viewToolBar->addAction(expandViewAct);
}
+static void setActionsVisible(QWidget *p_widget, bool p_visible)
+{
+ Q_ASSERT(p_widget);
+ QList actions = p_widget->actions();
+ for (auto const & act : actions) {
+ act->setVisible(p_visible);
+ }
+}
+
+void VMainWindow::initEditToolBar()
+{
+ m_editToolBar = addToolBar(tr("Edit Toolbar"));
+ m_editToolBar->setObjectName("EditToolBar");
+ m_editToolBar->setMovable(false);
+
+ m_editToolBar->addSeparator();
+
+ QAction *boldAct = new QAction(QIcon(":/resources/icons/bold.svg"),
+ tr("Bold"), this);
+ boldAct->setStatusTip(tr("Insert bold text or change selected text to bold"));
+ connect(boldAct, &QAction::triggered,
+ this, [this](){
+ if (m_curTab) {
+ m_curTab->decorateText(TextDecoration::Bold);
+ }
+ });
+
+ m_editToolBar->addAction(boldAct);
+
+ QAction *italicAct = new QAction(QIcon(":/resources/icons/italic.svg"),
+ tr("Italic"), this);
+ italicAct->setStatusTip(tr("Insert italic text or change selected text to italic"));
+ connect(italicAct, &QAction::triggered,
+ this, [this](){
+ if (m_curTab) {
+ m_curTab->decorateText(TextDecoration::Italic);
+ }
+ });
+
+ m_editToolBar->addAction(italicAct);
+
+ QAction *underlineAct = new QAction(QIcon(":/resources/icons/underline.svg"),
+ tr("Underline"), this);
+ underlineAct->setStatusTip(tr("Insert underlined text or change selected text to underlined"));
+ connect(underlineAct, &QAction::triggered,
+ this, [this](){
+ if (m_curTab) {
+ m_curTab->decorateText(TextDecoration::Underline);
+ }
+ });
+
+ m_editToolBar->addAction(underlineAct);
+
+ QAction *strikethroughAct = new QAction(QIcon(":/resources/icons/strikethrough.svg"),
+ tr("Strikethrough"), this);
+ strikethroughAct->setStatusTip(tr("Insert strikethrough text or change selected text to strikethroughed"));
+ connect(strikethroughAct, &QAction::triggered,
+ this, [this](){
+ if (m_curTab) {
+ m_curTab->decorateText(TextDecoration::Strikethrough);
+ }
+ });
+
+ m_editToolBar->addAction(strikethroughAct);
+
+ QAction *inlineCodeAct = new QAction(QIcon(":/resources/icons/inline_code.svg"),
+ tr("Inline Code"), this);
+ inlineCodeAct->setStatusTip(tr("Insert inline-code text or change selected text to inline-coded"));
+ connect(inlineCodeAct, &QAction::triggered,
+ this, [this](){
+ if (m_curTab) {
+ m_curTab->decorateText(TextDecoration::InlineCode);
+ }
+ });
+
+ m_editToolBar->addAction(inlineCodeAct);
+
+ QAction *toggleAct = m_editToolBar->toggleViewAction();
+ toggleAct->setToolTip(tr("Toggle the edit toolbar"));
+ viewMenu->addAction(toggleAct);
+
+ setActionsVisible(m_editToolBar, false);
+}
+
void VMainWindow::initFileToolBar()
{
QToolBar *fileToolBar = addToolBar(tr("Note"));
@@ -507,6 +592,7 @@ void VMainWindow::initMarkdownMenu()
void VMainWindow::initViewMenu()
{
viewMenu = menuBar()->addMenu(tr("&View"));
+ viewMenu->setToolTipsVisible(true);
}
void VMainWindow::initFileMenu()
@@ -795,7 +881,10 @@ void VMainWindow::initDockWindows()
toolBox->addItem(outline, QIcon(":/resources/icons/outline.svg"), tr("Outline"));
toolDock->setWidget(toolBox);
addDockWidget(Qt::RightDockWidgetArea, toolDock);
- viewMenu->addAction(toolDock->toggleViewAction());
+
+ QAction *toggleAct = toolDock->toggleViewAction();
+ toggleAct->setToolTip(tr("Toggle the tools dock widget"));
+ viewMenu->addAction(toggleAct);
}
void VMainWindow::initAvatar()
@@ -1154,6 +1243,9 @@ void VMainWindow::updateActionStateFromTabStatusChange(const VFile *p_file,
noteInfoAct->setEnabled(p_file && p_file->getType() == FileType::Normal);
m_insertImageAct->setEnabled(p_file && p_editMode);
+
+ setActionsVisible(m_editToolBar, p_file && p_editMode);
+
// Find/Replace
m_findReplaceAct->setEnabled(p_file);
m_findNextAct->setEnabled(p_file);
diff --git a/src/vmainwindow.h b/src/vmainwindow.h
index 7f63bb9f..02cb509f 100644
--- a/src/vmainwindow.h
+++ b/src/vmainwindow.h
@@ -108,6 +108,9 @@ private:
void initFileToolBar();
void initViewToolBar();
+ // Init the Edit toolbar.
+ void initEditToolBar();
+
void initMenuBar();
void initFileMenu();
void initEditMenu();
@@ -196,6 +199,9 @@ private:
// Menus
QMenu *viewMenu;
+ // Edit Toolbar.
+ QToolBar *m_editToolBar;
+
QVector predefinedColorPixmaps;
};
diff --git a/src/vmdeditoperations.cpp b/src/vmdeditoperations.cpp
index 239cc867..9a65e74b 100644
--- a/src/vmdeditoperations.cpp
+++ b/src/vmdeditoperations.cpp
@@ -241,10 +241,12 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_B:
{
- if (handleKeyB(p_event)) {
+ if (modifiers == Qt::ControlModifier) {
+ decorateBold();
+ p_event->accept();
ret = true;
- goto exit;
}
+
break;
}
@@ -259,19 +261,23 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_I:
{
- if (handleKeyI(p_event)) {
+ if (modifiers == Qt::ControlModifier) {
+ decorateItalic();
+ p_event->accept();
ret = true;
- goto exit;
}
+
break;
}
case Qt::Key_O:
{
- if (handleKeyO(p_event)) {
+ if (modifiers == Qt::ControlModifier) {
+ decorateInlineCode();
+ p_event->accept();
ret = true;
- goto exit;
}
+
break;
}
@@ -435,51 +441,6 @@ bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
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.
- // Or if there are two * after current cursor, just skip them.
- cursor.beginEditBlock();
- int pos = cursor.positionInBlock();
- bool hasStars = false;
- QString text = cursor.block().text();
- if (pos <= text.size() - 2) {
- if (text[pos] == '*' && text[pos + 1] == '*') {
- hasStars = true;
- }
- }
- if (hasStars) {
- cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2);
- } else {
- 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) {
@@ -493,95 +454,6 @@ bool VMdEditOperations::handleKeyH(QKeyEvent *p_event)
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.
- // Or if there are one * after current cursor, just skip it.
- cursor.beginEditBlock();
- int pos = cursor.positionInBlock();
- bool hasStar = false;
- QString text = cursor.block().text();
- if (pos <= text.size() - 1) {
- if (text[pos] == '*') {
- hasStar = true;
- }
- }
- if (hasStar) {
- cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
- } else {
- cursor.insertText("**");
- cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
- }
- cursor.endEditBlock();
- m_editor->setTextCursor(cursor);
- }
-
- p_event->accept();
- return true;
- }
- return false;
-}
-
-bool VMdEditOperations::handleKeyO(QKeyEvent *p_event)
-{
- if (p_event->modifiers() == Qt::ControlModifier) {
- // Ctrl+O, inline codeblock.
- 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.
- // Or if there are one ` after current cursor, just skip it.
- cursor.beginEditBlock();
- int pos = cursor.positionInBlock();
- bool hasBackquote = false;
- QString text = cursor.block().text();
- if (pos <= text.size() - 1) {
- if (text[pos] == '`') {
- hasBackquote = true;
- }
- }
- if (hasBackquote) {
- cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
- } else {
- 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) {
@@ -775,3 +647,140 @@ bool VMdEditOperations::insertTitle(int p_level)
return true;
}
+void VMdEditOperations::decorateText(TextDecoration p_decoration)
+{
+ if (p_decoration == TextDecoration::None) {
+ return;
+ }
+
+ m_vim->setMode(VimMode::Insert, false);
+
+ switch (p_decoration) {
+ case TextDecoration::Bold:
+ decorateBold();
+ break;
+
+ case TextDecoration::Italic:
+ decorateItalic();
+ break;
+
+ case TextDecoration::InlineCode:
+ decorateInlineCode();
+ break;
+
+ default:
+ qDebug() << "decoration" << (int)p_decoration << "is not implemented yet";
+ break;
+ }
+}
+
+void VMdEditOperations::decorateBold()
+{
+ QTextCursor cursor = m_editor->textCursor();
+ cursor.beginEditBlock();
+ if (cursor.hasSelection()) {
+ // Insert ** around the selected text.
+ int start = cursor.selectionStart();
+ int end = cursor.selectionEnd();
+ cursor.clearSelection();
+ cursor.setPosition(start, QTextCursor::MoveAnchor);
+ cursor.insertText("**");
+ cursor.setPosition(end + 2, QTextCursor::MoveAnchor);
+ cursor.insertText("**");
+ } else {
+ // Insert **** and place cursor in the middle.
+ // Or if there are two * after current cursor, just skip them.
+ int pos = cursor.positionInBlock();
+ bool hasStars = false;
+ QString text = cursor.block().text();
+ if (pos <= text.size() - 2) {
+ if (text[pos] == '*' && text[pos + 1] == '*') {
+ hasStars = true;
+ }
+ }
+
+ if (hasStars) {
+ cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2);
+ } else {
+ cursor.insertText("****");
+ cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2);
+ }
+ }
+
+ cursor.endEditBlock();
+ m_editor->setTextCursor(cursor);
+}
+
+void VMdEditOperations::decorateItalic()
+{
+ QTextCursor cursor = m_editor->textCursor();
+ cursor.beginEditBlock();
+ if (cursor.hasSelection()) {
+ // Insert * around the selected text.
+ int start = cursor.selectionStart();
+ int end = cursor.selectionEnd();
+ cursor.clearSelection();
+ cursor.setPosition(start, QTextCursor::MoveAnchor);
+ cursor.insertText("*");
+ cursor.setPosition(end + 1, QTextCursor::MoveAnchor);
+ cursor.insertText("*");
+ } else {
+ // Insert ** and place cursor in the middle.
+ // Or if there are one * after current cursor, just skip it.
+ int pos = cursor.positionInBlock();
+ bool hasStar = false;
+ QString text = cursor.block().text();
+ if (pos <= text.size() - 1) {
+ if (text[pos] == '*') {
+ hasStar = true;
+ }
+ }
+
+ if (hasStar) {
+ cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
+ } else {
+ cursor.insertText("**");
+ cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
+ }
+ }
+
+ cursor.endEditBlock();
+ m_editor->setTextCursor(cursor);
+}
+
+void VMdEditOperations::decorateInlineCode()
+{
+ QTextCursor cursor = m_editor->textCursor();
+ cursor.beginEditBlock();
+ if (cursor.hasSelection()) {
+ // Insert ` around the selected text.
+ int start = cursor.selectionStart();
+ int end = cursor.selectionEnd();
+ cursor.clearSelection();
+ cursor.setPosition(start, QTextCursor::MoveAnchor);
+ cursor.insertText("`");
+ cursor.setPosition(end + 1, QTextCursor::MoveAnchor);
+ cursor.insertText("`");
+ } else {
+ // Insert `` and place cursor in the middle.
+ // Or if there are one ` after current cursor, just skip it.
+ int pos = cursor.positionInBlock();
+ bool hasBackquote = false;
+ QString text = cursor.block().text();
+ if (pos <= text.size() - 1) {
+ if (text[pos] == '`') {
+ hasBackquote = true;
+ }
+ }
+
+ if (hasBackquote) {
+ cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
+ } else {
+ cursor.insertText("``");
+ cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
+ }
+ }
+
+ cursor.endEditBlock();
+ m_editor->setTextCursor(cursor);
+}
diff --git a/src/vmdeditoperations.h b/src/vmdeditoperations.h
index a5fa4143..c297c49b 100644
--- a/src/vmdeditoperations.h
+++ b/src/vmdeditoperations.h
@@ -21,6 +21,10 @@ public:
bool handleKeyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
bool insertImageFromURL(const QUrl &p_imageUrl) Q_DECL_OVERRIDE;
+ // Insert decoration markers or decorate selected text.
+ // If it is Vim Normal mode, change to Insert mode first.
+ void decorateText(TextDecoration p_decoration) Q_DECL_OVERRIDE;
+
private:
void insertImageFromPath(const QString &title, const QString &path, const QString &oriImagePath);
@@ -32,10 +36,7 @@ private:
// 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 handleKeyO(QKeyEvent *p_event);
bool handleKeyU(QKeyEvent *p_event);
bool handleKeyW(QKeyEvent *p_event);
bool handleKeyEsc(QKeyEvent *p_event);
@@ -46,6 +47,15 @@ private:
// Change the sequence number of a list block.
void changeListBlockSeqNumber(QTextBlock &p_block, int p_seq);
+ // Insert bold marker or set selected text bold.
+ void decorateBold();
+
+ // Insert italic marker or set selected text italic.
+ void decorateItalic();
+
+ // Insert inline-code marker or set selected text inline-coded.
+ void decorateInlineCode();
+
// The cursor position after auto indent or auto list.
// It will be -1 if last key press do not trigger the auto indent or auto list.
int m_autoIndentPos;
diff --git a/src/vmdtab.cpp b/src/vmdtab.cpp
index 2dcc397d..321f3d6c 100644
--- a/src/vmdtab.cpp
+++ b/src/vmdtab.cpp
@@ -665,3 +665,10 @@ VEditTabInfo VMdTab::createEditTabInfo()
return info;
}
+
+void VMdTab::decorateText(TextDecoration p_decoration)
+{
+ if (m_editor) {
+ m_editor->decorateText(p_decoration);
+ }
+}
diff --git a/src/vmdtab.h b/src/vmdtab.h
index 31c00f86..016363f3 100644
--- a/src/vmdtab.h
+++ b/src/vmdtab.h
@@ -57,6 +57,9 @@ public:
void requestUpdateVimStatus() Q_DECL_OVERRIDE;
+ // Insert decoration markers or decorate selected text.
+ void decorateText(TextDecoration p_decoration) Q_DECL_OVERRIDE;
+
public slots:
// Enter edit mode.
void editFile() Q_DECL_OVERRIDE;
diff --git a/src/vnote.qrc b/src/vnote.qrc
index 04f98be6..f9a8cc1e 100644
--- a/src/vnote.qrc
+++ b/src/vnote.qrc
@@ -111,5 +111,10 @@
resources/icons/vnote_update.svg
utils/flowchart.js/flowchart.min.js
utils/flowchart.js/raphael.min.js
+ resources/icons/bold.svg
+ resources/icons/italic.svg
+ resources/icons/underline.svg
+ resources/icons/strikethrough.svg
+ resources/icons/inline_code.svg