mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 22:09:52 +08:00
LivePreview: support bidirectional smart live preview
This commit is contained in:
parent
2338002b1f
commit
060c02297b
@ -1626,7 +1626,8 @@ var htmlToText = function(identifier, id, timeStamp, html) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var performSmartLivePreview = function(lang, text, hints, isRegex) {
|
var performSmartLivePreview = function(lang, text, hints, isRegex) {
|
||||||
if (previewDiv.style.display == 'none') {
|
if (previewDiv.style.display == 'none'
|
||||||
|
|| document.getSelection().type == 'Range') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +247,10 @@ max_tag_label_length=10
|
|||||||
max_num_of_tag_labels=3
|
max_num_of_tag_labels=3
|
||||||
|
|
||||||
; Smart live preview
|
; Smart live preview
|
||||||
smart_live_preview=true
|
; 0 - disable smart live preview
|
||||||
|
; 1 - editor to web
|
||||||
|
; 2 - web to editor
|
||||||
|
smart_live_preview=3
|
||||||
|
|
||||||
; Support multiple keyboard layout
|
; Support multiple keyboard layout
|
||||||
multiple_keyboard_layout=true
|
multiple_keyboard_layout=true
|
||||||
@ -467,8 +470,12 @@ ApplySnippet=S
|
|||||||
Export=O
|
Export=O
|
||||||
; Toggle live preview
|
; Toggle live preview
|
||||||
LivePreview=I
|
LivePreview=I
|
||||||
|
; Expand or restore live preview area
|
||||||
|
ExpandLivePreview=U
|
||||||
; Focus edit area
|
; Focus edit area
|
||||||
FocusEditArea=Y
|
FocusEditArea=Y
|
||||||
|
; Parse HTML and paste
|
||||||
|
ParseAndPaste=P
|
||||||
|
|
||||||
[external_editors]
|
[external_editors]
|
||||||
; Define external editors which could be called to edit notes
|
; Define external editors which could be called to edit notes
|
||||||
|
@ -312,7 +312,7 @@ void VConfigManager::initialize()
|
|||||||
"max_num_of_tag_labels").toInt();
|
"max_num_of_tag_labels").toInt();
|
||||||
|
|
||||||
m_smartLivePreview = getConfigFromSettings("global",
|
m_smartLivePreview = getConfigFromSettings("global",
|
||||||
"smart_live_preview").toBool();
|
"smart_live_preview").toInt();
|
||||||
|
|
||||||
m_multipleKeyboardLayout = getConfigFromSettings("global",
|
m_multipleKeyboardLayout = getConfigFromSettings("global",
|
||||||
"multiple_keyboard_layout").toBool();
|
"multiple_keyboard_layout").toBool();
|
||||||
|
@ -60,6 +60,12 @@ enum class KeyMode
|
|||||||
Invalid
|
Invalid
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum SmartLivePreview
|
||||||
|
{
|
||||||
|
Disabled = 0,
|
||||||
|
EditorToWeb = 0x1,
|
||||||
|
WebToEditor = 0x2
|
||||||
|
};
|
||||||
|
|
||||||
class VConfigManager : public QObject
|
class VConfigManager : public QObject
|
||||||
{
|
{
|
||||||
@ -547,7 +553,8 @@ public:
|
|||||||
|
|
||||||
QChar getVimLeaderKey() const;
|
QChar getVimLeaderKey() const;
|
||||||
|
|
||||||
bool getSmartLivePreview() const;
|
int getSmartLivePreview() const;
|
||||||
|
void setSmartLivePreview(int p_preview);
|
||||||
|
|
||||||
bool getMultipleKeyboardLayout() const;
|
bool getMultipleKeyboardLayout() const;
|
||||||
|
|
||||||
@ -989,7 +996,7 @@ private:
|
|||||||
QChar m_vimLeaderKey;
|
QChar m_vimLeaderKey;
|
||||||
|
|
||||||
// Smart live preview.
|
// Smart live preview.
|
||||||
bool m_smartLivePreview;
|
int m_smartLivePreview;
|
||||||
|
|
||||||
// Support multiple keyboard layout.
|
// Support multiple keyboard layout.
|
||||||
bool m_multipleKeyboardLayout;
|
bool m_multipleKeyboardLayout;
|
||||||
@ -2550,11 +2557,21 @@ inline QChar VConfigManager::getVimLeaderKey() const
|
|||||||
return m_vimLeaderKey;
|
return m_vimLeaderKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool VConfigManager::getSmartLivePreview() const
|
inline int VConfigManager::getSmartLivePreview() const
|
||||||
{
|
{
|
||||||
return m_smartLivePreview;
|
return m_smartLivePreview;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void VConfigManager::setSmartLivePreview(int p_preview)
|
||||||
|
{
|
||||||
|
if (m_smartLivePreview == p_preview) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_smartLivePreview = p_preview;
|
||||||
|
setConfigToSettings("global", "smart_live_preview", m_smartLivePreview);
|
||||||
|
}
|
||||||
|
|
||||||
inline bool VConfigManager::getMultipleKeyboardLayout() const
|
inline bool VConfigManager::getMultipleKeyboardLayout() const
|
||||||
{
|
{
|
||||||
return m_multipleKeyboardLayout;
|
return m_multipleKeyboardLayout;
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
#include "vcaptain.h"
|
#include "vcaptain.h"
|
||||||
#include "vfilelist.h"
|
#include "vfilelist.h"
|
||||||
#include "vmathjaxpreviewhelper.h"
|
#include "vmathjaxpreviewhelper.h"
|
||||||
|
#include "vmdtab.h"
|
||||||
|
#include "vmdeditor.h"
|
||||||
|
|
||||||
extern VConfigManager *g_config;
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
@ -954,6 +956,14 @@ void VEditArea::registerCaptainTargets()
|
|||||||
g_config->getCaptainShortcutKeySequence("LivePreview"),
|
g_config->getCaptainShortcutKeySequence("LivePreview"),
|
||||||
this,
|
this,
|
||||||
toggleLivePreviewByCaptain);
|
toggleLivePreviewByCaptain);
|
||||||
|
captain->registerCaptainTarget(tr("ExpandLivePreview"),
|
||||||
|
g_config->getCaptainShortcutKeySequence("ExpandLivePreview"),
|
||||||
|
this,
|
||||||
|
expandLivePreviewByCaptain);
|
||||||
|
captain->registerCaptainTarget(tr("ParseAndPaste"),
|
||||||
|
g_config->getCaptainShortcutKeySequence("ParseAndPaste"),
|
||||||
|
this,
|
||||||
|
parseAndPasteByCaptain);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VEditArea::activateTabByCaptain(void *p_target, void *p_data, int p_idx)
|
bool VEditArea::activateTabByCaptain(void *p_target, void *p_data, int p_idx)
|
||||||
@ -1139,9 +1149,40 @@ bool VEditArea::toggleLivePreviewByCaptain(void *p_target, void *p_data)
|
|||||||
Q_UNUSED(p_data);
|
Q_UNUSED(p_data);
|
||||||
VEditArea *obj = static_cast<VEditArea *>(p_target);
|
VEditArea *obj = static_cast<VEditArea *>(p_target);
|
||||||
|
|
||||||
VEditTab *tab = obj->getCurrentTab();
|
VMdTab *tab = dynamic_cast<VMdTab *>(obj->getCurrentTab());
|
||||||
if (tab) {
|
if (tab) {
|
||||||
tab->toggleLivePreview();
|
if (tab->toggleLivePreview()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VEditArea::expandLivePreviewByCaptain(void *p_target, void *p_data)
|
||||||
|
{
|
||||||
|
Q_UNUSED(p_data);
|
||||||
|
VEditArea *obj = static_cast<VEditArea *>(p_target);
|
||||||
|
|
||||||
|
VMdTab *tab = dynamic_cast<VMdTab *>(obj->getCurrentTab());
|
||||||
|
if (tab) {
|
||||||
|
if (tab->expandRestorePreviewArea()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VEditArea::parseAndPasteByCaptain(void *p_target, void *p_data)
|
||||||
|
{
|
||||||
|
Q_UNUSED(p_data);
|
||||||
|
VEditArea *obj = static_cast<VEditArea *>(p_target);
|
||||||
|
|
||||||
|
const VMdTab *tab = dynamic_cast<const VMdTab *>(obj->getCurrentTab());
|
||||||
|
if (tab && tab->isEditMode()) {
|
||||||
|
VMdEditor *editor = tab->getEditor();
|
||||||
|
editor->parseAndPaste();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -234,6 +234,12 @@ private:
|
|||||||
// Toggle live preview.
|
// Toggle live preview.
|
||||||
static bool toggleLivePreviewByCaptain(void *p_target, void *p_data);
|
static bool toggleLivePreviewByCaptain(void *p_target, void *p_data);
|
||||||
|
|
||||||
|
// Expand live preview.
|
||||||
|
static bool expandLivePreviewByCaptain(void *p_target, void *p_data);
|
||||||
|
|
||||||
|
// Parse and paste.
|
||||||
|
static bool parseAndPasteByCaptain(void *p_target, void *p_data);
|
||||||
|
|
||||||
// End Captain mode functions.
|
// End Captain mode functions.
|
||||||
|
|
||||||
int curWindowIndex;
|
int curWindowIndex;
|
||||||
|
@ -519,35 +519,43 @@ bool VEditor::findText(const QString &p_text,
|
|||||||
bool wrapped = false;
|
bool wrapped = false;
|
||||||
QTextCursor retCursor;
|
QTextCursor retCursor;
|
||||||
int matches = 0;
|
int matches = 0;
|
||||||
int start = p_forward ? cursor.position() + 1 : cursor.position();
|
int start = p_cursor ? p_cursor->position() : cursor.position();
|
||||||
if (p_cursor) {
|
|
||||||
start = p_forward ? p_cursor->position() + 1 : p_cursor->position();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_useLeftSideOfCursor) {
|
if (p_useLeftSideOfCursor) {
|
||||||
--start;
|
--start;
|
||||||
}
|
}
|
||||||
|
int skipPosition = start;
|
||||||
|
|
||||||
bool found = findTextHelper(p_text, p_options, p_forward, start,
|
bool found = false;
|
||||||
wrapped, retCursor);
|
while (true) {
|
||||||
if (found) {
|
found = findTextHelper(p_text, p_options, p_forward, start, wrapped, retCursor);
|
||||||
Q_ASSERT(!retCursor.isNull());
|
if (found) {
|
||||||
if (wrapped) {
|
Q_ASSERT(!retCursor.isNull());
|
||||||
showWrapLabel();
|
if (wrapped) {
|
||||||
}
|
showWrapLabel();
|
||||||
|
}
|
||||||
|
|
||||||
if (p_cursor) {
|
if (p_forward && retCursor.selectionStart() == skipPosition) {
|
||||||
p_cursor->setPosition(retCursor.selectionStart(), p_moveMode);
|
// Skip the first match.
|
||||||
|
skipPosition = -1;
|
||||||
|
start = retCursor.selectionEnd();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_cursor) {
|
||||||
|
p_cursor->setPosition(retCursor.selectionStart(), p_moveMode);
|
||||||
|
} else {
|
||||||
|
cursor.setPosition(retCursor.selectionStart(), p_moveMode);
|
||||||
|
setTextCursorW(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightSearchedWord(p_text, p_options);
|
||||||
|
highlightSearchedWordUnderCursor(retCursor);
|
||||||
|
matches = m_extraSelections[(int)SelectionId::SearchedKeyword].size();
|
||||||
} else {
|
} else {
|
||||||
cursor.setPosition(retCursor.selectionStart(), p_moveMode);
|
clearSearchedWordHighlight();
|
||||||
setTextCursorW(cursor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
highlightSearchedWord(p_text, p_options);
|
break;
|
||||||
highlightSearchedWordUnderCursor(retCursor);
|
|
||||||
matches = m_extraSelections[(int)SelectionId::SearchedKeyword].size();
|
|
||||||
} else {
|
|
||||||
clearSearchedWordHighlight();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matches == 0) {
|
if (matches == 0) {
|
||||||
@ -561,6 +569,18 @@ bool VEditor::findText(const QString &p_text,
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VEditor::findTextInRange(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
bool p_forward,
|
||||||
|
int p_start,
|
||||||
|
int p_end)
|
||||||
|
{
|
||||||
|
Q_UNUSED(p_start);
|
||||||
|
Q_UNUSED(p_end);
|
||||||
|
// TODO
|
||||||
|
return findText(p_text, p_options, p_forward);
|
||||||
|
}
|
||||||
|
|
||||||
void VEditor::highlightIncrementalSearchedWord(const QTextCursor &p_cursor)
|
void VEditor::highlightIncrementalSearchedWord(const QTextCursor &p_cursor)
|
||||||
{
|
{
|
||||||
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::IncrementalSearchedKeyword];
|
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::IncrementalSearchedKeyword];
|
||||||
|
@ -83,6 +83,13 @@ public:
|
|||||||
QTextCursor::MoveMode p_moveMode = QTextCursor::MoveAnchor,
|
QTextCursor::MoveMode p_moveMode = QTextCursor::MoveAnchor,
|
||||||
bool p_useLeftSideOfCursor = false);
|
bool p_useLeftSideOfCursor = false);
|
||||||
|
|
||||||
|
// Constrain the scope.
|
||||||
|
bool findTextInRange(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
bool p_forward,
|
||||||
|
int p_start,
|
||||||
|
int p_end);
|
||||||
|
|
||||||
void replaceText(const QString &p_text,
|
void replaceText(const QString &p_text,
|
||||||
uint p_options,
|
uint p_options,
|
||||||
const QString &p_replaceText,
|
const QString &p_replaceText,
|
||||||
|
@ -119,10 +119,6 @@ public:
|
|||||||
// Fetch tab stat info.
|
// Fetch tab stat info.
|
||||||
virtual VWordCountInfo fetchWordCountInfo(bool p_editMode) const;
|
virtual VWordCountInfo fetchWordCountInfo(bool p_editMode) const;
|
||||||
|
|
||||||
virtual void toggleLivePreview()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
// Enter edit mode
|
// Enter edit mode
|
||||||
virtual void editFile() = 0;
|
virtual void editFile() = 0;
|
||||||
|
@ -202,6 +202,7 @@ void VLivePreviewHelper::updateCodeBlocks(TimeStamp p_timeStamp, const QVector<V
|
|||||||
&& vcb.m_endBlock >= cursorBlock) {
|
&& vcb.m_endBlock >= cursorBlock) {
|
||||||
if (lastIndex == idx && cached && !oldCache) {
|
if (lastIndex == idx && cached && !oldCache) {
|
||||||
needUpdate = false;
|
needUpdate = false;
|
||||||
|
m_curLivePreviewInfo.update(vcb);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_cbIndex = idx;
|
m_cbIndex = idx;
|
||||||
@ -260,12 +261,16 @@ void VLivePreviewHelper::handleCursorPositionChanged()
|
|||||||
void VLivePreviewHelper::updateLivePreview()
|
void VLivePreviewHelper::updateLivePreview()
|
||||||
{
|
{
|
||||||
if (m_cbIndex < 0) {
|
if (m_cbIndex < 0) {
|
||||||
|
m_curLivePreviewInfo.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_ASSERT(!(m_cbIndex & ~INDEX_MASK));
|
Q_ASSERT(!(m_cbIndex & ~INDEX_MASK));
|
||||||
const CodeBlockPreviewInfo &cb = m_codeBlocks[m_cbIndex];
|
const CodeBlockPreviewInfo &cb = m_codeBlocks[m_cbIndex];
|
||||||
const VCodeBlock &vcb = cb.codeBlock();
|
const VCodeBlock &vcb = cb.codeBlock();
|
||||||
|
|
||||||
|
m_curLivePreviewInfo.update(vcb);
|
||||||
|
|
||||||
if (vcb.m_lang == "dot") {
|
if (vcb.m_lang == "dot") {
|
||||||
if (!m_graphvizHelper) {
|
if (!m_graphvizHelper) {
|
||||||
m_graphvizHelper = new VGraphvizHelper(this);
|
m_graphvizHelper = new VGraphvizHelper(this);
|
||||||
@ -530,7 +535,7 @@ void VLivePreviewHelper::performSmartLivePreview()
|
|||||||
{
|
{
|
||||||
if (m_cbIndex < 0
|
if (m_cbIndex < 0
|
||||||
|| m_cbIndex >= m_codeBlocks.size()
|
|| m_cbIndex >= m_codeBlocks.size()
|
||||||
|| !g_config->getSmartLivePreview()) {
|
|| !(g_config->getSmartLivePreview() & SmartLivePreview::EditorToWeb)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +92,36 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct LivePreviewInfo
|
||||||
|
{
|
||||||
|
LivePreviewInfo()
|
||||||
|
: m_startPos(),
|
||||||
|
m_endPos()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_startPos = 0;
|
||||||
|
m_endPos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(const VCodeBlock &p_cb)
|
||||||
|
{
|
||||||
|
m_startPos = p_cb.m_startPos;
|
||||||
|
m_endPos = p_cb.m_startPos + p_cb.m_text.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValid() const
|
||||||
|
{
|
||||||
|
return m_endPos > m_startPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
int m_startPos;
|
||||||
|
int m_endPos;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Manage live preview and inplace preview.
|
// Manage live preview and inplace preview.
|
||||||
class VLivePreviewHelper : public QObject
|
class VLivePreviewHelper : public QObject
|
||||||
{
|
{
|
||||||
@ -109,6 +139,8 @@ public:
|
|||||||
|
|
||||||
bool isPreviewEnabled() const;
|
bool isPreviewEnabled() const;
|
||||||
|
|
||||||
|
const LivePreviewInfo &getLivePreviewInfo() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateCodeBlocks(TimeStamp p_timeStamp, const QVector<VCodeBlock> &p_codeBlocks);
|
void updateCodeBlocks(TimeStamp p_timeStamp, const QVector<VCodeBlock> &p_codeBlocks);
|
||||||
|
|
||||||
@ -220,7 +252,6 @@ private:
|
|||||||
QString m_imageBackground;
|
QString m_imageBackground;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void checkLang(const QString &p_lang, bool &p_livePreview, bool &p_inplacePreview) const;
|
void checkLang(const QString &p_lang, bool &p_livePreview, bool &p_inplacePreview) const;
|
||||||
|
|
||||||
// Get image data for this code block for inplace preview.
|
// Get image data for this code block for inplace preview.
|
||||||
@ -277,6 +308,8 @@ private:
|
|||||||
int m_lastCursorBlock;
|
int m_lastCursorBlock;
|
||||||
|
|
||||||
QTimer *m_livePreviewTimer;
|
QTimer *m_livePreviewTimer;
|
||||||
|
|
||||||
|
LivePreviewInfo m_curLivePreviewInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool VLivePreviewHelper::isPreviewEnabled() const
|
inline bool VLivePreviewHelper::isPreviewEnabled() const
|
||||||
@ -292,4 +325,9 @@ inline qreal VLivePreviewHelper::getScaleFactor(const CodeBlockPreviewInfo &p_cb
|
|||||||
return m_scaleFactor;
|
return m_scaleFactor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline const LivePreviewInfo &VLivePreviewHelper::getLivePreviewInfo() const
|
||||||
|
{
|
||||||
|
return m_curLivePreviewInfo;
|
||||||
|
}
|
||||||
#endif // VLIVEPREVIEWHELPER_H
|
#endif // VLIVEPREVIEWHELPER_H
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "utils/vclipboardutils.h"
|
#include "utils/vclipboardutils.h"
|
||||||
#include "vplantumlhelper.h"
|
#include "vplantumlhelper.h"
|
||||||
#include "vgraphvizhelper.h"
|
#include "vgraphvizhelper.h"
|
||||||
|
#include "vmdtab.h"
|
||||||
|
|
||||||
extern VWebUtils *g_webUtils;
|
extern VWebUtils *g_webUtils;
|
||||||
|
|
||||||
@ -382,19 +383,25 @@ void VMdEditor::contextMenuEvent(QContextMenuEvent *p_event)
|
|||||||
emit m_object->discardAndRead();
|
emit m_object->discardAndRead();
|
||||||
});
|
});
|
||||||
|
|
||||||
QAction *toggleLivePreviewAct = new QAction(tr("Live Preview For Graphs"), menu.data());
|
VMdTab *mdtab = dynamic_cast<VMdTab *>(m_editTab);
|
||||||
toggleLivePreviewAct->setToolTip(tr("Toggle live preview panel for graphs"));
|
if (mdtab) {
|
||||||
VUtils::fixTextWithCaptainShortcut(toggleLivePreviewAct, "LivePreview");
|
QAction *toggleLivePreviewAct = new QAction(tr("Live Preview For Graphs"), menu.data());
|
||||||
connect(toggleLivePreviewAct, &QAction::triggered,
|
toggleLivePreviewAct->setToolTip(tr("Toggle live preview panel for graphs"));
|
||||||
this, [this]() {
|
VUtils::fixTextWithCaptainShortcut(toggleLivePreviewAct, "LivePreview");
|
||||||
m_editTab->toggleLivePreview();
|
connect(toggleLivePreviewAct, &QAction::triggered,
|
||||||
});
|
this, [this, mdtab]() {
|
||||||
|
mdtab->toggleLivePreview();
|
||||||
|
});
|
||||||
|
|
||||||
menu->insertAction(firstAct, toggleLivePreviewAct);
|
menu->insertAction(firstAct, toggleLivePreviewAct);
|
||||||
menu->insertAction(toggleLivePreviewAct, discardExitAct);
|
menu->insertAction(toggleLivePreviewAct, discardExitAct);
|
||||||
menu->insertAction(discardExitAct, saveExitAct);
|
menu->insertAction(discardExitAct, saveExitAct);
|
||||||
|
menu->insertSeparator(toggleLivePreviewAct);
|
||||||
menu->insertSeparator(toggleLivePreviewAct);
|
} else {
|
||||||
|
menu->insertAction(firstAct, discardExitAct);
|
||||||
|
menu->insertAction(discardExitAct, saveExitAct);
|
||||||
|
menu->insertSeparator(discardExitAct);
|
||||||
|
}
|
||||||
|
|
||||||
if (firstAct) {
|
if (firstAct) {
|
||||||
menu->insertSeparator(firstAct);
|
menu->insertSeparator(firstAct);
|
||||||
@ -1389,18 +1396,10 @@ QAction *VMdEditor::initPasteAsBlockQuoteMenu(QAction *p_after, QMenu *p_menu)
|
|||||||
QAction *VMdEditor::initPasteAfterParseMenu(QAction *p_after, QMenu *p_menu)
|
QAction *VMdEditor::initPasteAfterParseMenu(QAction *p_after, QMenu *p_menu)
|
||||||
{
|
{
|
||||||
QAction *papAct = new QAction(tr("Paste Parsed &Markdown Text"), p_menu);
|
QAction *papAct = new QAction(tr("Paste Parsed &Markdown Text"), p_menu);
|
||||||
|
VUtils::fixTextWithCaptainShortcut(papAct, "ParseAndPaste");
|
||||||
papAct->setToolTip(tr("Parse HTML to Markdown text and paste"));
|
papAct->setToolTip(tr("Parse HTML to Markdown text and paste"));
|
||||||
connect(papAct, &QAction::triggered,
|
connect(papAct, &QAction::triggered,
|
||||||
this, [this]() {
|
this, &VMdEditor::parseAndPaste);
|
||||||
QClipboard *clipboard = QApplication::clipboard();
|
|
||||||
const QMimeData *mimeData = clipboard->mimeData();
|
|
||||||
QString html(mimeData->html());
|
|
||||||
if (!html.isEmpty()) {
|
|
||||||
++m_copyTimeStamp;
|
|
||||||
emit requestHtmlToText(html, 0, m_copyTimeStamp);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
insertActionAfter(p_after, papAct, p_menu);
|
insertActionAfter(p_after, papAct, p_menu);
|
||||||
return papAct;
|
return papAct;
|
||||||
@ -1824,3 +1823,20 @@ void VMdEditor::exportGraphAndCopy(const QString &p_lang,
|
|||||||
|
|
||||||
m_exportTempFile->close();
|
m_exportTempFile->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VMdEditor::parseAndPaste()
|
||||||
|
{
|
||||||
|
if (!m_editTab
|
||||||
|
|| !m_editTab->isEditMode()
|
||||||
|
|| isReadOnly()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QClipboard *clipboard = QApplication::clipboard();
|
||||||
|
const QMimeData *mimeData = clipboard->mimeData();
|
||||||
|
QString html(mimeData->html());
|
||||||
|
if (!html.isEmpty()) {
|
||||||
|
++m_copyTimeStamp;
|
||||||
|
emit requestHtmlToText(html, 0, m_copyTimeStamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -88,6 +88,8 @@ public slots:
|
|||||||
|
|
||||||
void htmlToTextFinished(int p_id, int p_timeStamp, const QString &p_html);
|
void htmlToTextFinished(int p_id, int p_timeStamp, const QString &p_html);
|
||||||
|
|
||||||
|
void parseAndPaste();
|
||||||
|
|
||||||
// Wrapper functions for QPlainTextEdit/QTextEdit.
|
// Wrapper functions for QPlainTextEdit/QTextEdit.
|
||||||
public:
|
public:
|
||||||
void setExtraSelectionsW(const QList<QTextEdit::ExtraSelection> &p_selections) Q_DECL_OVERRIDE
|
void setExtraSelectionsW(const QList<QTextEdit::ExtraSelection> &p_selections) Q_DECL_OVERRIDE
|
||||||
|
@ -65,6 +65,26 @@ VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
|
|||||||
writeBackupFile();
|
writeBackupFile();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_livePreviewTimer = new QTimer(this);
|
||||||
|
m_livePreviewTimer->setSingleShot(true);
|
||||||
|
m_livePreviewTimer->setInterval(500);
|
||||||
|
connect(m_livePreviewTimer, &QTimer::timeout,
|
||||||
|
this, [this]() {
|
||||||
|
QString text = m_webViewer->selectedText();
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LivePreviewInfo &info = m_livePreviewHelper->getLivePreviewInfo();
|
||||||
|
if (info.isValid()) {
|
||||||
|
m_editor->findTextInRange(text,
|
||||||
|
FindOption::CaseSensitive,
|
||||||
|
true,
|
||||||
|
info.m_startPos,
|
||||||
|
info.m_endPos);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (p_mode == OpenFileMode::Edit) {
|
if (p_mode == OpenFileMode::Edit) {
|
||||||
showFileEditMode();
|
showFileEditMode();
|
||||||
} else {
|
} else {
|
||||||
@ -396,6 +416,10 @@ void VMdTab::setupMarkdownViewer()
|
|||||||
this, &VMdTab::editFile);
|
this, &VMdTab::editFile);
|
||||||
connect(m_webViewer, &VWebView::requestSavePage,
|
connect(m_webViewer, &VWebView::requestSavePage,
|
||||||
this, &VMdTab::handleSavePageRequested);
|
this, &VMdTab::handleSavePageRequested);
|
||||||
|
connect(m_webViewer, &VWebView::selectionChanged,
|
||||||
|
this, &VMdTab::handleWebSelectionChanged);
|
||||||
|
connect(m_webViewer, &VWebView::requestExpandRestorePreviewArea,
|
||||||
|
this, &VMdTab::expandRestorePreviewArea);
|
||||||
|
|
||||||
VPreviewPage *page = new VPreviewPage(m_webViewer);
|
VPreviewPage *page = new VPreviewPage(m_webViewer);
|
||||||
m_webViewer->setPage(page);
|
m_webViewer->setPage(page);
|
||||||
@ -1516,18 +1540,51 @@ void VMdTab::setCurrentMode(Mode p_mode)
|
|||||||
focusChild();
|
focusChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdTab::toggleLivePreview()
|
bool VMdTab::toggleLivePreview()
|
||||||
{
|
{
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
switch (m_mode) {
|
switch (m_mode) {
|
||||||
case Mode::EditPreview:
|
case Mode::EditPreview:
|
||||||
setCurrentMode(Mode::Edit);
|
setCurrentMode(Mode::Edit);
|
||||||
|
ret = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Mode::Edit:
|
case Mode::Edit:
|
||||||
setCurrentMode(Mode::EditPreview);
|
setCurrentMode(Mode::EditPreview);
|
||||||
|
ret = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdTab::handleWebSelectionChanged()
|
||||||
|
{
|
||||||
|
if (m_mode != Mode::EditPreview
|
||||||
|
|| !(g_config->getSmartLivePreview() & SmartLivePreview::WebToEditor)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_livePreviewTimer->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMdTab::expandRestorePreviewArea()
|
||||||
|
{
|
||||||
|
if (m_mode != Mode::EditPreview) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_editor->isVisible()) {
|
||||||
|
m_editor->hide();
|
||||||
|
m_webViewer->setFocus();
|
||||||
|
} else {
|
||||||
|
m_editor->show();
|
||||||
|
m_editor->setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,9 @@ public:
|
|||||||
VWordCountInfo fetchWordCountInfo(bool p_editMode) const Q_DECL_OVERRIDE;
|
VWordCountInfo fetchWordCountInfo(bool p_editMode) const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
// Toggle live preview in edit mode.
|
// Toggle live preview in edit mode.
|
||||||
void toggleLivePreview() Q_DECL_OVERRIDE;
|
bool toggleLivePreview();
|
||||||
|
|
||||||
|
bool expandRestorePreviewArea();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
// Enter edit mode.
|
// Enter edit mode.
|
||||||
@ -148,6 +150,9 @@ private slots:
|
|||||||
// Handle save page request.
|
// Handle save page request.
|
||||||
void handleSavePageRequested();
|
void handleSavePageRequested();
|
||||||
|
|
||||||
|
// Selection changed in web.
|
||||||
|
void handleWebSelectionChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum TabReady { None = 0, ReadMode = 0x1, EditMode = 0x2 };
|
enum TabReady { None = 0, ReadMode = 0x1, EditMode = 0x2 };
|
||||||
|
|
||||||
@ -243,6 +248,8 @@ private:
|
|||||||
// Timer to write backup file when content has been changed.
|
// Timer to write backup file when content has been changed.
|
||||||
QTimer *m_backupTimer;
|
QTimer *m_backupTimer;
|
||||||
|
|
||||||
|
QTimer *m_livePreviewTimer;
|
||||||
|
|
||||||
bool m_backupFileChecked;
|
bool m_backupFileChecked;
|
||||||
|
|
||||||
// Used to scroll to the header of edit mode in read mode.
|
// Used to scroll to the header of edit mode in read mode.
|
||||||
|
108
src/vwebview.cpp
108
src/vwebview.cpp
@ -45,6 +45,9 @@ void VWebView::contextMenuEvent(QContextMenuEvent *p_event)
|
|||||||
menu->setToolTipsVisible(true);
|
menu->setToolTipsVisible(true);
|
||||||
|
|
||||||
const QList<QAction *> actions = menu->actions();
|
const QList<QAction *> actions = menu->actions();
|
||||||
|
QAction *firstAction = actions.isEmpty() ? NULL : actions[0];
|
||||||
|
|
||||||
|
bool selection = hasSelection();
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
if (!m_copyImageUrlActionHooked) {
|
if (!m_copyImageUrlActionHooked) {
|
||||||
@ -64,26 +67,46 @@ void VWebView::contextMenuEvent(QContextMenuEvent *p_event)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!hasSelection()
|
if (!selection) {
|
||||||
&& !m_inPreview
|
if (m_inPreview) {
|
||||||
&& m_file
|
QAction *expandAct = new QAction(tr("Expand/Restore Preview Area"), menu);
|
||||||
&& m_file->isModifiable()) {
|
VUtils::fixTextWithCaptainShortcut(expandAct, "ExpandLivePreview");
|
||||||
QAction *editAct= new QAction(VIconUtils::menuIcon(":/resources/icons/edit_note.svg"),
|
connect(expandAct, &QAction::triggered,
|
||||||
tr("&Edit"), menu);
|
this, &VWebView::requestExpandRestorePreviewArea);
|
||||||
editAct->setToolTip(tr("Edit current note"));
|
menu->insertAction(firstAction, expandAct);
|
||||||
connect(editAct, &QAction::triggered,
|
|
||||||
this, &VWebView::handleEditAction);
|
initPreviewTunnelMenu(firstAction, menu);
|
||||||
menu->insertAction(actions.isEmpty() ? NULL : actions[0], editAct);
|
|
||||||
// actions does not contain editAction.
|
if (firstAction) {
|
||||||
if (!actions.isEmpty()) {
|
menu->insertSeparator(firstAction);
|
||||||
menu->insertSeparator(actions[0]);
|
}
|
||||||
|
} else {
|
||||||
|
if (m_file && m_file->isModifiable()) {
|
||||||
|
QAction *editAct= new QAction(VIconUtils::menuIcon(":/resources/icons/edit_note.svg"),
|
||||||
|
tr("&Edit"),
|
||||||
|
menu);
|
||||||
|
editAct->setToolTip(tr("Edit current note"));
|
||||||
|
connect(editAct, &QAction::triggered,
|
||||||
|
this, &VWebView::handleEditAction);
|
||||||
|
menu->insertAction(firstAction, editAct);
|
||||||
|
if (firstAction) {
|
||||||
|
menu->insertSeparator(firstAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QAction *savePageAct = new QAction(QWebEnginePage::tr("Save &Page"), menu);
|
||||||
|
connect(savePageAct, &QAction::triggered,
|
||||||
|
this, &VWebView::requestSavePage);
|
||||||
|
menu->addAction(savePageAct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Copy As menu.
|
// Add Copy As menu.
|
||||||
QAction *copyAct = pageAction(QWebEnginePage::Copy);
|
{
|
||||||
if (actions.contains(copyAct) && !m_inPreview) {
|
QAction *copyAct = pageAction(QWebEnginePage::Copy);
|
||||||
initCopyAsMenu(copyAct, menu);
|
if (actions.contains(copyAct) && !m_inPreview) {
|
||||||
|
initCopyAsMenu(copyAct, menu);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to replace the "Copy Image" action:
|
// We need to replace the "Copy Image" action:
|
||||||
@ -100,13 +123,6 @@ void VWebView::contextMenuEvent(QContextMenuEvent *p_event)
|
|||||||
defaultCopyImageAct->setVisible(false);
|
defaultCopyImageAct->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasSelection() && !m_inPreview) {
|
|
||||||
QAction *savePageAct = new QAction(QWebEnginePage::tr("Save &Page"), menu);
|
|
||||||
connect(savePageAct, &QAction::triggered,
|
|
||||||
this, &VWebView::requestSavePage);
|
|
||||||
menu->addAction(savePageAct);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add Copy All As menu.
|
// Add Copy All As menu.
|
||||||
if (!m_inPreview) {
|
if (!m_inPreview) {
|
||||||
initCopyAllAsMenu(menu);
|
initCopyAllAsMenu(menu);
|
||||||
@ -419,3 +435,49 @@ void VWebView::handleCopyAllAsAction(QAction *p_act)
|
|||||||
|
|
||||||
triggerPageAction(QWebEnginePage::Unselect);
|
triggerPageAction(QWebEnginePage::Unselect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VWebView::initPreviewTunnelMenu(QAction *p_before, QMenu *p_menu)
|
||||||
|
{
|
||||||
|
QMenu *subMenu = new QMenu(tr("Live Preview Tunnel"), p_menu);
|
||||||
|
|
||||||
|
int config = g_config->getSmartLivePreview();
|
||||||
|
|
||||||
|
QActionGroup *ag = new QActionGroup(subMenu);
|
||||||
|
QAction *act = ag->addAction(tr("Disabled"));
|
||||||
|
act->setData(SmartLivePreview::Disabled);
|
||||||
|
act->setCheckable(true);
|
||||||
|
if (act->data().toInt() == config) {
|
||||||
|
act->setChecked(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
act = ag->addAction(tr("Editor -> Preview"));
|
||||||
|
act->setData(SmartLivePreview::EditorToWeb);
|
||||||
|
act->setCheckable(true);
|
||||||
|
if (act->data().toInt() == config) {
|
||||||
|
act->setChecked(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
act = ag->addAction(tr("Preview -> Editor"));
|
||||||
|
act->setData(SmartLivePreview::WebToEditor);
|
||||||
|
act->setCheckable(true);
|
||||||
|
if (act->data().toInt() == config) {
|
||||||
|
act->setChecked(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
act = ag->addAction(tr("Bidirectional"));
|
||||||
|
act->setData(SmartLivePreview::EditorToWeb | SmartLivePreview::WebToEditor);
|
||||||
|
act->setCheckable(true);
|
||||||
|
if (act->data().toInt() == config) {
|
||||||
|
act->setChecked(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(ag, &QActionGroup::triggered,
|
||||||
|
this, [this](QAction *p_act) {
|
||||||
|
int data = p_act->data().toInt();
|
||||||
|
g_config->setSmartLivePreview(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
subMenu->addActions(ag->actions());
|
||||||
|
|
||||||
|
p_menu->insertMenu(p_before, subMenu);
|
||||||
|
}
|
||||||
|
@ -22,6 +22,8 @@ signals:
|
|||||||
|
|
||||||
void requestSavePage();
|
void requestSavePage();
|
||||||
|
|
||||||
|
void requestExpandRestorePreviewArea();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void contextMenuEvent(QContextMenuEvent *p_event);
|
void contextMenuEvent(QContextMenuEvent *p_event);
|
||||||
|
|
||||||
@ -56,6 +58,8 @@ private:
|
|||||||
|
|
||||||
void initCopyAllAsMenu(QMenu *p_menu);
|
void initCopyAllAsMenu(QMenu *p_menu);
|
||||||
|
|
||||||
|
void initPreviewTunnelMenu(QAction *p_before, QMenu *p_menu);
|
||||||
|
|
||||||
VFile *m_file;
|
VFile *m_file;
|
||||||
|
|
||||||
// Whether this view has hooked the Copy Image Url action.
|
// Whether this view has hooked the Copy Image Url action.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user