preview non-codeblock MathJax

This commit is contained in:
Le Tan 2018-04-17 20:17:23 +08:00
parent 9566e6f5d2
commit aa5960f974
33 changed files with 910 additions and 158 deletions

View File

@ -87,7 +87,10 @@ HGMarkdownHighlighter::HGMarkdownHighlighter(const QVector<HighlightingStyle> &s
m_completeTimer->setSingleShot(true); m_completeTimer->setSingleShot(true);
m_completeTimer->setInterval(completeWaitTime); m_completeTimer->setInterval(completeWaitTime);
connect(m_completeTimer, &QTimer::timeout, connect(m_completeTimer, &QTimer::timeout,
this, &HGMarkdownHighlighter::highlightCompleted); this, [this]() {
updateMathjaxBlocks();
emit highlightCompleted();
});
connect(document, &QTextDocument::contentsChange, connect(document, &QTextDocument::contentsChange,
this, &HGMarkdownHighlighter::handleContentChange); this, &HGMarkdownHighlighter::handleContentChange);
@ -183,7 +186,7 @@ void HGMarkdownHighlighter::highlightBlock(const QString &text)
setCurrentBlockState(HighlightBlockState::Verbatim); setCurrentBlockState(HighlightBlockState::Verbatim);
goto exit; goto exit;
} else if (m_enableMathjax) { } else if (m_enableMathjax) {
highlightMathJax(curBlock, text); highlightMathJax(text);
} }
} }
@ -520,11 +523,14 @@ static bool intersect(const QList<QPair<int, int>> &p_indices, int &p_start, int
return false; return false;
} }
void HGMarkdownHighlighter::highlightMathJax(const QTextBlock &p_block, const QString &p_text) void HGMarkdownHighlighter::highlightMathJax(const QString &p_text)
{ {
const int blockMarkLength = 2; const int blockMarkLength = 2;
const int inlineMarkLength = 1; const int inlineMarkLength = 1;
VTextBlockData *blockData = currentBlockData();
Q_ASSERT(blockData);
int startIdx = 0; int startIdx = 0;
// Next position to search. // Next position to search.
int pos = 0; int pos = 0;
@ -532,8 +538,10 @@ void HGMarkdownHighlighter::highlightMathJax(const QTextBlock &p_block, const QS
QList<QPair<int, int>> blockIdices; QList<QPair<int, int>> blockIdices;
bool fromPreBlock = true;
// Mathjax block formula. // Mathjax block formula.
if (state != HighlightBlockState::MathjaxBlock) { if (state != HighlightBlockState::MathjaxBlock) {
fromPreBlock = false;
startIdx = m_mathjaxBlockExp.indexIn(p_text); startIdx = m_mathjaxBlockExp.indexIn(p_text);
pos = startIdx + m_mathjaxBlockExp.matchedLength(); pos = startIdx + m_mathjaxBlockExp.matchedLength();
startIdx = pos - blockMarkLength; startIdx = pos - blockMarkLength;
@ -542,14 +550,56 @@ void HGMarkdownHighlighter::highlightMathJax(const QTextBlock &p_block, const QS
while (startIdx >= 0) { while (startIdx >= 0) {
int endIdx = m_mathjaxBlockExp.indexIn(p_text, pos); int endIdx = m_mathjaxBlockExp.indexIn(p_text, pos);
int mathLength = 0; int mathLength = 0;
MathjaxInfo info;
if (endIdx == -1) { if (endIdx == -1) {
setCurrentBlockState(HighlightBlockState::MathjaxBlock); setCurrentBlockState(HighlightBlockState::MathjaxBlock);
mathLength = p_text.length() - startIdx; mathLength = p_text.length() - startIdx;
pos = startIdx + mathLength;
info.m_previewedAsBlock = false;
info.m_index = startIdx,
info.m_length = mathLength;
if (fromPreBlock) {
VTextBlockData *preBlockData = previousBlockData();
Q_ASSERT(preBlockData);
const MathjaxInfo &preInfo = preBlockData->getPendingMathjax();
info.m_text = preInfo.text() + "\n" + p_text.mid(startIdx, mathLength);
} else {
info.m_text = p_text.mid(startIdx, mathLength);
}
blockData->setPendingMathjax(info);
} else { } else {
// Found end marker of a formula.
mathLength = endIdx - startIdx + m_mathjaxBlockExp.matchedLength(); mathLength = endIdx - startIdx + m_mathjaxBlockExp.matchedLength();
pos = startIdx + mathLength;
info.m_previewedAsBlock = false;
info.m_index = startIdx;
info.m_length = mathLength;
if (fromPreBlock) {
// A cross-block formula.
if (pos >= p_text.length()) {
info.m_previewedAsBlock = true;
}
VTextBlockData *preBlockData = previousBlockData();
Q_ASSERT(preBlockData);
const MathjaxInfo &preInfo = preBlockData->getPendingMathjax();
info.m_text = preInfo.text() + "\n" + p_text.mid(startIdx, mathLength);
} else {
// A formula within one block.
if (pos >= p_text.length() && startIdx == 0) {
info.m_previewedAsBlock = true;
}
info.m_text = p_text.mid(startIdx, mathLength);
}
blockData->addMathjax(info);
} }
pos = startIdx + mathLength; fromPreBlock = false;
blockIdices.append(QPair<int, int>(startIdx, pos)); blockIdices.append(QPair<int, int>(startIdx, pos));
@ -562,7 +612,9 @@ void HGMarkdownHighlighter::highlightMathJax(const QTextBlock &p_block, const QS
// Mathjax inline formula. // Mathjax inline formula.
startIdx = 0; startIdx = 0;
pos = 0; pos = 0;
fromPreBlock = true;
if (state != HighlightBlockState::MathjaxInline) { if (state != HighlightBlockState::MathjaxInline) {
fromPreBlock = false;
startIdx = m_mathjaxInlineExp.indexIn(p_text); startIdx = m_mathjaxInlineExp.indexIn(p_text);
pos = startIdx + m_mathjaxInlineExp.matchedLength(); pos = startIdx + m_mathjaxInlineExp.matchedLength();
startIdx = pos - inlineMarkLength; startIdx = pos - inlineMarkLength;
@ -582,8 +634,47 @@ void HGMarkdownHighlighter::highlightMathJax(const QTextBlock &p_block, const QS
// Check if it intersect with blocks. // Check if it intersect with blocks.
if (!intersect(blockIdices, startIdx, pos)) { if (!intersect(blockIdices, startIdx, pos)) {
// A valid inline mathjax. // A valid inline mathjax.
MathjaxInfo info;
if (endIdx == -1) { if (endIdx == -1) {
setCurrentBlockState(HighlightBlockState::MathjaxInline); setCurrentBlockState(HighlightBlockState::MathjaxInline);
info.m_previewedAsBlock = false;
info.m_index = startIdx,
info.m_length = mathLength;
if (fromPreBlock) {
VTextBlockData *preBlockData = previousBlockData();
Q_ASSERT(preBlockData);
const MathjaxInfo &preInfo = preBlockData->getPendingMathjax();
info.m_text = preInfo.text() + "\n" + p_text.mid(startIdx, mathLength);
} else {
info.m_text = p_text.mid(startIdx, mathLength);
}
blockData->setPendingMathjax(info);
} else {
info.m_previewedAsBlock = false;
info.m_index = startIdx;
info.m_length = mathLength;
if (fromPreBlock) {
// A cross-block formula.
if (pos >= p_text.length()) {
info.m_previewedAsBlock = true;
}
VTextBlockData *preBlockData = previousBlockData();
Q_ASSERT(preBlockData);
const MathjaxInfo &preInfo = preBlockData->getPendingMathjax();
info.m_text = preInfo.text() + "\n" + p_text.mid(startIdx, mathLength);
} else {
// A formula within one block.
if (pos >= p_text.length() && startIdx == 0) {
info.m_previewedAsBlock = true;
}
info.m_text = p_text.mid(startIdx, mathLength);
}
blockData->addMathjax(info);
} }
setFormat(startIdx, mathLength, m_mathjaxFormat); setFormat(startIdx, mathLength, m_mathjaxFormat);
@ -599,6 +690,8 @@ void HGMarkdownHighlighter::highlightMathJax(const QTextBlock &p_block, const QS
startIdx = pos - inlineMarkLength; startIdx = pos - inlineMarkLength;
} }
fromPreBlock = false;
} }
} }
@ -813,12 +906,8 @@ bool HGMarkdownHighlighter::updateCodeBlocks()
} }
m_numOfCodeBlockHighlightsToRecv = codeBlocks.size(); m_numOfCodeBlockHighlightsToRecv = codeBlocks.size();
if (m_numOfCodeBlockHighlightsToRecv > 0) { emit codeBlocksUpdated(codeBlocks);
emit codeBlocksUpdated(codeBlocks); return m_numOfCodeBlockHighlightsToRecv > 0;
return true;
} else {
return false;
}
} }
static bool compHLUnitStyle(const HLUnitStyle &a, const HLUnitStyle &b) static bool compHLUnitStyle(const HLUnitStyle &a, const HLUnitStyle &b)
@ -977,3 +1066,29 @@ void HGMarkdownHighlighter::highlightHeaderFast(int p_blockNumber, const QString
} }
} }
} }
void HGMarkdownHighlighter::updateMathjaxBlocks()
{
if (!m_enableMathjax) {
return;
}
QVector<VMathjaxBlock> blocks;
QTextBlock bl = document->firstBlock();
while (bl.isValid()) {
VTextBlockData *data = static_cast<VTextBlockData *>(bl.userData());
if (!data) {
bl = bl.next();
continue;
}
const QVector<MathjaxInfo> &info = data->getMathjax();
for (auto const & it : info) {
blocks.append(VMathjaxBlock(bl.blockNumber(), it));
}
bl = bl.next();
}
emit mathjaxBlocksUpdated(blocks);
}

