mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
VMdTab: live preview
This commit is contained in:
parent
051088be31
commit
c6b7561864
@ -892,8 +892,8 @@ int VExportDialog::doExport(VCart *p_cart,
|
|||||||
QString *p_errMsg,
|
QString *p_errMsg,
|
||||||
QList<QString> *p_outputFiles)
|
QList<QString> *p_outputFiles)
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(p_cart);
|
||||||
Q_ASSERT(p_cart);
|
Q_ASSERT(p_cart);
|
||||||
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
QVector<QString> files = m_cart->getFiles();
|
QVector<QString> files = m_cart->getFiles();
|
||||||
|
@ -46,12 +46,27 @@ struct HLUnitStyle
|
|||||||
// Fenced code block only.
|
// Fenced code block only.
|
||||||
struct VCodeBlock
|
struct VCodeBlock
|
||||||
{
|
{
|
||||||
|
// Global position of the start.
|
||||||
int m_startPos;
|
int m_startPos;
|
||||||
|
|
||||||
int m_startBlock;
|
int m_startBlock;
|
||||||
int m_endBlock;
|
int m_endBlock;
|
||||||
|
|
||||||
QString m_lang;
|
QString m_lang;
|
||||||
|
|
||||||
QString m_text;
|
QString m_text;
|
||||||
|
|
||||||
|
bool equalContent(const VCodeBlock &p_block) const
|
||||||
|
{
|
||||||
|
return p_block.m_lang == m_lang && p_block.m_text == m_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateNonContent(const VCodeBlock &p_block)
|
||||||
|
{
|
||||||
|
m_startPos = p_block.m_startPos;
|
||||||
|
m_startBlock = p_block.m_startBlock;
|
||||||
|
m_endBlock = p_block.m_endBlock;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Highlight unit with global position and string style name.
|
// Highlight unit with global position and string style name.
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
var placeholder = document.getElementById('placeholder');
|
|
||||||
|
|
||||||
// Use Marked to highlight code blocks in edit mode.
|
// Use Marked to highlight code blocks in edit mode.
|
||||||
marked.setOptions({
|
marked.setOptions({
|
||||||
highlight: function(code, lang) {
|
highlight: function(code, lang) {
|
||||||
@ -18,7 +16,7 @@ marked.setOptions({
|
|||||||
var updateHtml = function(html) {
|
var updateHtml = function(html) {
|
||||||
asyncJobsCount = 0;
|
asyncJobsCount = 0;
|
||||||
|
|
||||||
placeholder.innerHTML = html;
|
contentDiv.innerHTML = html;
|
||||||
|
|
||||||
insertImageCaption();
|
insertImageCaption();
|
||||||
|
|
||||||
@ -83,7 +81,7 @@ var updateHtml = function(html) {
|
|||||||
// MathJax may be not loaded for now.
|
// MathJax may be not loaded for now.
|
||||||
if (VEnableMathjax && (typeof MathJax != "undefined")) {
|
if (VEnableMathjax && (typeof MathJax != "undefined")) {
|
||||||
try {
|
try {
|
||||||
MathJax.Hub.Queue(["Typeset", MathJax.Hub, placeholder, postProcessMathJax]);
|
MathJax.Hub.Queue(["Typeset", MathJax.Hub, contentDiv, postProcessMathJax]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
content.setLog("err: " + err);
|
content.setLog("err: " + err);
|
||||||
finishLogics();
|
finishLogics();
|
||||||
@ -100,7 +98,7 @@ var highlightText = function(text, id, timeStamp) {
|
|||||||
|
|
||||||
var textToHtml = function(text) {
|
var textToHtml = function(text) {
|
||||||
var html = marked(text);
|
var html = marked(text);
|
||||||
var container = document.getElementById('text-to-html-div');
|
var container = textHtmlDiv;
|
||||||
container.innerHTML = html;
|
container.innerHTML = html;
|
||||||
|
|
||||||
html = getHtmlWithInlineStyles(container);
|
html = getHtmlWithInlineStyles(container);
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
var placeholder = document.getElementById('placeholder');
|
|
||||||
var nameCounter = 0;
|
var nameCounter = 0;
|
||||||
var toc = []; // Table of Content as a list
|
var toc = []; // Table of Content as a list
|
||||||
|
|
||||||
@ -110,7 +109,7 @@ var updateText = function(text) {
|
|||||||
|
|
||||||
var needToc = mdHasTocSection(text);
|
var needToc = mdHasTocSection(text);
|
||||||
var html = markdownToHtml(text, needToc);
|
var html = markdownToHtml(text, needToc);
|
||||||
placeholder.innerHTML = html;
|
contentDiv.innerHTML = html;
|
||||||
handleToc(needToc);
|
handleToc(needToc);
|
||||||
insertImageCaption();
|
insertImageCaption();
|
||||||
renderMermaid('lang-mermaid');
|
renderMermaid('lang-mermaid');
|
||||||
@ -124,7 +123,7 @@ var updateText = function(text) {
|
|||||||
// finishLoading logic.
|
// finishLoading logic.
|
||||||
if (VEnableMathjax) {
|
if (VEnableMathjax) {
|
||||||
try {
|
try {
|
||||||
MathJax.Hub.Queue(["Typeset", MathJax.Hub, placeholder, postProcessMathJax]);
|
MathJax.Hub.Queue(["Typeset", MathJax.Hub, contentDiv, postProcessMathJax]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
content.setLog("err: " + err);
|
content.setLog("err: " + err);
|
||||||
finishLogics();
|
finishLogics();
|
||||||
@ -141,7 +140,7 @@ var highlightText = function(text, id, timeStamp) {
|
|||||||
|
|
||||||
var textToHtml = function(text) {
|
var textToHtml = function(text) {
|
||||||
var html = mdit.render(text);
|
var html = mdit.render(text);
|
||||||
var container = document.getElementById('text-to-html-div');
|
var container = textHtmlDiv;
|
||||||
container.innerHTML = html;
|
container.innerHTML = html;
|
||||||
|
|
||||||
html = getHtmlWithInlineStyles(container);
|
html = getHtmlWithInlineStyles(container);
|
||||||
|
@ -30,8 +30,10 @@
|
|||||||
<script src="qrc:/resources/markdown_template.js" defer></script>
|
<script src="qrc:/resources/markdown_template.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="placeholder"></div>
|
<div id="content-div"></div>
|
||||||
|
|
||||||
<div id="text-to-html-div" style="display:none;"></div>
|
<div id="preview-div" style="display:none;"></div>
|
||||||
|
|
||||||
|
<div id="text-html-div" style="display:none;"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
var channelInitialized = false;
|
var channelInitialized = false;
|
||||||
|
|
||||||
|
var contentDiv = document.getElementById('content-div');
|
||||||
|
|
||||||
|
var previewDiv = document.getElementById('preview-div');
|
||||||
|
|
||||||
|
var textHtmlDiv = document.getElementById('text-html-div');
|
||||||
|
|
||||||
var content;
|
var content;
|
||||||
|
|
||||||
// Current header index in all headers.
|
// Current header index in all headers.
|
||||||
@ -131,7 +137,7 @@ var styleContent = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var htmlContent = function() {
|
var htmlContent = function() {
|
||||||
content.htmlContentCB("", styleContent(), placeholder.innerHTML);
|
content.htmlContentCB("", styleContent(), contentDiv.innerHTML);
|
||||||
};
|
};
|
||||||
|
|
||||||
new QWebChannel(qt.webChannelTransport,
|
new QWebChannel(qt.webChannelTransport,
|
||||||
@ -157,6 +163,11 @@ new QWebChannel(qt.webChannelTransport,
|
|||||||
content.plantUMLResultReady.connect(handlePlantUMLResult);
|
content.plantUMLResultReady.connect(handlePlantUMLResult);
|
||||||
content.graphvizResultReady.connect(handleGraphvizResult);
|
content.graphvizResultReady.connect(handleGraphvizResult);
|
||||||
|
|
||||||
|
content.requestPreviewEnabled.connect(setPreviewEnabled);
|
||||||
|
|
||||||
|
content.requestPreviewCodeBlock.connect(previewCodeBlock);
|
||||||
|
content.requestSetPreviewContent.connect(setPreviewContent);
|
||||||
|
|
||||||
if (typeof updateHtml == "function") {
|
if (typeof updateHtml == "function") {
|
||||||
updateHtml(content.html);
|
updateHtml(content.html);
|
||||||
content.htmlChanged.connect(updateHtml);
|
content.htmlChanged.connect(updateHtml);
|
||||||
@ -980,7 +991,7 @@ window.onmousedown = function(e) {
|
|||||||
// Left button and Ctrl key.
|
// Left button and Ctrl key.
|
||||||
if (e.buttons == 1
|
if (e.buttons == 1
|
||||||
&& e.ctrlKey
|
&& e.ctrlKey
|
||||||
&& window.getSelection().rangeCount == 0) {
|
&& window.getSelection().type != 'Range') {
|
||||||
vds_oriMouseClientX = e.clientX;
|
vds_oriMouseClientX = e.clientX;
|
||||||
vds_oriMouseClientY = e.clientY;
|
vds_oriMouseClientY = e.clientY;
|
||||||
vds_readyToScroll = true;
|
vds_readyToScroll = true;
|
||||||
@ -1267,7 +1278,7 @@ function getNodeText(el) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var calculateWordCount = function() {
|
var calculateWordCount = function() {
|
||||||
var words = getNodeText(placeholder);
|
var words = getNodeText(contentDiv);
|
||||||
|
|
||||||
// Char without spaces.
|
// Char without spaces.
|
||||||
var cns = 0;
|
var cns = 0;
|
||||||
@ -1349,3 +1360,49 @@ var handleGraphvizResult = function(id, format, result) {
|
|||||||
|
|
||||||
finishOneAsyncJob();
|
finishOneAsyncJob();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var setPreviewEnabled = function(enabled) {
|
||||||
|
if (enabled) {
|
||||||
|
contentDiv.style.display = 'none';
|
||||||
|
previewDiv.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
contentDiv.style.display = 'block';
|
||||||
|
previewDiv.style.display = 'none';
|
||||||
|
previewDiv.innerHTML = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var previewCodeBlock = function(id, lang, text, isLivePreview) {
|
||||||
|
var div = previewDiv;
|
||||||
|
div.innerHTML = '';
|
||||||
|
div.className = '';
|
||||||
|
|
||||||
|
if (text.length == 0
|
||||||
|
|| (lang != 'flow'
|
||||||
|
&& lang != 'flowchart'
|
||||||
|
&& lang != 'mermaid'
|
||||||
|
&& (lang != 'puml' || VPlantUMLMode != 1))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pre = document.createElement('pre');
|
||||||
|
var code = document.createElement('code');
|
||||||
|
code.textContent = text;
|
||||||
|
|
||||||
|
pre.appendChild(code);
|
||||||
|
div.appendChild(pre);
|
||||||
|
|
||||||
|
if (lang == 'flow' || lang == 'flowchart') {
|
||||||
|
renderFlowchartOne(code);
|
||||||
|
} else if (lang == 'mermaid') {
|
||||||
|
renderMermaidOne(code);
|
||||||
|
} else if (lang == 'puml') {
|
||||||
|
renderPlantUMLOneOnline(code);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var setPreviewContent = function(lang, html) {
|
||||||
|
previewDiv.innerHTML = html;
|
||||||
|
// Treat plantUML and graphviz the same.
|
||||||
|
previewDiv.classList = VPlantUMLDivClass;
|
||||||
|
};
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
var placeholder = document.getElementById('placeholder');
|
|
||||||
var renderer = new marked.Renderer();
|
var renderer = new marked.Renderer();
|
||||||
var toc = []; // Table of contents as a list
|
var toc = []; // Table of contents as a list
|
||||||
var nameCounter = 0;
|
var nameCounter = 0;
|
||||||
@ -54,7 +53,7 @@ var updateText = function(text) {
|
|||||||
|
|
||||||
var needToc = mdHasTocSection(text);
|
var needToc = mdHasTocSection(text);
|
||||||
var html = markdownToHtml(text, needToc);
|
var html = markdownToHtml(text, needToc);
|
||||||
placeholder.innerHTML = html;
|
contentDiv.innerHTML = html;
|
||||||
handleToc(needToc);
|
handleToc(needToc);
|
||||||
insertImageCaption();
|
insertImageCaption();
|
||||||
renderMermaid('lang-mermaid');
|
renderMermaid('lang-mermaid');
|
||||||
@ -68,7 +67,7 @@ var updateText = function(text) {
|
|||||||
// finishLoading logic.
|
// finishLoading logic.
|
||||||
if (VEnableMathjax) {
|
if (VEnableMathjax) {
|
||||||
try {
|
try {
|
||||||
MathJax.Hub.Queue(["Typeset", MathJax.Hub, placeholder, postProcessMathJax]);
|
MathJax.Hub.Queue(["Typeset", MathJax.Hub, contentDiv, postProcessMathJax]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
content.setLog("err: " + err);
|
content.setLog("err: " + err);
|
||||||
finishLogics();
|
finishLogics();
|
||||||
@ -85,7 +84,7 @@ var highlightText = function(text, id, timeStamp) {
|
|||||||
|
|
||||||
var textToHtml = function(text) {
|
var textToHtml = function(text) {
|
||||||
var html = marked(text);
|
var html = marked(text);
|
||||||
var container = document.getElementById('text-to-html-div');
|
var container = textHtmlDiv;
|
||||||
container.innerHTML = html;
|
container.innerHTML = html;
|
||||||
|
|
||||||
html = getHtmlWithInlineStyles(container);
|
html = getHtmlWithInlineStyles(container);
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
var placeholder = document.getElementById('placeholder');
|
|
||||||
var renderer = new showdown.Converter({simplifiedAutoLink: 'true',
|
var renderer = new showdown.Converter({simplifiedAutoLink: 'true',
|
||||||
excludeTrailingPunctuationFromURLs: 'true',
|
excludeTrailingPunctuationFromURLs: 'true',
|
||||||
strikethrough: 'true',
|
strikethrough: 'true',
|
||||||
@ -94,7 +93,7 @@ var updateText = function(text) {
|
|||||||
|
|
||||||
var needToc = mdHasTocSection(text);
|
var needToc = mdHasTocSection(text);
|
||||||
var html = markdownToHtml(text, needToc);
|
var html = markdownToHtml(text, needToc);
|
||||||
placeholder.innerHTML = html;
|
contentDiv.innerHTML = html;
|
||||||
handleToc(needToc);
|
handleToc(needToc);
|
||||||
insertImageCaption();
|
insertImageCaption();
|
||||||
highlightCodeBlocks(document,
|
highlightCodeBlocks(document,
|
||||||
@ -114,7 +113,7 @@ var updateText = function(text) {
|
|||||||
// finishLoading logic.
|
// finishLoading logic.
|
||||||
if (VEnableMathjax) {
|
if (VEnableMathjax) {
|
||||||
try {
|
try {
|
||||||
MathJax.Hub.Queue(["Typeset", MathJax.Hub, placeholder, postProcessMathJax]);
|
MathJax.Hub.Queue(["Typeset", MathJax.Hub, contentDiv, postProcessMathJax]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
content.setLog("err: " + err);
|
content.setLog("err: " + err);
|
||||||
finishLogics();
|
finishLogics();
|
||||||
@ -149,7 +148,7 @@ var textToHtml = function(text) {
|
|||||||
|
|
||||||
delete parser;
|
delete parser;
|
||||||
|
|
||||||
var container = document.getElementById('text-to-html-div');
|
var container = textHtmlDiv;
|
||||||
container.innerHTML = html;
|
container.innerHTML = html;
|
||||||
|
|
||||||
html = getHtmlWithInlineStyles(container);
|
html = getHtmlWithInlineStyles(container);
|
||||||
|
@ -307,3 +307,7 @@ table.hljs-ln tr td.hljs-ln-code {
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: #64B5F6;
|
||||||
|
}
|
||||||
|
@ -237,3 +237,7 @@ table.hljs-ln tr td.hljs-ln-numbers {
|
|||||||
table.hljs-ln tr td.hljs-ln-code {
|
table.hljs-ln tr td.hljs-ln-code {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: #64B5F6;
|
||||||
|
}
|
||||||
|
@ -307,3 +307,7 @@ table.hljs-ln tr td.hljs-ln-code {
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background: #64B5F6;
|
||||||
|
}
|
||||||
|
@ -387,6 +387,8 @@ MagicWord=M
|
|||||||
ApplySnippet=S
|
ApplySnippet=S
|
||||||
; Open export dialog
|
; Open export dialog
|
||||||
Export=O
|
Export=O
|
||||||
|
; Toggle live preview
|
||||||
|
LivePreview=I
|
||||||
|
|
||||||
[external_editors]
|
[external_editors]
|
||||||
; Define external editors which could be called to edit notes
|
; Define external editors which could be called to edit notes
|
||||||
|
@ -127,7 +127,8 @@ SOURCES += main.cpp\
|
|||||||
vlistfolderue.cpp \
|
vlistfolderue.cpp \
|
||||||
dialog/vfixnotebookdialog.cpp \
|
dialog/vfixnotebookdialog.cpp \
|
||||||
vplantumlhelper.cpp \
|
vplantumlhelper.cpp \
|
||||||
vgraphvizhelper.cpp
|
vgraphvizhelper.cpp \
|
||||||
|
vlivepreviewhelper.cpp
|
||||||
|
|
||||||
HEADERS += vmainwindow.h \
|
HEADERS += vmainwindow.h \
|
||||||
vdirectorytree.h \
|
vdirectorytree.h \
|
||||||
@ -245,7 +246,8 @@ HEADERS += vmainwindow.h \
|
|||||||
vlistfolderue.h \
|
vlistfolderue.h \
|
||||||
dialog/vfixnotebookdialog.h \
|
dialog/vfixnotebookdialog.h \
|
||||||
vplantumlhelper.h \
|
vplantumlhelper.h \
|
||||||
vgraphvizhelper.h
|
vgraphvizhelper.h \
|
||||||
|
vlivepreviewhelper.h
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
vnote.qrc \
|
vnote.qrc \
|
||||||
|
@ -160,3 +160,21 @@ void VDocument::processGraphviz(int p_id, const QString &p_format, const QString
|
|||||||
|
|
||||||
m_graphvizHelper->processAsync(p_id, p_format, p_text);
|
m_graphvizHelper->processAsync(p_id, p_format, p_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VDocument::setPreviewEnabled(bool p_enabled)
|
||||||
|
{
|
||||||
|
emit requestPreviewEnabled(p_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VDocument::previewCodeBlock(int p_id,
|
||||||
|
const QString &p_lang,
|
||||||
|
const QString &p_text,
|
||||||
|
bool p_livePreview)
|
||||||
|
{
|
||||||
|
emit requestPreviewCodeBlock(p_id, p_lang, p_text, p_livePreview);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VDocument::setPreviewContent(const QString &p_lang, const QString &p_html)
|
||||||
|
{
|
||||||
|
emit requestSetPreviewContent(p_lang, p_html);
|
||||||
|
}
|
||||||
|
@ -47,6 +47,20 @@ public:
|
|||||||
|
|
||||||
const VWordCountInfo &getWordCountInfo() const;
|
const VWordCountInfo &getWordCountInfo() const;
|
||||||
|
|
||||||
|
// Whether change to preview mode.
|
||||||
|
void setPreviewEnabled(bool p_enabled);
|
||||||
|
|
||||||
|
// @p_livePreview: if true, display the result in the preview-div; otherwise,
|
||||||
|
// call previewCodeBlockCB() to pass back the result.
|
||||||
|
// Only for online parser.
|
||||||
|
void previewCodeBlock(int p_id,
|
||||||
|
const QString &p_lang,
|
||||||
|
const QString &p_text,
|
||||||
|
bool p_livePreview);
|
||||||
|
|
||||||
|
// Set the content of the preview.
|
||||||
|
void setPreviewContent(const QString &p_lang, const QString &p_html);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
// Will be called in the HTML side
|
// Will be called in the HTML side
|
||||||
|
|
||||||
@ -130,6 +144,15 @@ signals:
|
|||||||
|
|
||||||
void graphvizResultReady(int p_id, const QString &p_format, const QString &p_result);
|
void graphvizResultReady(int p_id, const QString &p_format, const QString &p_result);
|
||||||
|
|
||||||
|
void requestPreviewEnabled(bool p_enabled);
|
||||||
|
|
||||||
|
void requestPreviewCodeBlock(int p_id,
|
||||||
|
const QString &p_lang,
|
||||||
|
const QString &p_text,
|
||||||
|
bool p_livePreview);
|
||||||
|
|
||||||
|
void requestSetPreviewContent(const QString &p_lang, const QString &p_html);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_toc;
|
QString m_toc;
|
||||||
QString m_header;
|
QString m_header;
|
||||||
|
@ -923,6 +923,10 @@ void VEditArea::registerCaptainTargets()
|
|||||||
g_config->getCaptainShortcutKeySequence("ApplySnippet"),
|
g_config->getCaptainShortcutKeySequence("ApplySnippet"),
|
||||||
this,
|
this,
|
||||||
applySnippetByCaptain);
|
applySnippetByCaptain);
|
||||||
|
captain->registerCaptainTarget(tr("LivePreview"),
|
||||||
|
g_config->getCaptainShortcutKeySequence("LivePreview"),
|
||||||
|
this,
|
||||||
|
toggleLivePreviewByCaptain);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VEditArea::activateTabByCaptain(void *p_target, void *p_data, int p_idx)
|
bool VEditArea::activateTabByCaptain(void *p_target, void *p_data, int p_idx)
|
||||||
@ -1085,6 +1089,19 @@ bool VEditArea::applySnippetByCaptain(void *p_target, void *p_data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VEditArea::toggleLivePreviewByCaptain(void *p_target, void *p_data)
|
||||||
|
{
|
||||||
|
Q_UNUSED(p_data);
|
||||||
|
VEditArea *obj = static_cast<VEditArea *>(p_target);
|
||||||
|
|
||||||
|
VEditTab *tab = obj->getCurrentTab();
|
||||||
|
if (tab) {
|
||||||
|
tab->toggleLivePreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void VEditArea::recordClosedFile(const VFileSessionInfo &p_file)
|
void VEditArea::recordClosedFile(const VFileSessionInfo &p_file)
|
||||||
{
|
{
|
||||||
for (auto it = m_lastClosedFiles.begin(); it != m_lastClosedFiles.end(); ++it) {
|
for (auto it = m_lastClosedFiles.begin(); it != m_lastClosedFiles.end(); ++it) {
|
||||||
|
@ -208,6 +208,9 @@ private:
|
|||||||
// Prompt for user to apply a snippet.
|
// Prompt for user to apply a snippet.
|
||||||
static bool applySnippetByCaptain(void *p_target, void *p_data);
|
static bool applySnippetByCaptain(void *p_target, void *p_data);
|
||||||
|
|
||||||
|
// Toggle live preview.
|
||||||
|
static bool toggleLivePreviewByCaptain(void *p_target, void *p_data);
|
||||||
|
|
||||||
// End Captain mode functions.
|
// End Captain mode functions.
|
||||||
|
|
||||||
int curWindowIndex;
|
int curWindowIndex;
|
||||||
|
@ -366,6 +366,8 @@ signals:
|
|||||||
|
|
||||||
void mouseReleased(QMouseEvent *p_event);
|
void mouseReleased(QMouseEvent *p_event);
|
||||||
|
|
||||||
|
void cursorPositionChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// Timer for find-wrap label.
|
// Timer for find-wrap label.
|
||||||
void labelTimerTimeout()
|
void labelTimerTimeout()
|
||||||
|
@ -118,6 +118,10 @@ 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;
|
||||||
|
235
src/vlivepreviewhelper.cpp
Normal file
235
src/vlivepreviewhelper.cpp
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
#include "vlivepreviewhelper.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "veditor.h"
|
||||||
|
#include "vdocument.h"
|
||||||
|
#include "vconfigmanager.h"
|
||||||
|
#include "vgraphvizhelper.h"
|
||||||
|
#include "vplantumlhelper.h"
|
||||||
|
|
||||||
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
|
// Use the highest 4 bits (31-28) to indicate the lang.
|
||||||
|
#define LANG_PREFIX_GRAPHVIZ 0x10000000UL
|
||||||
|
#define LANG_PREFIX_PLANTUML 0x20000000UL
|
||||||
|
#define LANG_PREFIX_MASK 0xf0000000UL
|
||||||
|
|
||||||
|
// Use th 27th bit to indicate the preview type.
|
||||||
|
#define TYPE_LIVE_PREVIEW 0x0UL
|
||||||
|
#define TYPE_INPLACE_PREVIEW 0x08000000UL
|
||||||
|
#define TYPE_MASK 0x08000000UL
|
||||||
|
|
||||||
|
#define INDEX_MASK 0x00ffffffUL
|
||||||
|
|
||||||
|
VLivePreviewHelper::VLivePreviewHelper(VEditor *p_editor,
|
||||||
|
VDocument *p_document,
|
||||||
|
QObject *p_parent)
|
||||||
|
: QObject(p_parent),
|
||||||
|
m_editor(p_editor),
|
||||||
|
m_document(p_document),
|
||||||
|
m_cbIndex(-1),
|
||||||
|
m_livePreviewEnabled(false),
|
||||||
|
m_graphvizHelper(NULL),
|
||||||
|
m_plantUMLHelper(NULL)
|
||||||
|
{
|
||||||
|
connect(m_editor->object(), &VEditorObject::cursorPositionChanged,
|
||||||
|
this, &VLivePreviewHelper::handleCursorPositionChanged);
|
||||||
|
|
||||||
|
m_flowchartEnabled = g_config->getEnableFlowchart();
|
||||||
|
m_mermaidEnabled = g_config->getEnableMermaid();
|
||||||
|
m_plantUMLMode = g_config->getPlantUMLMode();
|
||||||
|
m_graphvizEnabled = g_config->getEnableGraphviz();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VLivePreviewHelper::isPreviewLang(const QString &p_lang) const
|
||||||
|
{
|
||||||
|
return (m_flowchartEnabled && (p_lang == "flow" || p_lang == "flowchart"))
|
||||||
|
|| (m_mermaidEnabled && p_lang == "mermaid")
|
||||||
|
|| (m_plantUMLMode != PlantUMLMode::DisablePlantUML && p_lang == "puml")
|
||||||
|
|| (m_graphvizEnabled && p_lang == "dot");
|
||||||
|
}
|
||||||
|
|
||||||
|
void VLivePreviewHelper::updateCodeBlocks(const QVector<VCodeBlock> &p_codeBlocks)
|
||||||
|
{
|
||||||
|
if (!m_livePreviewEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lastIndex = m_cbIndex;
|
||||||
|
m_cbIndex = -1;
|
||||||
|
int cursorBlock = m_editor->textCursorW().block().blockNumber();
|
||||||
|
int idx = 0;
|
||||||
|
bool needUpdate = true;
|
||||||
|
int nrCached = 0;
|
||||||
|
for (auto const & cb : p_codeBlocks) {
|
||||||
|
if (!isPreviewLang(cb.m_lang)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cached = false;
|
||||||
|
if (idx < m_codeBlocks.size()) {
|
||||||
|
CodeBlock &vcb = m_codeBlocks[idx];
|
||||||
|
if (vcb.m_codeBlock.equalContent(cb)) {
|
||||||
|
vcb.m_codeBlock.updateNonContent(cb);
|
||||||
|
cached = true;
|
||||||
|
++nrCached;
|
||||||
|
} else {
|
||||||
|
vcb.m_codeBlock = cb;
|
||||||
|
vcb.m_cachedResult.clear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_codeBlocks.append(CodeBlock());
|
||||||
|
m_codeBlocks[idx].m_codeBlock = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb.m_startBlock <= cursorBlock && cb.m_endBlock >= cursorBlock) {
|
||||||
|
if (lastIndex == idx && cached) {
|
||||||
|
needUpdate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cbIndex = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_codeBlocks.resize(idx);
|
||||||
|
|
||||||
|
qDebug() << "VLivePreviewHelper cache" << nrCached << "code blocks of" << m_codeBlocks.size();
|
||||||
|
|
||||||
|
if (needUpdate) {
|
||||||
|
updateLivePreview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VLivePreviewHelper::handleCursorPositionChanged()
|
||||||
|
{
|
||||||
|
if (!m_livePreviewEnabled || m_codeBlocks.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cursorBlock = m_editor->textCursorW().block().blockNumber();
|
||||||
|
|
||||||
|
int left = 0, right = m_codeBlocks.size() - 1;
|
||||||
|
int mid = left;
|
||||||
|
while (left <= right) {
|
||||||
|
mid = (left + right) / 2;
|
||||||
|
const CodeBlock &cb = m_codeBlocks[mid];
|
||||||
|
|
||||||
|
if (cb.m_codeBlock.m_startBlock <= cursorBlock && cb.m_codeBlock.m_endBlock >= cursorBlock) {
|
||||||
|
break;
|
||||||
|
} else if (cb.m_codeBlock.m_startBlock > cursorBlock) {
|
||||||
|
right = mid - 1;
|
||||||
|
} else {
|
||||||
|
left = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left <= right) {
|
||||||
|
if (m_cbIndex != mid) {
|
||||||
|
m_cbIndex = mid;
|
||||||
|
updateLivePreview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString removeFence(const QString &p_text)
|
||||||
|
{
|
||||||
|
Q_ASSERT(p_text.startsWith("```") && p_text.endsWith("```"));
|
||||||
|
int idx = p_text.indexOf('\n') + 1;
|
||||||
|
return p_text.mid(idx, p_text.size() - idx - 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VLivePreviewHelper::updateLivePreview()
|
||||||
|
{
|
||||||
|
if (m_cbIndex < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(!(m_cbIndex & ~INDEX_MASK));
|
||||||
|
|
||||||
|
const CodeBlock &cb = m_codeBlocks[m_cbIndex];
|
||||||
|
QString text = removeFence(cb.m_codeBlock.m_text);
|
||||||
|
qDebug() << "updateLivePreview" << m_cbIndex << cb.m_codeBlock.m_lang;
|
||||||
|
|
||||||
|
if (cb.m_codeBlock.m_lang == "dot") {
|
||||||
|
if (!m_graphvizHelper) {
|
||||||
|
m_graphvizHelper = new VGraphvizHelper(this);
|
||||||
|
connect(m_graphvizHelper, &VGraphvizHelper::resultReady,
|
||||||
|
this, &VLivePreviewHelper::localAsyncResultReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb.m_cachedResult.isEmpty()) {
|
||||||
|
m_graphvizHelper->processAsync(m_cbIndex | LANG_PREFIX_GRAPHVIZ | TYPE_LIVE_PREVIEW,
|
||||||
|
"svg",
|
||||||
|
text);
|
||||||
|
} else {
|
||||||
|
qDebug() << "use cached preview result of code block" << m_cbIndex;
|
||||||
|
m_document->setPreviewContent(cb.m_codeBlock.m_lang, cb.m_cachedResult);
|
||||||
|
}
|
||||||
|
} else if (cb.m_codeBlock.m_lang == "puml" && m_plantUMLMode == PlantUMLMode::LocalPlantUML) {
|
||||||
|
if (!m_plantUMLHelper) {
|
||||||
|
m_plantUMLHelper = new VPlantUMLHelper(this);
|
||||||
|
connect(m_plantUMLHelper, &VPlantUMLHelper::resultReady,
|
||||||
|
this, &VLivePreviewHelper::localAsyncResultReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb.m_cachedResult.isEmpty()) {
|
||||||
|
m_plantUMLHelper->processAsync(m_cbIndex | LANG_PREFIX_PLANTUML | TYPE_LIVE_PREVIEW,
|
||||||
|
"svg",
|
||||||
|
text);
|
||||||
|
} else {
|
||||||
|
qDebug() << "use cached preview result of code block" << m_cbIndex;
|
||||||
|
m_document->setPreviewContent(cb.m_codeBlock.m_lang, cb.m_cachedResult);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_document->previewCodeBlock(m_cbIndex, cb.m_codeBlock.m_lang, text, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VLivePreviewHelper::setLivePreviewEnabled(bool p_enabled)
|
||||||
|
{
|
||||||
|
if (m_livePreviewEnabled == p_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_livePreviewEnabled = p_enabled;
|
||||||
|
if (!m_livePreviewEnabled) {
|
||||||
|
m_cbIndex = -1;
|
||||||
|
m_codeBlocks.clear();
|
||||||
|
m_document->previewCodeBlock(-1, "", "", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VLivePreviewHelper::localAsyncResultReady(int p_id,
|
||||||
|
const QString &p_format,
|
||||||
|
const QString &p_result)
|
||||||
|
{
|
||||||
|
Q_UNUSED(p_format);
|
||||||
|
Q_ASSERT(p_format == "svg");
|
||||||
|
int idx = p_id & INDEX_MASK;
|
||||||
|
bool livePreview = (p_id & TYPE_MASK) == TYPE_LIVE_PREVIEW;
|
||||||
|
QString lang;
|
||||||
|
switch (p_id & LANG_PREFIX_MASK) {
|
||||||
|
case LANG_PREFIX_PLANTUML:
|
||||||
|
lang = "puml";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LANG_PREFIX_GRAPHVIZ:
|
||||||
|
lang = "dot";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (livePreview) {
|
||||||
|
if (idx != m_cbIndex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_codeBlocks[idx].m_cachedResult = p_result;
|
||||||
|
m_document->setPreviewContent(lang, p_result);
|
||||||
|
}
|
||||||
|
}
|
63
src/vlivepreviewhelper.h
Normal file
63
src/vlivepreviewhelper.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#ifndef VLIVEPREVIEWHELPER_H
|
||||||
|
#define VLIVEPREVIEWHELPER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "hgmarkdownhighlighter.h"
|
||||||
|
|
||||||
|
class VEditor;
|
||||||
|
class VDocument;
|
||||||
|
class VGraphvizHelper;
|
||||||
|
class VPlantUMLHelper;
|
||||||
|
|
||||||
|
class VLivePreviewHelper : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
VLivePreviewHelper(VEditor *p_editor,
|
||||||
|
VDocument *p_document,
|
||||||
|
QObject *p_parent = nullptr);
|
||||||
|
|
||||||
|
void updateLivePreview();
|
||||||
|
|
||||||
|
void setLivePreviewEnabled(bool p_enabled);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void updateCodeBlocks(const QVector<VCodeBlock> &p_codeBlocks);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleCursorPositionChanged();
|
||||||
|
|
||||||
|
void localAsyncResultReady(int p_id, const QString &p_format, const QString &p_result);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool isPreviewLang(const QString &p_lang) const;
|
||||||
|
|
||||||
|
struct CodeBlock
|
||||||
|
{
|
||||||
|
VCodeBlock m_codeBlock;
|
||||||
|
QString m_cachedResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sorted by m_startBlock in ascending order.
|
||||||
|
QVector<CodeBlock> m_codeBlocks;
|
||||||
|
|
||||||
|
VEditor *m_editor;
|
||||||
|
|
||||||
|
VDocument *m_document;
|
||||||
|
|
||||||
|
// Current previewed code block index in m_codeBlocks.
|
||||||
|
int m_cbIndex;
|
||||||
|
|
||||||
|
bool m_flowchartEnabled;
|
||||||
|
bool m_mermaidEnabled;
|
||||||
|
int m_plantUMLMode;
|
||||||
|
bool m_graphvizEnabled;
|
||||||
|
|
||||||
|
bool m_livePreviewEnabled;
|
||||||
|
|
||||||
|
VGraphvizHelper *m_graphvizHelper;
|
||||||
|
VPlantUMLHelper *m_plantUMLHelper;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VLIVEPREVIEWHELPER_H
|
@ -38,7 +38,8 @@ VMdEditor::VMdEditor(VFile *p_file,
|
|||||||
m_mdHighlighter(NULL),
|
m_mdHighlighter(NULL),
|
||||||
m_freshEdit(true),
|
m_freshEdit(true),
|
||||||
m_textToHtmlDialog(NULL),
|
m_textToHtmlDialog(NULL),
|
||||||
m_zoomDelta(0)
|
m_zoomDelta(0),
|
||||||
|
m_editTab(NULL)
|
||||||
{
|
{
|
||||||
Q_ASSERT(p_file->getDocType() == DocType::Markdown);
|
Q_ASSERT(p_file->getDocType() == DocType::Markdown);
|
||||||
|
|
||||||
@ -97,6 +98,9 @@ VMdEditor::VMdEditor(VFile *p_file,
|
|||||||
connect(this, &VTextEdit::cursorPositionChanged,
|
connect(this, &VTextEdit::cursorPositionChanged,
|
||||||
this, &VMdEditor::updateCurrentHeader);
|
this, &VMdEditor::updateCurrentHeader);
|
||||||
|
|
||||||
|
connect(this, &VTextEdit::cursorPositionChanged,
|
||||||
|
m_object, &VEditorObject::cursorPositionChanged);
|
||||||
|
|
||||||
setDisplayScaleFactor(VUtils::calculateScaleFactor());
|
setDisplayScaleFactor(VUtils::calculateScaleFactor());
|
||||||
|
|
||||||
updateFontAndPalette();
|
updateFontAndPalette();
|
||||||
@ -276,10 +280,7 @@ void VMdEditor::contextMenuEvent(QContextMenuEvent *p_event)
|
|||||||
{
|
{
|
||||||
QScopedPointer<QMenu> menu(createStandardContextMenu());
|
QScopedPointer<QMenu> menu(createStandardContextMenu());
|
||||||
menu->setToolTipsVisible(true);
|
menu->setToolTipsVisible(true);
|
||||||
|
if (m_editTab && m_editTab->isEditMode()) {
|
||||||
VEditTab *editTab = dynamic_cast<VEditTab *>(parent());
|
|
||||||
Q_ASSERT(editTab);
|
|
||||||
if (editTab->isEditMode()) {
|
|
||||||
const QList<QAction *> actions = menu->actions();
|
const QList<QAction *> actions = menu->actions();
|
||||||
|
|
||||||
if (textCursor().hasSelection()) {
|
if (textCursor().hasSelection()) {
|
||||||
@ -303,9 +304,19 @@ void VMdEditor::contextMenuEvent(QContextMenuEvent *p_event)
|
|||||||
emit m_object->discardAndRead();
|
emit m_object->discardAndRead();
|
||||||
});
|
});
|
||||||
|
|
||||||
menu->insertAction(actions.isEmpty() ? NULL : actions[0], discardExitAct);
|
QAction *toggleLivePreviewAct = new QAction(tr("Toggle Live Preview"), menu.data());
|
||||||
|
toggleLivePreviewAct->setToolTip(tr("Toggle live preview of diagrams"));
|
||||||
|
connect(toggleLivePreviewAct, &QAction::triggered,
|
||||||
|
this, [this]() {
|
||||||
|
m_editTab->toggleLivePreview();
|
||||||
|
});
|
||||||
|
|
||||||
|
menu->insertAction(actions.isEmpty() ? NULL : actions[0], toggleLivePreviewAct);
|
||||||
|
menu->insertAction(toggleLivePreviewAct, discardExitAct);
|
||||||
menu->insertAction(discardExitAct, saveExitAct);
|
menu->insertAction(discardExitAct, saveExitAct);
|
||||||
|
|
||||||
|
menu->insertSeparator(toggleLivePreviewAct);
|
||||||
|
|
||||||
if (!actions.isEmpty()) {
|
if (!actions.isEmpty()) {
|
||||||
menu->insertSeparator(actions[0]);
|
menu->insertSeparator(actions[0]);
|
||||||
}
|
}
|
||||||
@ -1322,3 +1333,8 @@ VWordCountInfo VMdEditor::fetchWordCountInfo() const
|
|||||||
info.m_charWithSpacesCount = cc;
|
info.m_charWithSpacesCount = cc;
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VMdEditor::setEditTab(VEditTab *p_editTab)
|
||||||
|
{
|
||||||
|
m_editTab = p_editTab;
|
||||||
|
}
|
||||||
|
@ -19,6 +19,7 @@ class VCodeBlockHighlightHelper;
|
|||||||
class VDocument;
|
class VDocument;
|
||||||
class VPreviewManager;
|
class VPreviewManager;
|
||||||
class VCopyTextAsHtmlDialog;
|
class VCopyTextAsHtmlDialog;
|
||||||
|
class VEditTab;
|
||||||
|
|
||||||
class VMdEditor : public VTextEdit, public VEditor
|
class VMdEditor : public VTextEdit, public VEditor
|
||||||
{
|
{
|
||||||
@ -69,6 +70,10 @@ public:
|
|||||||
|
|
||||||
VWordCountInfo fetchWordCountInfo() const Q_DECL_OVERRIDE;
|
VWordCountInfo fetchWordCountInfo() const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void setEditTab(VEditTab *p_editTab);
|
||||||
|
|
||||||
|
HGMarkdownHighlighter *getMarkdownHighlighter() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
bool jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) Q_DECL_OVERRIDE;
|
bool jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
@ -260,5 +265,12 @@ private:
|
|||||||
VCopyTextAsHtmlDialog *m_textToHtmlDialog;
|
VCopyTextAsHtmlDialog *m_textToHtmlDialog;
|
||||||
|
|
||||||
int m_zoomDelta;
|
int m_zoomDelta;
|
||||||
|
|
||||||
|
VEditTab *m_editTab;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline HGMarkdownHighlighter *VMdEditor::getMarkdownHighlighter() const
|
||||||
|
{
|
||||||
|
return m_mdHighlighter;
|
||||||
|
}
|
||||||
#endif // VMDEDITOR_H
|
#endif // VMDEDITOR_H
|
||||||
|
@ -22,11 +22,13 @@
|
|||||||
#include "vsnippet.h"
|
#include "vsnippet.h"
|
||||||
#include "vinsertselector.h"
|
#include "vinsertselector.h"
|
||||||
#include "vsnippetlist.h"
|
#include "vsnippetlist.h"
|
||||||
|
#include "vlivepreviewhelper.h"
|
||||||
|
|
||||||
extern VMainWindow *g_mainWin;
|
extern VMainWindow *g_mainWin;
|
||||||
|
|
||||||
extern VConfigManager *g_config;
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
|
|
||||||
VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
|
VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
|
||||||
OpenFileMode p_mode, QWidget *p_parent)
|
OpenFileMode p_mode, QWidget *p_parent)
|
||||||
: VEditTab(p_file, p_editArea, p_parent),
|
: VEditTab(p_file, p_editArea, p_parent),
|
||||||
@ -35,7 +37,9 @@ VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
|
|||||||
m_document(NULL),
|
m_document(NULL),
|
||||||
m_mdConType(g_config->getMdConverterType()),
|
m_mdConType(g_config->getMdConverterType()),
|
||||||
m_enableHeadingSequence(false),
|
m_enableHeadingSequence(false),
|
||||||
m_backupFileChecked(false)
|
m_backupFileChecked(false),
|
||||||
|
m_mode(Mode::InvalidMode),
|
||||||
|
m_livePreviewHelper(NULL)
|
||||||
{
|
{
|
||||||
V_ASSERT(m_file->getDocType() == DocType::Markdown);
|
V_ASSERT(m_file->getDocType() == DocType::Markdown);
|
||||||
|
|
||||||
@ -459,6 +463,7 @@ void VMdTab::setupMarkdownEditor()
|
|||||||
|
|
||||||
m_editor = new VMdEditor(m_file, m_document, m_mdConType, this);
|
m_editor = new VMdEditor(m_file, m_document, m_mdConType, this);
|
||||||
m_editor->setProperty("MainEditor", true);
|
m_editor->setProperty("MainEditor", true);
|
||||||
|
m_editor->setEditTab(this);
|
||||||
connect(m_editor, &VMdEditor::headersChanged,
|
connect(m_editor, &VMdEditor::headersChanged,
|
||||||
this, &VMdTab::updateOutlineFromHeaders);
|
this, &VMdTab::updateOutlineFromHeaders);
|
||||||
connect(m_editor, SIGNAL(currentHeaderChanged(int)),
|
connect(m_editor, SIGNAL(currentHeaderChanged(int)),
|
||||||
@ -467,10 +472,10 @@ void VMdTab::setupMarkdownEditor()
|
|||||||
this, &VMdTab::updateStatus);
|
this, &VMdTab::updateStatus);
|
||||||
connect(m_editor, &VMdEditor::textChanged,
|
connect(m_editor, &VMdEditor::textChanged,
|
||||||
this, &VMdTab::updateStatus);
|
this, &VMdTab::updateStatus);
|
||||||
connect(m_editor, &VMdEditor::cursorPositionChanged,
|
|
||||||
this, &VMdTab::updateCursorStatus);
|
|
||||||
connect(g_mainWin, &VMainWindow::editorConfigUpdated,
|
connect(g_mainWin, &VMainWindow::editorConfigUpdated,
|
||||||
m_editor, &VMdEditor::updateConfig);
|
m_editor, &VMdEditor::updateConfig);
|
||||||
|
connect(m_editor->object(), &VEditorObject::cursorPositionChanged,
|
||||||
|
this, &VMdTab::updateCursorStatus);
|
||||||
connect(m_editor->object(), &VEditorObject::saveAndRead,
|
connect(m_editor->object(), &VEditorObject::saveAndRead,
|
||||||
this, &VMdTab::saveAndRead);
|
this, &VMdTab::saveAndRead);
|
||||||
connect(m_editor->object(), &VEditorObject::discardAndRead,
|
connect(m_editor->object(), &VEditorObject::discardAndRead,
|
||||||
@ -753,9 +758,7 @@ void VMdTab::handleWebKeyPressed(int p_key, bool p_ctrl, bool p_shift)
|
|||||||
|
|
||||||
void VMdTab::zoom(bool p_zoomIn, qreal p_step)
|
void VMdTab::zoom(bool p_zoomIn, qreal p_step)
|
||||||
{
|
{
|
||||||
// Editor will handle it itself.
|
if (!m_isEditMode || m_mode == Mode::EditPreview) {
|
||||||
Q_ASSERT(!m_isEditMode);
|
|
||||||
if (!m_isEditMode) {
|
|
||||||
zoomWebPage(p_zoomIn, p_step);
|
zoomWebPage(p_zoomIn, p_step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1316,6 +1319,18 @@ VWordCountInfo VMdTab::fetchWordCountInfo(bool p_editMode) const
|
|||||||
|
|
||||||
void VMdTab::setCurrentMode(Mode p_mode)
|
void VMdTab::setCurrentMode(Mode p_mode)
|
||||||
{
|
{
|
||||||
|
if (m_mode == p_mode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal factor = m_webViewer->zoomFactor();
|
||||||
|
if (m_mode == Mode::Read) {
|
||||||
|
m_readWebViewState->m_zoomFactor = factor;
|
||||||
|
} else if (m_mode == Mode::EditPreview) {
|
||||||
|
m_previewWebViewState->m_zoomFactor = factor;
|
||||||
|
m_livePreviewHelper->setLivePreviewEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
switch (p_mode) {
|
switch (p_mode) {
|
||||||
case Mode::Read:
|
case Mode::Read:
|
||||||
m_webViewer->show();
|
m_webViewer->show();
|
||||||
@ -1323,13 +1338,55 @@ void VMdTab::setCurrentMode(Mode p_mode)
|
|||||||
m_editor->hide();
|
m_editor->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_readWebViewState.isNull()) {
|
||||||
|
m_readWebViewState.reset(new WebViewState());
|
||||||
|
m_readWebViewState->m_zoomFactor = factor;
|
||||||
|
} else if (factor != m_readWebViewState->m_zoomFactor) {
|
||||||
|
m_webViewer->setZoomFactor(m_readWebViewState->m_zoomFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_document->setPreviewEnabled(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Mode::Edit:
|
||||||
|
m_editor->show();
|
||||||
|
m_webViewer->hide();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Mode::EditPreview:
|
case Mode::EditPreview:
|
||||||
case Mode::Edit:
|
|
||||||
Q_ASSERT(m_editor);
|
Q_ASSERT(m_editor);
|
||||||
m_editor->show();
|
m_editor->show();
|
||||||
m_webViewer->hide();
|
m_webViewer->show();
|
||||||
|
if (m_previewWebViewState.isNull()) {
|
||||||
|
m_previewWebViewState.reset(new WebViewState());
|
||||||
|
m_previewWebViewState->m_zoomFactor = factor;
|
||||||
|
|
||||||
|
// Init the size of two splits.
|
||||||
|
QList<int> sizes = m_splitter->sizes();
|
||||||
|
Q_ASSERT(sizes.size() == 2);
|
||||||
|
int a = (sizes[0] + sizes[1]) / 2;
|
||||||
|
if (a <= 0) {
|
||||||
|
a = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int b = (sizes[0] + sizes[1]) - a;
|
||||||
|
|
||||||
|
QList<int> newSizes;
|
||||||
|
newSizes.append(a);
|
||||||
|
newSizes.append(b);
|
||||||
|
m_splitter->setSizes(newSizes);
|
||||||
|
|
||||||
|
Q_ASSERT(!m_livePreviewHelper);
|
||||||
|
m_livePreviewHelper = new VLivePreviewHelper(m_editor, m_document, this);
|
||||||
|
connect(m_editor->getMarkdownHighlighter(), &HGMarkdownHighlighter::codeBlocksUpdated,
|
||||||
|
m_livePreviewHelper, &VLivePreviewHelper::updateCodeBlocks);
|
||||||
|
} else if (factor != m_previewWebViewState->m_zoomFactor) {
|
||||||
|
m_webViewer->setZoomFactor(m_previewWebViewState->m_zoomFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_document->setPreviewEnabled(true);
|
||||||
|
m_livePreviewHelper->setLivePreviewEnabled(true);
|
||||||
|
m_editor->getMarkdownHighlighter()->updateHighlight();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1340,3 +1397,19 @@ void VMdTab::setCurrentMode(Mode p_mode)
|
|||||||
|
|
||||||
focusChild();
|
focusChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VMdTab::toggleLivePreview()
|
||||||
|
{
|
||||||
|
switch (m_mode) {
|
||||||
|
case Mode::EditPreview:
|
||||||
|
setCurrentMode(Mode::Edit);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Mode::Edit:
|
||||||
|
setCurrentMode(Mode::EditPreview);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
17
src/vmdtab.h
17
src/vmdtab.h
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
#include <QSharedPointer>
|
||||||
#include "vedittab.h"
|
#include "vedittab.h"
|
||||||
#include "vconstants.h"
|
#include "vconstants.h"
|
||||||
#include "vmarkdownconverter.h"
|
#include "vmarkdownconverter.h"
|
||||||
@ -15,6 +16,7 @@ class VInsertSelector;
|
|||||||
class QTimer;
|
class QTimer;
|
||||||
class QWebEngineDownloadItem;
|
class QWebEngineDownloadItem;
|
||||||
class QSplitter;
|
class QSplitter;
|
||||||
|
class VLivePreviewHelper;
|
||||||
|
|
||||||
class VMdTab : public VEditTab
|
class VMdTab : public VEditTab
|
||||||
{
|
{
|
||||||
@ -91,6 +93,9 @@ public:
|
|||||||
// Fetch tab stat info.
|
// Fetch tab stat info.
|
||||||
VWordCountInfo fetchWordCountInfo(bool p_editMode) const Q_DECL_OVERRIDE;
|
VWordCountInfo fetchWordCountInfo(bool p_editMode) const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
// Toggle live preview in edit mode.
|
||||||
|
void toggleLivePreview() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
// Enter edit mode.
|
// Enter edit mode.
|
||||||
void editFile() Q_DECL_OVERRIDE;
|
void editFile() Q_DECL_OVERRIDE;
|
||||||
@ -145,7 +150,12 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
enum TabReady { None = 0, ReadMode = 0x1, EditMode = 0x2 };
|
enum TabReady { None = 0, ReadMode = 0x1, EditMode = 0x2 };
|
||||||
|
|
||||||
enum Mode { Read = 0, Edit, EditPreview };
|
enum Mode { InvalidMode = 0, Read, Edit, EditPreview };
|
||||||
|
|
||||||
|
struct WebViewState
|
||||||
|
{
|
||||||
|
qreal m_zoomFactor;
|
||||||
|
};
|
||||||
|
|
||||||
// Setup UI.
|
// Setup UI.
|
||||||
void setupUI();
|
void setupUI();
|
||||||
@ -238,6 +248,11 @@ private:
|
|||||||
VVim::SearchItem m_lastSearchItem;
|
VVim::SearchItem m_lastSearchItem;
|
||||||
|
|
||||||
Mode m_mode;
|
Mode m_mode;
|
||||||
|
|
||||||
|
QSharedPointer<WebViewState> m_readWebViewState;
|
||||||
|
QSharedPointer<WebViewState> m_previewWebViewState;
|
||||||
|
|
||||||
|
VLivePreviewHelper *m_livePreviewHelper;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline VMdEditor *VMdTab::getEditor()
|
inline VMdEditor *VMdTab::getEditor()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user