support Insert Link tool bar button

Support Ctrl+L to insert a link.
This commit is contained in:
Le Tan 2017-10-19 19:42:50 +08:00
parent da027e9fd9
commit e66b70b6ff
16 changed files with 322 additions and 10 deletions

View File

@ -0,0 +1,132 @@
#include "vinsertlinkdialog.h"
#include <QtWidgets>
#include "vlineedit.h"
VInsertLinkDialog::VInsertLinkDialog(const QString &p_title,
const QString &p_text,
const QString &p_info,
const QString &p_linkText,
const QString &p_linkUrl,
QWidget *p_parent)
: QDialog(p_parent)
{
setupUI(p_title, p_text, p_info, p_linkText, p_linkUrl);
fetchLinkFromClipboard();
handleInputChanged();
}
void VInsertLinkDialog::setupUI(const QString &p_title,
const QString &p_text,
const QString &p_info,
const QString &p_linkText,
const QString &p_linkUrl)
{
QLabel *textLabel = NULL;
if (!p_text.isEmpty()) {
textLabel = new QLabel(p_text);
textLabel->setWordWrap(true);
}
QLabel *infoLabel = NULL;
if (!p_info.isEmpty()) {
infoLabel = new QLabel(p_info);
infoLabel->setWordWrap(true);
}
m_linkTextEdit = new VLineEdit(p_linkText);
m_linkTextEdit->selectAll();
m_linkUrlEdit = new QLineEdit(p_linkUrl);
m_linkUrlEdit->setToolTip(tr("Absolute or relative path of the link"));
QFormLayout *inputLayout = new QFormLayout();
inputLayout->addRow(tr("&Text:"), m_linkTextEdit);
inputLayout->addRow(tr("&URL:"), m_linkUrlEdit);
// Ok is the default button.
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
m_linkTextEdit->setMinimumWidth(okBtn->sizeHint().width() * 3);
QVBoxLayout *mainLayout = new QVBoxLayout;
if (textLabel) {
mainLayout->addWidget(textLabel);
}
if (infoLabel) {
mainLayout->addWidget(infoLabel);
}
mainLayout->addLayout(inputLayout);
mainLayout->addWidget(m_btnBox);
setLayout(mainLayout);
setWindowTitle(p_title);
connect(m_linkTextEdit, &QLineEdit::textChanged,
this, &VInsertLinkDialog::handleInputChanged);
connect(m_linkUrlEdit, &QLineEdit::textChanged,
this, &VInsertLinkDialog::handleInputChanged);
}
void VInsertLinkDialog::handleInputChanged()
{
bool textOk = true;
if (m_linkTextEdit->getEvaluatedText().isEmpty()) {
textOk = false;
}
bool urlOk = true;
if (m_linkUrlEdit->text().isEmpty()) {
urlOk = false;
}
QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
okBtn->setEnabled(textOk && urlOk);
}
void VInsertLinkDialog::fetchLinkFromClipboard()
{
if (!m_linkUrlEdit->text().isEmpty()
|| !m_linkTextEdit->text().isEmpty()) {
return;
}
QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
if (!mimeData->hasText()) {
return;
}
QString text = mimeData->text();
if (text.isEmpty()) {
return;
}
QUrl url = QUrl::fromUserInput(text);
if (url.isValid()) {
if (m_linkUrlEdit->text().isEmpty()) {
m_linkUrlEdit->setText(text);
}
} else if (m_linkTextEdit->text().isEmpty()) {
m_linkTextEdit->setText(text);
}
}
QString VInsertLinkDialog::getLinkText() const
{
return m_linkTextEdit->getEvaluatedText();
}
QString VInsertLinkDialog::getLinkUrl() const
{
return m_linkUrlEdit->text();
}

View File