View File

@ -69,6 +69,52 @@ struct VCodeBlock
} }
}; };
struct VMathjaxBlock
{
VMathjaxBlock()
: m_blockNumber(-1),
m_previewedAsBlock(false),
m_index(-1),
m_length(-1)
{
}
VMathjaxBlock(int p_blockNumber, const MathjaxInfo &p_info)
: m_blockNumber(p_blockNumber),
m_previewedAsBlock(p_info.m_previewedAsBlock),
m_index(p_info.m_index),
m_length(p_info.m_length),
m_text(p_info.m_text)
{
}
bool equalContent(const VMathjaxBlock &p_block) const
{
return m_text == p_block.m_text;
}
void updateNonContent(const VMathjaxBlock &p_block)
{
m_blockNumber = p_block.m_blockNumber;
m_previewedAsBlock = p_block.m_previewedAsBlock;
m_index = p_block.m_index;
m_length = p_block.m_length;
}
int m_blockNumber;
bool m_previewedAsBlock;
// Start index within the block.
int m_index;
int m_length;
QString m_text;
};
// Highlight unit with global position and string style name. // Highlight unit with global position and string style name.
struct HLUnitPos struct HLUnitPos
{ {
@ -169,6 +215,9 @@ signals:
// Emitted when header regions have been fetched from a new parsing result. // Emitted when header regions have been fetched from a new parsing result.
void headersUpdated(const QVector<VElementRegion> &p_headerRegions); void headersUpdated(const QVector<VElementRegion> &p_headerRegions);
// Emitted when Mathjax blocks updated.
void mathjaxBlocksUpdated(const QVector<VMathjaxBlock> &p_mathjaxBlocks);
protected: protected:
void highlightBlock(const QString &text) Q_DECL_OVERRIDE; void highlightBlock(const QString &text) Q_DECL_OVERRIDE;
@ -272,7 +321,7 @@ private:
void highlightCodeBlock(const QTextBlock &p_block, const QString &p_text); void highlightCodeBlock(const QTextBlock &p_block, const QString &p_text);
void highlightMathJax(const QTextBlock &p_block, const QString &p_text); void highlightMathJax(const QString &p_text);
// Highlight links using regular expression. // Highlight links using regular expression.
// PEG Markdown Highlight treat URLs with spaces illegal. This function is // PEG Markdown Highlight treat URLs with spaces illegal. This function is
@ -295,6 +344,8 @@ private:
// Return false if there is none. // Return false if there is none.
bool updateCodeBlocks(); bool updateCodeBlocks();
void updateMathjaxBlocks();
// Fetch all the HTML comment regions from parsing result. // Fetch all the HTML comment regions from parsing result.
void initHtmlCommentRegionsFromResult(); void initHtmlCommentRegionsFromResult();

View File

@ -96,14 +96,14 @@ var highlightText = function(text, id, timeStamp) {
content.highlightTextCB(html, id, timeStamp); content.highlightTextCB(html, id, timeStamp);
} }
var textToHtml = function(text) { var textToHtml = function(identifier, id, timeStamp, text, inlineStyle) {
var html = marked(text); var html = marked(text);
var container = textHtmlDiv; if (inlineStyle) {
container.innerHTML = html; var container = textHtmlDiv;
container.innerHTML = html;
html = getHtmlWithInlineStyles(container);
container.innerHTML = "";
}
html = getHtmlWithInlineStyles(container); content.textToHtmlCB(identifier, id, timeStamp, html);
container.innerHTML = "";
content.textToHtmlCB(text, html);
} }

View File

@ -140,14 +140,14 @@ var highlightText = function(text, id, timeStamp) {
content.highlightTextCB(html, id, timeStamp); content.highlightTextCB(html, id, timeStamp);
}; };
var textToHtml = function(text) { var textToHtml = function(identifier, id, timeStamp, text, inlineStyle) {
var html = mdit.render(text); var html = mdit.render(text);
var container = textHtmlDiv; if (inlineStyle) {
container.innerHTML = html; var container = textHtmlDiv;
container.innerHTML = html;
html = getHtmlWithInlineStyles(container);
container.innerHTML = "";
}
html = getHtmlWithInlineStyles(container); content.textToHtmlCB(identifier, id, timeStamp, html);
container.innerHTML = "";
content.textToHtmlCB(text, html);
}; };

View File

@ -84,14 +84,14 @@ var highlightText = function(text, id, timeStamp) {
content.highlightTextCB(html, id, timeStamp); content.highlightTextCB(html, id, timeStamp);
} }
var textToHtml = function(text) { var textToHtml = function(identifier, id, timeStamp, text, inlineStyle) {
var html = marked(text); var html = marked(text);
var container = textHtmlDiv; if (inlineStyle) {
container.innerHTML = html; var container = textHtmlDiv;
container.innerHTML = html;
html = getHtmlWithInlineStyles(container);
container.innerHTML = "";
}
html = getHtmlWithInlineStyles(container); content.textToHtmlCB(identifier, id, timeStamp, html);
container.innerHTML = "";
content.textToHtmlCB(text, html);
} }

View File

@ -17,29 +17,72 @@ new QWebChannel(qt.webChannelTransport,
channelInitialized = true; channelInitialized = true;
}); });
var previewMathJax = function(identifier, id, timeStamp, text) { var timeStamps = new Map();
if (text.length == 0) {
var htmlToElement = function(html) {
var template = document.createElement('template');
html = html.trim();
template.innerHTML = html;
return template.content.firstChild;
};
var isEmptyMathJax = function(text) {
return text.replace(/\$/g, '').trim().length == 0;
};
var previewMathJax = function(identifier, id, timeStamp, text, isHtml) {
timeStamps.set(identifier, timeStamp);
if (isEmptyMathJax(text)) {
content.mathjaxResultReady(identifier, id, timeStamp, 'png', '');
return;
}
var p = null;
if (isHtml) {
p = htmlToElement(text);
if (isEmptyMathJax(p.textContent)) {
p = null;
}
} else {
p = document.createElement('p');
p.textContent = text;
}
if (!p) {
content.mathjaxResultReady(identifier, id, timeStamp, 'png', '');
return; return;
} }
var p = document.createElement('p');
p.textContent = text;
contentDiv.appendChild(p); contentDiv.appendChild(p);
var isBlock = false;
if (text.indexOf('$$') !== -1) {
isBlock = true;
}
try { try {
MathJax.Hub.Queue(["Typeset", MathJax.Hub.Queue(["Typeset",
MathJax.Hub, MathJax.Hub,
p, p,
postProcessMathJax.bind(undefined, identifier, id, timeStamp, p)]); [postProcessMathJax, identifier, id, timeStamp, p, isBlock]]);
} catch (err) { } catch (err) {
console.log("err: " + err); console.log("err: " + err);
content.mathjaxResultReady(identifier, id, timeStamp, 'png', '');
contentDiv.removeChild(p); contentDiv.removeChild(p);
delete p; delete p;
} }
}; };
var postProcessMathJax = function(identifier, id, timeStamp, container) { var postProcessMathJax = function(identifier, id, timeStamp, container, isBlock) {
domtoimage.toPng(container, { height: container.clientHeight * 1.5 }).then(function (dataUrl) { if (timeStamps.get(identifier) != timeStamp) {
contentDiv.removeChild(container);
delete container;
return;
}
var hei = (isBlock ? container.clientHeight * 1.5 : container.clientHeight * 1.8) + 5;
domtoimage.toPng(container, { height: hei }).then(function (dataUrl) {
var png = dataUrl.substring(dataUrl.indexOf(',') + 1); var png = dataUrl.substring(dataUrl.indexOf(',') + 1);
content.mathjaxResultReady(identifier, id, timeStamp, 'png', png); content.mathjaxResultReady(identifier, id, timeStamp, 'png', png);
@ -47,6 +90,7 @@ var postProcessMathJax = function(identifier, id, timeStamp, container) {
delete container; delete container;
}).catch(function (err) { }).catch(function (err) {
console.log("err: " + err); console.log("err: " + err);
content.mathjaxResultReady(identifier, id, timeStamp, 'png', '');
contentDiv.removeChild(container); contentDiv.removeChild(container);
delete container; delete container;
}); });

View File

@ -137,7 +137,7 @@ var highlightText = function(text, id, timeStamp) {
content.highlightTextCB(html, id, timeStamp); content.highlightTextCB(html, id, timeStamp);
} }
var textToHtml = function(text) { var textToHtml = function(identifier, id, timeStamp, text, inlineStyle) {
var html = renderer.makeHtml(text); var html = renderer.makeHtml(text);
var parser = new DOMParser(); var parser = new DOMParser();
@ -148,12 +148,12 @@ var textToHtml = function(text) {
delete parser; delete parser;
var container = textHtmlDiv; if (inlineStyle) {
container.innerHTML = html; var container = textHtmlDiv;
container.innerHTML = html;
html = getHtmlWithInlineStyles(container);
container.innerHTML = "";
}
html = getHtmlWithInlineStyles(container); content.textToHtmlCB(identifier, id, timeStamp, html);
container.innerHTML = "";
content.textToHtmlCB(text, html);
} }

View File

@ -23,17 +23,17 @@ p {
} }
h1 { h1 {
font-size: 36px;
}
h2 {
font-size: 30px; font-size: 30px;
} }
h3 { h2 {
font-size: 26px; font-size: 26px;
} }
h3 {
font-size: 24px;
}
h4 { h4 {
font-size: 22px; font-size: 22px;
} }

View File

@ -7,7 +7,7 @@ mdhl_file=v_moonlight.mdhl
css_file=v_moonlight.css css_file=v_moonlight.css
codeblock_css_file=v_moonlight_codeblock.css codeblock_css_file=v_moonlight_codeblock.css
mermaid_css_file=v_moonlight_mermaid.css mermaid_css_file=v_moonlight_mermaid.css
version=8 version=9
; This mapping will be used to translate colors when the content of HTML is copied ; This mapping will be used to translate colors when the content of HTML is copied
; without background. You could just specify the foreground colors mapping here. ; without background. You could just specify the foreground colors mapping here.

View File

@ -22,17 +22,17 @@ p {
} }
h1 { h1 {
font-size: 36px;
}
h2 {
font-size: 30px; font-size: 30px;
} }
h3 { h2 {
font-size: 26px; font-size: 26px;
} }
h3 {
font-size: 24px;
}
h4 { h4 {
font-size: 22px; font-size: 22px;
} }

View File

@ -7,7 +7,7 @@ mdhl_file=v_native.mdhl
css_file=v_native.css css_file=v_native.css
codeblock_css_file=v_native_codeblock.css codeblock_css_file=v_native_codeblock.css
mermaid_css_file=v_native_mermaid.css mermaid_css_file=v_native_mermaid.css
version=8 version=9
[phony] [phony]
; Abstract color attributes. ; Abstract color attributes.

View File

@ -23,17 +23,17 @@ p {
} }
h1 { h1 {
font-size: 36px;
}
h2 {
font-size: 30px; font-size: 30px;
} }
h3 { h2 {
font-size: 26px; font-size: 26px;
} }
h3 {
font-size: 24px;
}
h4 { h4 {
font-size: 22px; font-size: 22px;
} }

View File

@ -7,7 +7,7 @@ mdhl_file=v_pure.mdhl
css_file=v_pure.css css_file=v_pure.css
codeblock_css_file=v_pure_codeblock.css codeblock_css_file=v_pure_codeblock.css
mermaid_css_file=v_pure_mermaid.css mermaid_css_file=v_pure_mermaid.css
version=8 version=9
[phony] [phony]
; Abstract color attributes. ; Abstract color attributes.

View File

@ -130,7 +130,8 @@ SOURCES += main.cpp\
vgraphvizhelper.cpp \ vgraphvizhelper.cpp \
vlivepreviewhelper.cpp \ vlivepreviewhelper.cpp \
vmathjaxpreviewhelper.cpp \ vmathjaxpreviewhelper.cpp \
vmathjaxwebdocument.cpp vmathjaxwebdocument.cpp \
vmathjaxinplacepreviewhelper.cpp
HEADERS += vmainwindow.h \ HEADERS += vmainwindow.h \
vdirectorytree.h \ vdirectorytree.h \
@ -251,7 +252,8 @@ HEADERS += vmainwindow.h \
vgraphvizhelper.h \ vgraphvizhelper.h \
vlivepreviewhelper.h \ vlivepreviewhelper.h \
vmathjaxpreviewhelper.h \ vmathjaxpreviewhelper.h \
vmathjaxwebdocument.h vmathjaxwebdocument.h \
vmathjaxinplacepreviewhelper.h
RESOURCES += \ RESOURCES += \
vnote.qrc \ vnote.qrc \

View File

@ -535,6 +535,9 @@ qreal VUtils::calculateScaleFactor()
factor = dpi / refDpi; factor = dpi / refDpi;
if (factor < 1) { if (factor < 1) {
factor = 1; factor = 1;
} else {
// Keep only two digits after the dot.
factor = (int)(factor * 100) / 100.0;
} }
} }

View File

@ -11,7 +11,8 @@ VDocument::VDocument(const VFile *v_file, QObject *p_parent)
m_file(v_file), m_file(v_file),
m_readyToHighlight(false), m_readyToHighlight(false),
m_plantUMLHelper(NULL), m_plantUMLHelper(NULL),
m_graphvizHelper(NULL) m_graphvizHelper(NULL),
m_nextID(0)
{ {
} }
@ -83,9 +84,13 @@ void VDocument::highlightTextCB(const QString &p_html, int p_id, int p_timeStamp
emit textHighlighted(p_html, p_id, p_timeStamp); emit textHighlighted(p_html, p_id, p_timeStamp);
} }
void VDocument::textToHtmlAsync(const QString &p_text) void VDocument::textToHtmlAsync(int p_identitifer,
int p_id,
int p_timeStamp,
const QString &p_text,
bool p_inlineStyle)
{ {
emit requestTextToHtml(p_text); emit requestTextToHtml(p_identitifer, p_id, p_timeStamp, p_text, p_inlineStyle);
} }
void VDocument::getHtmlContentAsync() void VDocument::getHtmlContentAsync()
@ -93,9 +98,9 @@ void VDocument::getHtmlContentAsync()
emit requestHtmlContent(); emit requestHtmlContent();
} }
void VDocument::textToHtmlCB(const QString &p_text, const QString &p_html) void VDocument::textToHtmlCB(int p_identitifer, int p_id, int p_timeStamp, const QString &p_html)
{ {
emit textToHtmlFinished(p_text, p_html); emit textToHtmlFinished(p_identitifer, p_id, p_timeStamp, p_html);
} }
void VDocument::noticeReadyToHighlightText() void VDocument::noticeReadyToHighlightText()

View File

@ -34,7 +34,11 @@ public:
void highlightTextAsync(const QString &p_text, int p_id, int p_timeStamp); void highlightTextAsync(const QString &p_text, int p_id, int p_timeStamp);
// Request to convert @p_text to HTML. // Request to convert @p_text to HTML.
void textToHtmlAsync(const QString &p_text); void textToHtmlAsync(int p_identitifer,
int p_id,
int p_timeStamp,
const QString &p_text,
bool p_inlineStyle);
void setFile(const VFile *p_file); void setFile(const VFile *p_file);
@ -61,6 +65,8 @@ public:
// Set the content of the preview. // Set the content of the preview.
void setPreviewContent(const QString &p_lang, const QString &p_html); void setPreviewContent(const QString &p_lang, const QString &p_html);
int registerIdentifier();
public slots: public slots:
// Will be called in the HTML side // Will be called in the HTML side
@ -82,7 +88,7 @@ public slots:
void noticeReadyToHighlightText(); void noticeReadyToHighlightText();
void textToHtmlCB(const QString &p_text, const QString &p_html); void textToHtmlCB(int p_identitifer, int p_id, int p_timeStamp, const QString &p_html);
void noticeReadyToTextToHtml(); void noticeReadyToTextToHtml();
@ -130,9 +136,13 @@ signals:
void logicsFinished(); void logicsFinished();
void requestTextToHtml(const QString &p_text); void requestTextToHtml(int p_identitifer,
int p_id,
int p_timeStamp,
const QString &p_text,
bool p_inlineStyle);
void textToHtmlFinished(const QString &p_text, const QString &p_html); void textToHtmlFinished(int p_identitifer, int p_id, int p_timeStamp, const QString &p_html);
void requestHtmlContent(); void requestHtmlContent();
@ -186,6 +196,8 @@ private:
VPlantUMLHelper *m_plantUMLHelper; VPlantUMLHelper *m_plantUMLHelper;
VGraphvizHelper *m_graphvizHelper; VGraphvizHelper *m_graphvizHelper;
int m_nextID;
}; };
inline bool VDocument::isReadyToHighlight() const inline bool VDocument::isReadyToHighlight() const
@ -203,4 +215,8 @@ inline const VWordCountInfo &VDocument::getWordCountInfo() const
return m_wordCountInfo; return m_wordCountInfo;
} }
inline int VDocument::registerIdentifier()
{
return ++m_nextID;
}
#endif // VDOCUMENT_H #endif // VDOCUMENT_H

View File