@ -0,0 +1,46 @@
#ifndef VINSERTLINKDIALOG_H
#define VINSERTLINKDIALOG_H
#include <QDialog>
#include <QString>
class VLineEdit;
class QLineEdit;
class QDialogButtonBox;
class VInsertLinkDialog : public QDialog
{
Q_OBJECT
public:
VInsertLinkDialog(const QString &p_title,
const QString &p_text,
const QString &p_info,
const QString &p_linkText,
const QString &p_linkUrl,
QWidget *p_parent = nullptr);
QString getLinkText() const;
QString getLinkUrl() const;
private slots:
void handleInputChanged();
private:
void setupUI(const QString &p_title,
const QString &p_text,
const QString &p_info,
const QString &p_linkText,
const QString &p_linkUrl);
// Infer link text/url from clipboard only when text and url are both empty.
void fetchLinkFromClipboard();
VLineEdit *m_linkTextEdit;
QLineEdit *m_linkUrlEdit;
QDialogButtonBox *m_btnBox;
};
#endif // VINSERTLINKDIALOG_H

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<path d="M256.5,208H256v0C256.2,208,256.3,208,256.5,208z"/>
<path d="M368.5,160H320c0,0,26,17,31.6,48H368h0.5c17.6,0,31.5,13.9,31.5,31.5v32c0,17.6-13.9,32.5-31.5,32.5h-112
c-17.6,0-32.5-14.9-32.5-32.5V240h-48v31.5c0,11.5,2.5,22.5,6.9,32.5c12.6,28.2,40.9,48,73.6,48h112c44.2,0,79.5-36.3,79.5-80.5
v-32C448,195.3,412.7,160,368.5,160z"/>
<path d="M329.6,208c-12.1-28.3-40.1-48-73.1-48h-112c-44.2,0-80.5,35.3-80.5,79.5v32c0,44.2,36.3,80.5,80.5,80.5H192
c0,0-25.8-17-32.1-48h-15.4c-17.6,0-32.5-14.9-32.5-32.5v-32c0-17.6,14.9-31.5,32.5-31.5H256h0.5c17.6,0,31.5,13.9,31.5,31.5v32
c0,0.2,0,0.3,0,0.5h48c0-0.2,0-0.3,0-0.5v-32C336,228.3,333.7,217.6,329.6,208z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -80,7 +80,8 @@ SOURCES += main.cpp\
vfilesessioninfo.cpp \ vfilesessioninfo.cpp \
vtableofcontent.cpp \ vtableofcontent.cpp \
utils/vmetawordmanager.cpp \ utils/vmetawordmanager.cpp \
vlineedit.cpp vlineedit.cpp \
dialog/vinsertlinkdialog.cpp
HEADERS += vmainwindow.h \ HEADERS += vmainwindow.h \
vdirectorytree.h \ vdirectorytree.h \
@ -148,7 +149,8 @@ HEADERS += vmainwindow.h \
vfilesessioninfo.h \ vfilesessioninfo.h \
vtableofcontent.h \ vtableofcontent.h \
utils/vmetawordmanager.h \ utils/vmetawordmanager.h \
vlineedit.h vlineedit.h \
dialog/vinsertlinkdialog.h
RESOURCES += \ RESOURCES += \
vnote.qrc \ vnote.qrc \

View File