@ -39,13 +39,6 @@ CodeBlockPreviewInfo::CodeBlockPreviewInfo(const VCodeBlock &p_cb)
{ {
} }
void CodeBlockPreviewInfo::clearImageData()
{
m_imgData.clear();
m_imgDataBa.clear();
m_inplacePreview.clear();
}
void CodeBlockPreviewInfo::updateNonContent(const QTextDocument *p_doc, void CodeBlockPreviewInfo::updateNonContent(const QTextDocument *p_doc,
const VCodeBlock &p_cb) const VCodeBlock &p_cb)
{ {
@ -60,6 +53,7 @@ void CodeBlockPreviewInfo::updateNonContent(const QTextDocument *p_doc,
m_inplacePreview->m_endPos = block.position() + block.length(); m_inplacePreview->m_endPos = block.position() + block.length();
m_inplacePreview->m_blockPos = block.position(); m_inplacePreview->m_blockPos = block.position();
m_inplacePreview->m_blockNumber = m_codeBlock.m_endBlock; m_inplacePreview->m_blockNumber = m_codeBlock.m_endBlock;
// Padding is not changed since content is not changed.
} else { } else {
m_inplacePreview->clear(); m_inplacePreview->clear();
} }
@ -143,7 +137,8 @@ void VLivePreviewHelper::updateCodeBlocks(const QVector<VCodeBlock> &p_codeBlock
m_cbIndex = -1; m_cbIndex = -1;
int cursorBlock = m_editor->textCursorW().block().blockNumber(); int cursorBlock = m_editor->textCursorW().block().blockNumber();
int idx = 0; int idx = 0;
bool needUpdate = true; bool needUpdate = m_livePreviewEnabled;
bool manualInplacePreview = m_inplacePreviewEnabled;
for (auto const & vcb : p_codeBlocks) { for (auto const & vcb : p_codeBlocks) {
if (!isPreviewLang(vcb.m_lang)) { if (!isPreviewLang(vcb.m_lang)) {
continue; continue;
@ -165,6 +160,7 @@ void VLivePreviewHelper::updateCodeBlocks(const QVector<VCodeBlock> &p_codeBlock
if (m_inplacePreviewEnabled if (m_inplacePreviewEnabled
&& !m_codeBlocks[idx].inplacePreviewReady()) { && !m_codeBlocks[idx].inplacePreviewReady()) {
processForInplacePreview(idx); processForInplacePreview(idx);
manualInplacePreview = false;
} }
if (m_livePreviewEnabled if (m_livePreviewEnabled
@ -180,9 +176,17 @@ void VLivePreviewHelper::updateCodeBlocks(const QVector<VCodeBlock> &p_codeBlock
++idx; ++idx;
} }
m_codeBlocks.resize(idx); if (idx == m_codeBlocks.size()) {
manualInplacePreview = false;
} else {
m_codeBlocks.resize(idx);
}
if (m_livePreviewEnabled && needUpdate) { if (manualInplacePreview) {
updateInplacePreview();
}
if (needUpdate) {
updateLivePreview(); updateLivePreview();
} }
} }
@ -283,8 +287,12 @@ void VLivePreviewHelper::setLivePreviewEnabled(bool p_enabled)
m_livePreviewEnabled = p_enabled; m_livePreviewEnabled = p_enabled;
if (!m_livePreviewEnabled) { if (!m_livePreviewEnabled) {
m_cbIndex = -1; m_cbIndex = -1;
m_codeBlocks.clear();
m_document->previewCodeBlock(-1, "", "", true); m_document->previewCodeBlock(-1, "", "", true);
if (!m_inplacePreviewEnabled) {
m_codeBlocks.clear();
updateInplacePreview();
}
} }
} }
@ -295,13 +303,11 @@ void VLivePreviewHelper::setInplacePreviewEnabled(bool p_enabled)
} }
m_inplacePreviewEnabled = p_enabled; m_inplacePreviewEnabled = p_enabled;
if (!m_livePreviewEnabled) { if (!m_inplacePreviewEnabled && !m_livePreviewEnabled) {
for (auto & cb : m_codeBlocks) { m_codeBlocks.clear();
cb.clearImageData();
}
updateInplacePreview();
} }
updateInplacePreview();
} }
void VLivePreviewHelper::localAsyncResultReady(int p_id, void VLivePreviewHelper::localAsyncResultReady(int p_id,
@ -356,6 +362,7 @@ void VLivePreviewHelper::processForInplacePreview(int p_idx)
{ {
CodeBlockPreviewInfo &cb = m_codeBlocks[p_idx]; CodeBlockPreviewInfo &cb = m_codeBlocks[p_idx];
const VCodeBlock &vcb = cb.codeBlock(); const VCodeBlock &vcb = cb.codeBlock();
Q_ASSERT(!(cb.hasImageData() || cb.hasImageDataBa()));
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);
@ -363,15 +370,10 @@ void VLivePreviewHelper::processForInplacePreview(int p_idx)
this, &VLivePreviewHelper::localAsyncResultReady); this, &VLivePreviewHelper::localAsyncResultReady);
} }
if (cb.hasImageData()) { m_graphvizHelper->processAsync(p_idx | LANG_PREFIX_GRAPHVIZ | TYPE_INPLACE_PREVIEW,
cb.updateInplacePreview(m_editor, m_doc); m_timeStamp,
updateInplacePreview(); "svg",
} else { removeFence(vcb.m_text));
m_graphvizHelper->processAsync(p_idx | LANG_PREFIX_GRAPHVIZ | TYPE_INPLACE_PREVIEW,
m_timeStamp,
"svg",
removeFence(vcb.m_text));
}
} else if (vcb.m_lang == "puml" && m_plantUMLMode == PlantUMLMode::LocalPlantUML) { } else if (vcb.m_lang == "puml" && m_plantUMLMode == PlantUMLMode::LocalPlantUML) {
if (!m_plantUMLHelper) { if (!m_plantUMLHelper) {
m_plantUMLHelper = new VPlantUMLHelper(this); m_plantUMLHelper = new VPlantUMLHelper(this);
@ -379,15 +381,10 @@ void VLivePreviewHelper::processForInplacePreview(int p_idx)
this, &VLivePreviewHelper::localAsyncResultReady); this, &VLivePreviewHelper::localAsyncResultReady);
} }
if (cb.hasImageData()) { m_plantUMLHelper->processAsync(p_idx | LANG_PREFIX_PLANTUML | TYPE_INPLACE_PREVIEW,
cb.updateInplacePreview(m_editor, m_doc); m_timeStamp,
updateInplacePreview(); "svg",
} else { removeFence(vcb.m_text));
m_plantUMLHelper->processAsync(p_idx | LANG_PREFIX_PLANTUML | TYPE_INPLACE_PREVIEW,
m_timeStamp,
"svg",
removeFence(vcb.m_text));
}
} else if (vcb.m_lang == "flow" } else if (vcb.m_lang == "flow"
|| vcb.m_lang == "flowchart") { || vcb.m_lang == "flowchart") {
m_mathJaxHelper->previewDiagram(m_mathJaxID, m_mathJaxHelper->previewDiagram(m_mathJaxID,

View File

@ -21,9 +21,15 @@ public:
explicit CodeBlockPreviewInfo(const VCodeBlock &p_cb); explicit CodeBlockPreviewInfo(const VCodeBlock &p_cb);
void clearImageData(); void clearImageData()
{
m_imgData.clear();
m_imgDataBa.clear();
m_inplacePreview.clear();
}
void updateNonContent(const QTextDocument *p_doc, const VCodeBlock &p_cb); void updateNonContent(const QTextDocument *p_doc,
const VCodeBlock &p_cb);
void updateInplacePreview(const VEditor *p_editor, const QTextDocument *p_doc); void updateInplacePreview(const VEditor *p_editor, const QTextDocument *p_doc);
@ -151,7 +157,6 @@ private slots:
const QByteArray &p_data); const QByteArray &p_data);
private: private:
bool isPreviewLang(const QString &p_lang) const; bool isPreviewLang(const QString &p_lang) const;
// Get image data for this code block for inplace preview. // Get image data for this code block for inplace preview.

View File

@ -0,0 +1,242 @@
#include "vmathjaxinplacepreviewhelper.h"
#include <QDebug>
#include "veditor.h"
#include "vdocument.h"
#include "vmainwindow.h"
#include "veditarea.h"
#include "vmathjaxpreviewhelper.h"
extern VMainWindow *g_mainWin;
MathjaxBlockPreviewInfo::MathjaxBlockPreviewInfo()
{
}
MathjaxBlockPreviewInfo::MathjaxBlockPreviewInfo(const VMathjaxBlock &p_mb)
: m_mathjaxBlock(p_mb)
{
}
void MathjaxBlockPreviewInfo::updateNonContent(const QTextDocument *p_doc,
const VEditor *p_editor,
const VMathjaxBlock &p_mb)
{
m_mathjaxBlock.updateNonContent(p_mb);
if (m_inplacePreview.isNull()) {
return;
}
QTextBlock block = p_doc->findBlockByNumber(m_mathjaxBlock.m_blockNumber);
if (block.isValid()) {
m_inplacePreview->m_startPos = block.position() + m_mathjaxBlock.m_index;
m_inplacePreview->m_endPos = m_inplacePreview->m_startPos + m_mathjaxBlock.m_length;
m_inplacePreview->m_blockPos = block.position();
m_inplacePreview->m_blockNumber = m_mathjaxBlock.m_blockNumber;
// Padding may changed.
m_inplacePreview->m_padding = VPreviewManager::calculateBlockMargin(block,
p_editor->tabStopWidthW());
m_inplacePreview->m_isBlock = m_mathjaxBlock.m_previewedAsBlock;
} else {
m_inplacePreview->clear();
}
}
void MathjaxBlockPreviewInfo::updateInplacePreview(const VEditor *p_editor,
const QTextDocument *p_doc)
{
QTextBlock block = p_doc->findBlockByNumber(m_mathjaxBlock.m_blockNumber);
if (block.isValid()) {
if (m_inplacePreview.isNull()) {
m_inplacePreview.reset(new VImageToPreview());
}
m_inplacePreview->m_startPos = block.position() + m_mathjaxBlock.m_index;
m_inplacePreview->m_endPos = m_inplacePreview->m_startPos + m_mathjaxBlock.m_length;
m_inplacePreview->m_blockPos = block.position();
m_inplacePreview->m_blockNumber = m_mathjaxBlock.m_blockNumber;
m_inplacePreview->m_padding = VPreviewManager::calculateBlockMargin(block,
p_editor->tabStopWidthW());
m_inplacePreview->m_name = QString::number(getImageIndex());
m_inplacePreview->m_isBlock = m_mathjaxBlock.m_previewedAsBlock;
if (hasImageDataBa()) {
m_inplacePreview->m_image.loadFromData(m_imgDataBa,
m_imgFormat.toLocal8Bit().data());
} else {
m_inplacePreview->m_image = QPixmap();
}
} else {
m_inplacePreview->clear();
}
}
VMathJaxInplacePreviewHelper::VMathJaxInplacePreviewHelper(VEditor *p_editor,
VDocument *p_document,
QObject *p_parent)
: QObject(p_parent),
m_editor(p_editor),
m_document(p_document),
m_doc(p_editor->documentW()),
m_enabled(false),
m_lastInplacePreviewSize(0),
m_timeStamp(0)
{
m_mathJaxHelper = g_mainWin->getEditArea()->getMathJaxPreviewHelper();
m_mathJaxID = m_mathJaxHelper->registerIdentifier();
connect(m_mathJaxHelper, &VMathJaxPreviewHelper::mathjaxPreviewResultReady,
this, &VMathJaxInplacePreviewHelper::mathjaxPreviewResultReady);
m_documentID = m_document->registerIdentifier();
connect(m_document, &VDocument::textToHtmlFinished,
this, &VMathJaxInplacePreviewHelper::textToHtmlFinished);
}
void VMathJaxInplacePreviewHelper::setEnabled(bool p_enabled)
{
if (m_enabled != p_enabled) {
m_enabled = p_enabled;
if (!m_enabled) {
m_mathjaxBlocks.clear();
}
updateInplacePreview();
}
}
void VMathJaxInplacePreviewHelper::updateMathjaxBlocks(const QVector<VMathjaxBlock> &p_blocks)
{
if (!m_enabled) {
return;
}
++m_timeStamp;
int idx = 0;
bool manualUpdate = true;
for (auto const & vmb : p_blocks) {
if (idx < m_mathjaxBlocks.size()) {
MathjaxBlockPreviewInfo &mb = m_mathjaxBlocks[idx];
if (mb.mathjaxBlock().equalContent(vmb)) {
mb.updateNonContent(m_doc, m_editor, vmb);
} else {
mb.setMathjaxBlock(vmb);
}
} else {
m_mathjaxBlocks.append(MathjaxBlockPreviewInfo(vmb));
}
if (m_enabled
&& !m_mathjaxBlocks[idx].inplacePreviewReady()) {
manualUpdate = false;
processForInplacePreview(idx);
}
++idx;
}
m_mathjaxBlocks.resize(idx);
if (manualUpdate) {
updateInplacePreview();
}
}
void VMathJaxInplacePreviewHelper::processForInplacePreview(int p_idx)
{
MathjaxBlockPreviewInfo &mb = m_mathjaxBlocks[p_idx];
const VMathjaxBlock &vmb = mb.mathjaxBlock();
if (vmb.m_text.isEmpty()) {
updateInplacePreview();
} else {
textToHtmlViaWebView(vmb.m_text, p_idx, m_timeStamp);
}
}
void VMathJaxInplacePreviewHelper::textToHtmlViaWebView(const QString &p_text,
int p_id,
int p_timeStamp)
{
int maxRetry = 50;
while (!m_document->isReadyToTextToHtml() && maxRetry > 0) {
qDebug() << "wait for web side ready to convert text to HTML";
VUtils::sleepWait(100);
--maxRetry;
}
if (maxRetry == 0) {
qWarning() << "web side is not ready to convert text to HTML";
return;
}
m_document->textToHtmlAsync(m_documentID, p_id, p_timeStamp, p_text, false);
}
void VMathJaxInplacePreviewHelper::updateInplacePreview()
{
QSet<int> blocks;
QVector<QSharedPointer<VImageToPreview> > images;
for (int i = 0; i < m_mathjaxBlocks.size(); ++i) {
MathjaxBlockPreviewInfo &mb = m_mathjaxBlocks[i];
if (mb.inplacePreviewReady()) {
if (!mb.inplacePreview()->m_image.isNull()) {
images.append(mb.inplacePreview());
} else {
blocks.insert(mb.inplacePreview()->m_blockNumber);
}
} else {
blocks.insert(mb.mathjaxBlock().m_blockNumber);
}
}
if (images.isEmpty() && m_lastInplacePreviewSize == 0) {
return;
}
emit inplacePreviewMathjaxBlockUpdated(images);
m_lastInplacePreviewSize = images.size();
if (!blocks.isEmpty()) {
emit checkBlocksForObsoletePreview(blocks.toList());
}
}
void VMathJaxInplacePreviewHelper::mathjaxPreviewResultReady(int p_identitifer,
int p_id,
TimeStamp p_timeStamp,
const QString &p_format,
const QByteArray &p_data)
{
if (p_identitifer != m_mathJaxID || p_timeStamp != m_timeStamp) {
return;
}
if (p_id >= m_mathjaxBlocks.size() || p_data.isEmpty()) {
updateInplacePreview();
return;
}
MathjaxBlockPreviewInfo &mb = m_mathjaxBlocks[p_id];
mb.setImageDataBa(p_format, p_data);
mb.updateInplacePreview(m_editor, m_doc);
updateInplacePreview();
}
void VMathJaxInplacePreviewHelper::textToHtmlFinished(int p_identitifer,
int p_id,
int p_timeStamp,
const QString &p_html)
{
if (m_documentID != p_identitifer || m_timeStamp != p_timeStamp) {
return;
}
Q_ASSERT(p_html.startsWith("<"));
m_mathJaxHelper->previewMathJaxFromHtml(m_mathJaxID,
p_id,
p_timeStamp,
p_html);
}

View File

@ -0,0 +1,147 @@
#ifndef VMATHJAXINPLACEPREVIEWHELPER_H
#define VMATHJAXINPLACEPREVIEWHELPER_H
#include <QObject>
#include "hgmarkdownhighlighter.h"
#include "vpreviewmanager.h"
#include "vconstants.h"
class VEditor;
class VDocument;
class QTextDocument;
class VMathJaxPreviewHelper;
class MathjaxBlockPreviewInfo
{
public:
MathjaxBlockPreviewInfo();
explicit MathjaxBlockPreviewInfo(const VMathjaxBlock &p_mb);
void clearImageData()
{
m_imgDataBa.clear();
m_inplacePreview.clear();
}
void updateNonContent(const QTextDocument *p_doc,
const VEditor *p_editor,
const VMathjaxBlock &p_mb);
void updateInplacePreview(const VEditor *p_editor, const QTextDocument *p_doc);
VMathjaxBlock &mathjaxBlock()
{
return m_mathjaxBlock;
}
const VMathjaxBlock &mathjaxBlock() const
{
return m_mathjaxBlock;
}
void setMathjaxBlock(const VMathjaxBlock &p_mb)
{
m_mathjaxBlock = p_mb;
clearImageData();
}
bool inplacePreviewReady() const
{
return !m_inplacePreview.isNull();
}
void setImageDataBa(const QString &p_format, const QByteArray &p_data)
{
m_imgFormat = p_format;
m_imgDataBa = p_data;
}
bool hasImageDataBa() const
{
return !m_imgDataBa.isEmpty();
}
const QSharedPointer<VImageToPreview> inplacePreview() const
{
return m_inplacePreview;
}
private:
static int getImageIndex()
{
static int index = 0;
return ++index;
}
VMathjaxBlock m_mathjaxBlock;
QByteArray m_imgDataBa;
QString m_imgFormat;
QSharedPointer<VImageToPreview> m_inplacePreview;
};
class VMathJaxInplacePreviewHelper : public QObject
{
Q_OBJECT
public:
VMathJaxInplacePreviewHelper(VEditor *p_editor,
VDocument *p_document,
QObject *p_parent = nullptr);
void setEnabled(bool p_enabled);
public slots:
void updateMathjaxBlocks(const QVector<VMathjaxBlock> &p_blocks);
signals:
void inplacePreviewMathjaxBlockUpdated(const QVector<QSharedPointer<VImageToPreview> > &p_images);
void checkBlocksForObsoletePreview(const QList<int> &p_blocks);
private slots:
void mathjaxPreviewResultReady(int p_identitifer,
int p_id,
TimeStamp p_timeStamp,
const QString &p_format,
const QByteArray &p_data);
void textToHtmlFinished(int p_identitifer, int p_id, int p_timeStamp, const QString &p_html);
private:
void processForInplacePreview(int p_idx);
// Emit signal to update inplace preview.
void updateInplacePreview();
void textToHtmlViaWebView(const QString &p_text,
int p_id,
int p_timeStamp);
VEditor *m_editor;
VDocument *m_document;
QTextDocument *m_doc;
bool m_enabled;
VMathJaxPreviewHelper *m_mathJaxHelper;
// Identification for VMathJaxPreviewHelper.
int m_mathJaxID;
int m_lastInplacePreviewSize;
TimeStamp m_timeStamp;
// Sorted by m_blockNumber in ascending order.
QVector<MathjaxBlockPreviewInfo> m_mathjaxBlocks;
int m_documentID;
};
#endif // VMATHJAXINPLACEPREVIEWHELPER_H

View File

@ -1,8 +1,8 @@
#include "vmathjaxpreviewhelper.h" #include "vmathjaxpreviewhelper.h"
#include <QDebug>
#include <QWebEngineView> #include <QWebEngineView>
#include <QWebChannel> #include <QWebChannel>
#include <QApplication>
#include "utils/vutils.h" #include "utils/vutils.h"
#include "vmathjaxwebdocument.h" #include "vmathjaxwebdocument.h"
@ -24,17 +24,31 @@ VMathJaxPreviewHelper::~VMathJaxPreviewHelper()
void VMathJaxPreviewHelper::doInit() void VMathJaxPreviewHelper::doInit()
{ {
Q_ASSERT(!m_initialized); Q_ASSERT(!m_initialized);
Q_ASSERT(m_parentWidget);
m_initialized = true; m_initialized = true;
QWidget *focusWid = QApplication::focusWidget();
m_webView = new QWebEngineView(m_parentWidget); m_webView = new QWebEngineView(m_parentWidget);
connect(m_webView, &QWebEngineView::loadFinished, connect(m_webView, &QWebEngineView::loadFinished,
this, [this]() { this, [this]() {
m_webReady = true; m_webReady = true;
}); for (auto const & it : m_pendingFunc) {
it();
}
m_pendingFunc.clear();
});
m_webView->hide(); m_webView->hide();
m_webView->setFocusPolicy(Qt::NoFocus); m_webView->setFocusPolicy(Qt::NoFocus);
if (focusWid) {
focusWid->setFocus();
} else {
m_parentWidget->setFocus();
}
m_webDoc = new VMathJaxWebDocument(m_webView); m_webDoc = new VMathJaxWebDocument(m_webView);
connect(m_webDoc, &VMathJaxWebDocument::mathjaxPreviewResultReady, connect(m_webDoc, &VMathJaxWebDocument::mathjaxPreviewResultReady,
this, [this](int p_identifier, this, [this](int p_identifier,
@ -61,10 +75,6 @@ void VMathJaxPreviewHelper::doInit()
m_webView->page()->setWebChannel(channel); m_webView->page()->setWebChannel(channel);
m_webView->setHtml(VUtils::generateMathJaxPreviewTemplate(), QUrl("qrc:/resources")); m_webView->setHtml(VUtils::generateMathJaxPreviewTemplate(), QUrl("qrc:/resources"));
while (!m_webReady) {
VUtils::sleepWait(100);
}
} }
void VMathJaxPreviewHelper::previewMathJax(int p_identifier, void VMathJaxPreviewHelper::previewMathJax(int p_identifier,
@ -74,7 +84,39 @@ void VMathJaxPreviewHelper::previewMathJax(int p_identifier,
{ {
init(); init();
m_webDoc->previewMathJax(p_identifier, p_id, p_timeStamp, p_text); if (!m_webReady) {
auto func = std::bind(&VMathJaxWebDocument::previewMathJax,
m_webDoc,
p_identifier,
p_id,
p_timeStamp,
p_text,
false);
m_pendingFunc.append(func);
} else {
m_webDoc->previewMathJax(p_identifier, p_id, p_timeStamp, p_text, false);
}
}
void VMathJaxPreviewHelper::previewMathJaxFromHtml(int p_identifier,
int p_id,
TimeStamp p_timeStamp,
const QString &p_html)
{
init();
if (!m_webReady) {
auto func = std::bind(&VMathJaxWebDocument::previewMathJax,
m_webDoc,
p_identifier,
p_id,
p_timeStamp,
p_html,
true);
m_pendingFunc.append(func);
} else {
m_webDoc->previewMathJax(p_identifier, p_id, p_timeStamp, p_html, true);
}
} }
void VMathJaxPreviewHelper::previewDiagram(int p_identifier, void VMathJaxPreviewHelper::previewDiagram(int p_identifier,
@ -85,5 +127,16 @@ void VMathJaxPreviewHelper::previewDiagram(int p_identifier,
{ {
init(); init();
m_webDoc->previewDiagram(p_identifier, p_id, p_timeStamp, p_lang, p_text); if (!m_webReady) {
auto func = std::bind(&VMathJaxWebDocument::previewDiagram,
m_webDoc,
p_identifier,
p_id,
p_timeStamp,
p_lang,
p_text);
m_pendingFunc.append(func);
} else {
m_webDoc->previewDiagram(p_identifier, p_id, p_timeStamp, p_lang, p_text);
}
} }

View File

@ -2,6 +2,8 @@
#define VMATHJAXPREVIEWHELPER_H #define VMATHJAXPREVIEWHELPER_H
#include <QObject> #include <QObject>
#include <functional>
#include <QVector>
#include "vconstants.h" #include "vconstants.h"
@ -9,6 +11,8 @@ class QWebEngineView;
class VMathJaxWebDocument; class VMathJaxWebDocument;
class QWidget; class QWidget;
typedef std::function<void(void)> PendingFunc;
class VMathJaxPreviewHelper : public QObject class VMathJaxPreviewHelper : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -26,6 +30,8 @@ public:
// @p_text: raw text of the MathJax script. // @p_text: raw text of the MathJax script.
void previewMathJax(int p_identifier, int p_id, TimeStamp p_timeStamp, const QString &p_text); void previewMathJax(int p_identifier, int p_id, TimeStamp p_timeStamp, const QString &p_text);
void previewMathJaxFromHtml(int p_identitifer, int p_id, TimeStamp p_timeStamp, const QString &p_html);
// Preview @p_text and return PNG data asynchronously. // Preview @p_text and return PNG data asynchronously.
// @p_identifier: identifier the caller registered; // @p_identifier: identifier the caller registered;
// @p_id: internal id for each caller; // @p_id: internal id for each caller;
@ -66,6 +72,8 @@ private:
VMathJaxWebDocument *m_webDoc; VMathJaxWebDocument *m_webDoc;
bool m_webReady; bool m_webReady;
QVector<PendingFunc> m_pendingFunc;
}; };
inline int VMathJaxPreviewHelper::registerIdentifier() inline int VMathJaxPreviewHelper::registerIdentifier()

View File

@ -1,7 +1,5 @@
#include "vmathjaxwebdocument.h" #include "vmathjaxwebdocument.h"
#include <QDebug>
VMathJaxWebDocument::VMathJaxWebDocument(QObject *p_parent) VMathJaxWebDocument::VMathJaxWebDocument(QObject *p_parent)
: QObject(p_parent) : QObject(p_parent)
{ {
@ -10,9 +8,10 @@ VMathJaxWebDocument::VMathJaxWebDocument(QObject *p_parent)
void VMathJaxWebDocument::previewMathJax(int p_identifier, void VMathJaxWebDocument::previewMathJax(int p_identifier,
int p_id, int p_id,
TimeStamp p_timeStamp, TimeStamp p_timeStamp,
const QString &p_text) const QString &p_text,
bool p_isHtml)
{ {
emit requestPreviewMathJax(p_identifier, p_id, p_timeStamp, p_text); emit requestPreviewMathJax(p_identifier, p_id, p_timeStamp, p_text, p_isHtml);
} }
void VMathJaxWebDocument::mathjaxResultReady(int p_identifier, void VMathJaxWebDocument::mathjaxResultReady(int p_identifier,

View File

@ -11,7 +11,11 @@ class VMathJaxWebDocument : public QObject
public: public:
explicit VMathJaxWebDocument(QObject *p_parent = nullptr); explicit VMathJaxWebDocument(QObject *p_parent = nullptr);
void previewMathJax(int p_identifier, int p_id, TimeStamp p_timeStamp, const QString &p_text); void previewMathJax(int p_identifier,
int p_id,
TimeStamp p_timeStamp,
const QString &p_text,
bool p_isHtml);
void previewDiagram(int p_identifier, void previewDiagram(int p_identifier,
int p_id, int p_id,
@ -38,7 +42,8 @@ signals:
void requestPreviewMathJax(int p_identifier, void requestPreviewMathJax(int p_identifier,
int p_id, int p_id,
unsigned long long p_timeStamp, unsigned long long p_timeStamp,
const QString &p_text); const QString &p_text,
bool p_isHtml);
void requestPreviewDiagram(int p_identifier, void requestPreviewDiagram(int p_identifier,
int p_id, int p_id,

View File

@ -39,7 +39,8 @@ VMdEditor::VMdEditor(VFile *p_file,
m_freshEdit(true), m_freshEdit(true),
m_textToHtmlDialog(NULL), m_textToHtmlDialog(NULL),
m_zoomDelta(0), m_zoomDelta(0),
m_editTab(NULL) m_editTab(NULL),
m_copyTimeStamp(0)
{ {
Q_ASSERT(p_file->getDocType() == DocType::Markdown); Q_ASSERT(p_file->getDocType() == DocType::Markdown);
@ -1124,6 +1125,8 @@ void VMdEditor::updateInitAndInsertedImages(bool p_fileChanged, UpdateAction p_a
void VMdEditor::handleCopyAsAction(QAction *p_act) void VMdEditor::handleCopyAsAction(QAction *p_act)
{ {
++m_copyTimeStamp;
QTextCursor cursor = textCursor(); QTextCursor cursor = textCursor();
Q_ASSERT(cursor.hasSelection()); Q_ASSERT(cursor.hasSelection());
@ -1134,7 +1137,7 @@ void VMdEditor::handleCopyAsAction(QAction *p_act)
m_textToHtmlDialog = new VCopyTextAsHtmlDialog(text, p_act->data().toString(), this); m_textToHtmlDialog = new VCopyTextAsHtmlDialog(text, p_act->data().toString(), this);
// For Hoedown, we use marked.js to convert the text to have a general interface. // For Hoedown, we use marked.js to convert the text to have a general interface.
emit requestTextToHtml(text); emit requestTextToHtml(text, 0, m_copyTimeStamp);
m_textToHtmlDialog->exec(); m_textToHtmlDialog->exec();
@ -1142,11 +1145,13 @@ void VMdEditor::handleCopyAsAction(QAction *p_act)
m_textToHtmlDialog = NULL; m_textToHtmlDialog = NULL;
} }
void VMdEditor::textToHtmlFinished(const QString &p_text, void VMdEditor::textToHtmlFinished(int p_id,
int p_timeStamp,
const QUrl &p_baseUrl, const QUrl &p_baseUrl,
const QString &p_html) const QString &p_html)
{ {
if (m_textToHtmlDialog && m_textToHtmlDialog->getText() == p_text) { Q_UNUSED(p_id);
if (m_textToHtmlDialog && p_timeStamp == m_copyTimeStamp) {
m_textToHtmlDialog->setConvertedHtml(p_baseUrl, p_html); m_textToHtmlDialog->setConvertedHtml(p_baseUrl, p_html);
} }
} }

View File

@ -79,7 +79,7 @@ public:
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;
void textToHtmlFinished(const QString &p_text, const QUrl &p_baseUrl, const QString &p_html); void textToHtmlFinished(int p_id, int p_timeStamp, const QUrl &p_baseUrl, const QString &p_html);
// Wrapper functions for QPlainTextEdit/QTextEdit. // Wrapper functions for QPlainTextEdit/QTextEdit.
public: public:
@ -194,7 +194,7 @@ signals:
void statusChanged(); void statusChanged();
// Request to convert @p_text to Html. // Request to convert @p_text to Html.
void requestTextToHtml(const QString &p_text); void requestTextToHtml(const QString &p_text, int p_id, int p_timeStamp);
protected: protected:
void updateFontAndPalette() Q_DECL_OVERRIDE; void updateFontAndPalette() Q_DECL_OVERRIDE;
@ -274,6 +274,8 @@ private:
int m_zoomDelta; int m_zoomDelta;
VEditTab *m_editTab; VEditTab *m_editTab;
int m_copyTimeStamp;
}; };
inline HGMarkdownHighlighter *VMdEditor::getMarkdownHighlighter() const inline HGMarkdownHighlighter *VMdEditor::getMarkdownHighlighter() const

View File

@ -23,6 +23,7 @@
#include "vinsertselector.h" #include "vinsertselector.h"
#include "vsnippetlist.h" #include "vsnippetlist.h"
#include "vlivepreviewhelper.h" #include "vlivepreviewhelper.h"
#include "vmathjaxinplacepreviewhelper.h"
extern VMainWindow *g_mainWin; extern VMainWindow *g_mainWin;
@ -39,7 +40,8 @@ VMdTab::VMdTab(VFile *p_file, VEditArea *p_editArea,
m_enableHeadingSequence(false), m_enableHeadingSequence(false),
m_backupFileChecked(false), m_backupFileChecked(false),
m_mode(Mode::InvalidMode), m_mode(Mode::InvalidMode),
m_livePreviewHelper(NULL) m_livePreviewHelper(NULL),
m_mathjaxPreviewHelper(NULL)
{ {
V_ASSERT(m_file->getDocType() == DocType::Markdown); V_ASSERT(m_file->getDocType() == DocType::Markdown);
@ -412,6 +414,7 @@ void VMdTab::setupMarkdownViewer()
page->setBackgroundColor(Qt::transparent); page->setBackgroundColor(Qt::transparent);
m_document = new VDocument(m_file, m_webViewer); m_document = new VDocument(m_file, m_webViewer);
m_documentID = m_document->registerIdentifier();
QWebChannel *channel = new QWebChannel(m_webViewer); QWebChannel *channel = new QWebChannel(m_webViewer);
channel->registerObject(QStringLiteral("content"), m_document); channel->registerObject(QStringLiteral("content"), m_document);
@ -435,9 +438,13 @@ void VMdTab::setupMarkdownViewer()
tabIsReady(TabReady::ReadMode); tabIsReady(TabReady::ReadMode);
}); });
connect(m_document, &VDocument::textToHtmlFinished, connect(m_document, &VDocument::textToHtmlFinished,
this, [this](const QString &p_text, const QString &p_html) { this, [this](int p_identitifer, int p_id, int p_timeStamp, const QString &p_html) {
Q_ASSERT(m_editor); Q_ASSERT(m_editor);
m_editor->textToHtmlFinished(p_text, m_webViewer->url(), p_html); if (m_documentID != p_identitifer) {
return;
}
m_editor->textToHtmlFinished(p_id, p_timeStamp, m_webViewer->url(), p_html);
}); });
connect(m_document, &VDocument::wordCountInfoUpdated, connect(m_document, &VDocument::wordCountInfoUpdated,
this, [this]() { this, [this]() {
@ -526,6 +533,17 @@ void VMdTab::setupMarkdownEditor()
connect(m_livePreviewHelper, &VLivePreviewHelper::checkBlocksForObsoletePreview, connect(m_livePreviewHelper, &VLivePreviewHelper::checkBlocksForObsoletePreview,
m_editor->getPreviewManager(), &VPreviewManager::checkBlocksForObsoletePreview); m_editor->getPreviewManager(), &VPreviewManager::checkBlocksForObsoletePreview);
m_livePreviewHelper->setInplacePreviewEnabled(m_editor->getPreviewManager()->isPreviewEnabled()); m_livePreviewHelper->setInplacePreviewEnabled(m_editor->getPreviewManager()->isPreviewEnabled());
m_mathjaxPreviewHelper = new VMathJaxInplacePreviewHelper(m_editor, m_document, this);
connect(m_editor->getMarkdownHighlighter(), &HGMarkdownHighlighter::mathjaxBlocksUpdated,
m_mathjaxPreviewHelper, &VMathJaxInplacePreviewHelper::updateMathjaxBlocks);
connect(m_editor->getPreviewManager(), &VPreviewManager::previewEnabledChanged,
m_mathjaxPreviewHelper, &VMathJaxInplacePreviewHelper::setEnabled);
connect(m_mathjaxPreviewHelper, &VMathJaxInplacePreviewHelper::inplacePreviewMathjaxBlockUpdated,
m_editor->getPreviewManager(), &VPreviewManager::updateMathjaxBlocks);
connect(m_mathjaxPreviewHelper, &VMathJaxInplacePreviewHelper::checkBlocksForObsoletePreview,
m_editor->getPreviewManager(), &VPreviewManager::checkBlocksForObsoletePreview);
m_mathjaxPreviewHelper->setEnabled(m_editor->getPreviewManager()->isPreviewEnabled());
} }
void VMdTab::updateOutlineFromHtml(const QString &p_tocHtml) void VMdTab::updateOutlineFromHtml(const QString &p_tocHtml)
@ -1129,7 +1147,7 @@ void VMdTab::handleFileOrDirectoryChange(bool p_isFile, UpdateAction p_act)
} }
} }
void VMdTab::textToHtmlViaWebView(const QString &p_text) void VMdTab::textToHtmlViaWebView(const QString &p_text, int p_id, int p_timeStamp)
{ {
int maxRetry = 50; int maxRetry = 50;
while (!m_document->isReadyToTextToHtml() && maxRetry > 0) { while (!m_document->isReadyToTextToHtml() && maxRetry > 0) {
@ -1143,7 +1161,7 @@ void VMdTab::textToHtmlViaWebView(const QString &p_text)
return; return;
} }
m_document->textToHtmlAsync(p_text); m_document->textToHtmlAsync(m_documentID, p_id, p_timeStamp, p_text, true);
} }
void VMdTab::handleVimCmdCommandCancelled() void VMdTab::handleVimCmdCommandCancelled()

View File

@ -17,6 +17,7 @@ class QTimer;
class QWebEngineDownloadItem; class QWebEngineDownloadItem;
class QSplitter; class QSplitter;
class VLivePreviewHelper; class VLivePreviewHelper;
class VMathJaxInplacePreviewHelper;
class VMdTab : public VEditTab class VMdTab : public VEditTab
{ {
@ -218,7 +219,7 @@ private:
// updateStatus() with only cursor position information. // updateStatus() with only cursor position information.
void updateCursorStatus(); void updateCursorStatus();
void textToHtmlViaWebView(const QString &p_text); void textToHtmlViaWebView(const QString &p_text, int p_id, int p_timeStamp);
bool executeVimCommandInWebView(const QString &p_cmd); bool executeVimCommandInWebView(const QString &p_cmd);
@ -253,6 +254,9 @@ private:
QSharedPointer<WebViewState> m_previewWebViewState; QSharedPointer<WebViewState> m_previewWebViewState;
VLivePreviewHelper *m_livePreviewHelper; VLivePreviewHelper *m_livePreviewHelper;
VMathJaxInplacePreviewHelper *m_mathjaxPreviewHelper;
int m_documentID;
}; };
inline VMdEditor *VMdTab::getEditor() inline VMdEditor *VMdTab::getEditor()

View File

@ -258,9 +258,10 @@ QString VPreviewManager::imageResourceName(const ImageLinkInfo &p_link)
return name; return name;
} }
QString VPreviewManager::imageResourceNameFromCodeBlock(const QSharedPointer<VImageToPreview> &p_image) QString VPreviewManager::imageResourceNameForSource(PreviewSource p_source,
const QSharedPointer<VImageToPreview> &p_image)
{ {
QString name = "CODE_BLOCK_" + p_image->m_name; QString name = QString::number((int)p_source) + "_" + p_image->m_name;
if (m_editor->containsImage(name)) { if (m_editor->containsImage(name)) {
return name; return name;
} }
@ -371,7 +372,7 @@ void VPreviewManager::updateBlockPreviewInfo(TS p_timeStamp,
continue; continue;
} }
QString name = imageResourceNameFromCodeBlock(img); QString name = imageResourceNameForSource(p_source, img);
if (name.isEmpty()) { if (name.isEmpty()) {
continue; continue;
} }
@ -420,7 +421,6 @@ void VPreviewManager::clearBlockObsoletePreviewInfo(long long p_timeStamp,
QSet<int> affectedBlocks; QSet<int> affectedBlocks;
QVector<int> obsoleteBlocks; QVector<int> obsoleteBlocks;
const QSet<int> &blocks = m_highlighter->getPossiblePreviewBlocks(); const QSet<int> &blocks = m_highlighter->getPossiblePreviewBlocks();
qDebug() << "possible preview blocks" << blocks;
for (auto i : blocks) { for (auto i : blocks) {
QTextBlock block = m_document->findBlockByNumber(i); QTextBlock block = m_document->findBlockByNumber(i);
if (!block.isValid()) { if (!block.isValid()) {
@ -474,6 +474,21 @@ void VPreviewManager::updateCodeBlocks(const QVector<QSharedPointer<VImageToPrev
clearObsoleteImages(ts, PreviewSource::CodeBlock); clearObsoleteImages(ts, PreviewSource::CodeBlock);
} }
void VPreviewManager::updateMathjaxBlocks(const QVector<QSharedPointer<VImageToPreview> > &p_images)
{
if (!m_previewEnabled) {
return;
}
TS ts = ++timeStamp(PreviewSource::MathjaxBlock);
updateBlockPreviewInfo(ts, PreviewSource::MathjaxBlock, p_images);
clearBlockObsoletePreviewInfo(ts, PreviewSource::MathjaxBlock);
clearObsoleteImages(ts, PreviewSource::MathjaxBlock);
}
void VPreviewManager::checkBlocksForObsoletePreview(const QList<int> &p_blocks) void VPreviewManager::checkBlocksForObsoletePreview(const QList<int> &p_blocks)
{ {
if (p_blocks.isEmpty()) { if (p_blocks.isEmpty()) {

View File

@ -80,6 +80,8 @@ public slots:
void updateCodeBlocks(const QVector<QSharedPointer<VImageToPreview> > &p_images); void updateCodeBlocks(const QVector<QSharedPointer<VImageToPreview> > &p_images);
void updateMathjaxBlocks(const QVector<QSharedPointer<VImageToPreview> > &p_images);
signals: signals:
// Request highlighter to update image links. // Request highlighter to update image links.
void requestUpdateImageLinks(); void requestUpdateImageLinks();
@ -168,7 +170,7 @@ private:
// Returns empty if fail to add the image to the resource manager. // Returns empty if fail to add the image to the resource manager.
QString imageResourceName(const ImageLinkInfo &p_link); QString imageResourceName(const ImageLinkInfo &p_link);
QString imageResourceNameFromCodeBlock(const QSharedPointer<VImageToPreview> &p_image); QString imageResourceNameForSource(PreviewSource p_source, const QSharedPointer<VImageToPreview> &p_image);
QHash<QString, long long> &imageCache(PreviewSource p_source); QHash<QString, long long> &imageCache(PreviewSource p_source);

View File

@ -35,13 +35,11 @@ bool VTextBlockData::insertPreviewInfo(VPreviewInfo *p_info)
*it = p_info; *it = p_info;
inserted = true; inserted = true;
tsUpdated = true; tsUpdated = true;
qDebug() << "update eixsting image's timestamp" << p_info->m_imageInfo.toString();
break; break;
} else if (p_info->m_imageInfo.intersect(ele->m_imageInfo)) { } else if (p_info->m_imageInfo.intersect(ele->m_imageInfo)) {
// The new one intersect with an old one. // The new one intersect with an old one.
// Remove the old one. // Remove the old one.
Q_ASSERT(ele->m_timeStamp < p_info->m_timeStamp); Q_ASSERT(ele->m_timeStamp < p_info->m_timeStamp);
qDebug() << "remove intersecting old image" << ele->m_imageInfo.toString();
delete ele; delete ele;
it = m_previews.erase(it); it = m_previews.erase(it);
} else { } else {

View File

@ -9,6 +9,7 @@ enum class PreviewSource
{ {
ImageLink = 0, ImageLink = 0,
CodeBlock, CodeBlock,
MathjaxBlock,
MaxNumberOfSources MaxNumberOfSources
}; };
@ -133,7 +134,7 @@ struct MathjaxInfo
{ {
public: public:
MathjaxInfo() MathjaxInfo()
: m_isBlock(false), : m_previewedAsBlock(false),
m_index(-1), m_index(-1),
m_length(0) m_length(0)
{ {
@ -145,26 +146,41 @@ public:
return m_index >= 0 && m_length > 0; return m_index >= 0 && m_length > 0;
} }
bool isBlock() const bool previewedAsBlock() const
{ {
return m_isBlock; return m_previewedAsBlock;
} }
void clear() void clear()
{ {
m_isBlock = false; m_previewedAsBlock = false;
m_index = -1; m_index = -1;
m_length = 0; m_length = 0;
} }
// Inline or block formula. const QString &text() const
bool m_isBlock; {
return m_text;
}
QString toString() const
{
return QString("MathjaxInfo %1 (%2,%3) %4").arg(m_previewedAsBlock)
.arg(m_index)
.arg(m_length)
.arg(m_text);
}
// Whether it should be previewed as block or not.
bool m_previewedAsBlock;
// Start index wihtin block, including the start mark. // Start index wihtin block, including the start mark.
int m_index; int m_index;
// Length of this mathjax, including the end mark. // Length of this mathjax, including the end mark.
int m_length; int m_length;
QString m_text;
}; };
@ -198,7 +214,7 @@ public:
void setPendingMathjax(const MathjaxInfo &p_info); void setPendingMathjax(const MathjaxInfo &p_info);
const QVector<MathjaxInfo> getMathjax() const; const QVector<MathjaxInfo> &getMathjax() const;
void addMathjax(const MathjaxInfo &p_info); void addMathjax(const MathjaxInfo &p_info);
@ -250,7 +266,7 @@ inline void VTextBlockData::setPendingMathjax(const MathjaxInfo &p_info)
m_pendingMathjax = p_info; m_pendingMathjax = p_info;
} }
inline const QVector<MathjaxInfo> VTextBlockData::getMathjax() const inline const QVector<MathjaxInfo> &VTextBlockData::getMathjax() const
{ {
return m_mathjax; return m_mathjax;
} }