@ -10,7 +10,7 @@
#include "utils/vmetawordmanager.h" #include "utils/vmetawordmanager.h"
#include "veditoperations.h" #include "veditoperations.h"
#include "vedittab.h" #include "vedittab.h"
#include "dialog/vinsertlinkdialog.h"
extern VConfigManager *g_config; extern VConfigManager *g_config;
@ -199,6 +199,49 @@ void VEdit::insertImage()
} }
} }
void VEdit::insertLink()
{
if (!m_editOps) {
return;
}
QString text;
QString linkText, linkUrl;
QTextCursor cursor = textCursor();
if (cursor.hasSelection()) {
text = VEditUtils::selectedText(cursor).trimmed();
// Only pure space is accepted.
QRegExp reg("[\\S ]*");
if (reg.exactMatch(text)) {
QUrl url = QUrl::fromUserInput(text,
m_file->fetchBasePath());
QRegExp urlReg("[\\.\\\\/]");
if (url.isValid()
&& text.contains(urlReg)) {
// Url.
linkUrl = text;
} else {
// Text.
linkText = text;
}
}
}
VInsertLinkDialog dialog(tr("Insert Link"),
"",
"",
linkText,
linkUrl,
this);
if (dialog.exec() == QDialog::Accepted) {
linkText = dialog.getLinkText();
linkUrl = dialog.getLinkUrl();
Q_ASSERT(!linkText.isEmpty() && !linkUrl.isEmpty());
m_editOps->insertLink(linkText, linkUrl);
}
}
bool VEdit::peekText(const QString &p_text, uint p_options, bool p_forward) bool VEdit::peekText(const QString &p_text, uint p_options, bool p_forward)
{ {
if (p_text.isEmpty()) { if (p_text.isEmpty()) {

View File

@ -91,6 +91,9 @@ public:
// User requests to insert an image. // User requests to insert an image.
virtual void insertImage(); virtual void insertImage();
// User requests to insert a link.
virtual void insertLink();
// Used for incremental search. // Used for incremental search.
// User has enter the content to search, but does not enter the "find" button yet. // User has enter the content to search, but does not enter the "find" button yet.
bool peekText(const QString &p_text, uint p_options, bool p_forward = true); bool peekText(const QString &p_text, uint p_options, bool p_forward = true);

View File

@ -90,3 +90,10 @@ void VEditOperations::requestUpdateVimStatus()
{ {
emit vimStatusUpdated(m_vim); emit vimStatusUpdated(m_vim);
} }
void VEditOperations::setVimMode(VimMode p_mode)
{
if (m_vim && m_editConfig->m_enableVimMode) {
m_vim->setMode(p_mode);
}
}

View File

@ -18,11 +18,18 @@ class VEditOperations: public QObject
Q_OBJECT Q_OBJECT
public: public:
VEditOperations(VEdit *p_editor, VFile *p_file); VEditOperations(VEdit *p_editor, VFile *p_file);
virtual ~VEditOperations(); virtual ~VEditOperations();
virtual bool insertImageFromMimeData(const QMimeData *source) = 0; virtual bool insertImageFromMimeData(const QMimeData *source) = 0;
virtual bool insertImage() = 0; virtual bool insertImage() = 0;
virtual bool insertImageFromURL(const QUrl &p_imageUrl) = 0; virtual bool insertImageFromURL(const QUrl &p_imageUrl) = 0;
virtual bool insertLink(const QString &p_linkText,
const QString &p_linkUrl) = 0;
// Return true if @p_event has been handled and no need to be further // Return true if @p_event has been handled and no need to be further
// processed. // processed.
virtual bool handleKeyPressEvent(QKeyEvent *p_event) = 0; virtual bool handleKeyPressEvent(QKeyEvent *p_event) = 0;
@ -63,11 +70,4 @@ protected:
VVim *m_vim; VVim *m_vim;
}; };
inline void VEditOperations::setVimMode(VimMode p_mode)
{
if (m_vim) {
m_vim->setMode(p_mode);
}
}
#endif // VEDITOPERATIONS_H #endif // VEDITOPERATIONS_H

View File

@ -120,3 +120,7 @@ bool VEditTab::tabHasFocus() const
QWidget *wid = QApplication::focusWidget(); QWidget *wid = QApplication::focusWidget();
return wid == this || isAncestorOf(wid); return wid == this || isAncestorOf(wid);
} }
void VEditTab::insertLink()
{
}

View File

@ -49,6 +49,9 @@ public:
// User requests to insert image. // User requests to insert image.
virtual void insertImage() = 0; virtual void insertImage() = 0;
// User requests to insert link.
virtual void insertLink();
// Search @p_text in current note. // Search @p_text in current note.
virtual void findText(const QString &p_text, uint p_options, bool p_peek, virtual void findText(const QString &p_text, uint p_options, bool p_peek,
bool p_forward = true) = 0; bool p_forward = true) = 0;

View File

@ -463,6 +463,19 @@ void VMainWindow::initEditToolBar(QSize p_iconSize)
m_editToolBar->addAction(inlineCodeAct); m_editToolBar->addAction(inlineCodeAct);
// Insert link.
QAction *insetLinkAct = new QAction(QIcon(":/resources/icons/link.svg"),
tr("Insert Link (Ctrl+L)"), this);
insetLinkAct->setStatusTip(tr("Insert a link"));
connect(insetLinkAct, &QAction::triggered,
this, [this]() {
if (m_curTab) {
m_curTab->insertLink();
}
});
m_editToolBar->addAction(insetLinkAct);
// Insert image. // Insert image.
QAction *insertImageAct = new QAction(QIcon(":/resources/icons/insert_image.svg"), QAction *insertImageAct = new QAction(QIcon(":/resources/icons/insert_image.svg"),
tr("Insert Image"), tr("Insert Image"),

View File

@ -278,6 +278,17 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
break; break;
} }
case Qt::Key_L:
{
if (modifiers == Qt::ControlModifier) {
m_editor->insertLink();
p_event->accept();
ret = true;
}
break;
}
case Qt::Key_O: case Qt::Key_O:
{ {
if (modifiers == Qt::ControlModifier) { if (modifiers == Qt::ControlModifier) {
@ -832,3 +843,16 @@ void VMdEditOperations::decorateStrikethrough()
cursor.endEditBlock(); cursor.endEditBlock();
m_editor->setTextCursor(cursor); m_editor->setTextCursor(cursor);
} }
bool VMdEditOperations::insertLink(const QString &p_linkText,
const QString &p_linkUrl)
{
QString link = QString("[%1](%2)").arg(p_linkText).arg(p_linkUrl);
QTextCursor cursor = m_editor->textCursor();
cursor.insertText(link);
m_editor->setTextCursor(cursor);
setVimMode(VimMode::Insert);
return true;
}

View File

@ -16,11 +16,18 @@ class VMdEditOperations : public VEditOperations
Q_OBJECT Q_OBJECT
public: public:
VMdEditOperations(VEdit *p_editor, VFile *p_file); VMdEditOperations(VEdit *p_editor, VFile *p_file);
bool insertImageFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE; bool insertImageFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
bool insertImage() Q_DECL_OVERRIDE; bool insertImage() Q_DECL_OVERRIDE;
bool handleKeyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE; bool handleKeyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
bool insertImageFromURL(const QUrl &p_imageUrl) Q_DECL_OVERRIDE; bool insertImageFromURL(const QUrl &p_imageUrl) Q_DECL_OVERRIDE;
bool insertLink(const QString &p_linkText,
const QString &p_linkUrl);
// Insert decoration markers or decorate selected text. // Insert decoration markers or decorate selected text.
// If it is Vim Normal mode, change to Insert mode first. // If it is Vim Normal mode, change to Insert mode first.
void decorateText(TextDecoration p_decoration) Q_DECL_OVERRIDE; void decorateText(TextDecoration p_decoration) Q_DECL_OVERRIDE;

View File

@ -469,6 +469,16 @@ void VMdTab::insertImage()
m_editor->insertImage(); m_editor->insertImage();
} }
void VMdTab::insertLink()
{
if (!m_isEditMode) {
return;
}
Q_ASSERT(m_editor);
m_editor->insertLink();
}
void VMdTab::findText(const QString &p_text, uint p_options, bool p_peek, void VMdTab::findText(const QString &p_text, uint p_options, bool p_peek,
bool p_forward) bool p_forward)
{ {

View File

@ -36,6 +36,8 @@ public:
void insertImage() Q_DECL_OVERRIDE; void insertImage() Q_DECL_OVERRIDE;
void insertLink() Q_DECL_OVERRIDE;
// Search @p_text in current note. // Search @p_text in current note.
void findText(const QString &p_text, uint p_options, bool p_peek, void findText(const QString &p_text, uint p_options, bool p_peek,
bool p_forward = true) Q_DECL_OVERRIDE; bool p_forward = true) Q_DECL_OVERRIDE;

View File

@ -132,5 +132,6 @@
<file>resources/icons/create_subdir.svg</file> <file>resources/icons/create_subdir.svg</file>
<file>resources/icons/compact_mode.svg</file> <file>resources/icons/compact_mode.svg</file>
<file>resources/icons/heading_sequence.svg</file> <file>resources/icons/heading_sequence.svg</file>
<file>resources/icons/link.svg</file>
</qresource> </qresource>
</RCC> </RCC>