mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
support in place preview and live preview of code blocks
This commit is contained in:
parent
0b9cc6e5b3
commit
cfcc7e5494
@ -146,6 +146,8 @@ public:
|
|||||||
|
|
||||||
void clearPossiblePreviewBlocks(const QVector<int> &p_blocksToClear);
|
void clearPossiblePreviewBlocks(const QVector<int> &p_blocksToClear);
|
||||||
|
|
||||||
|
void addPossiblePreviewBlock(int p_blockNumber);
|
||||||
|
|
||||||
// Parse and only update the highlight results for rehighlight().
|
// Parse and only update the highlight results for rehighlight().
|
||||||
void updateHighlightFast();
|
void updateHighlightFast();
|
||||||
|
|
||||||
@ -329,6 +331,11 @@ inline void HGMarkdownHighlighter::clearPossiblePreviewBlocks(const QVector<int>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void HGMarkdownHighlighter::addPossiblePreviewBlock(int p_blockNumber)
|
||||||
|
{
|
||||||
|
m_possiblePreviewBlocks.insert(p_blockNumber);
|
||||||
|
}
|
||||||
|
|
||||||
inline VTextBlockData *HGMarkdownHighlighter::currentBlockData() const
|
inline VTextBlockData *HGMarkdownHighlighter::currentBlockData() const
|
||||||
{
|
{
|
||||||
return static_cast<VTextBlockData *>(currentBlockUserData());
|
return static_cast<VTextBlockData *>(currentBlockUserData());
|
||||||
|
@ -43,7 +43,7 @@ var mdit = window.markdownit({
|
|||||||
typographer: false,
|
typographer: false,
|
||||||
langPrefix: 'lang-',
|
langPrefix: 'lang-',
|
||||||
highlight: function(str, lang) {
|
highlight: function(str, lang) {
|
||||||
if (lang && !specialCodeBlock(lang)) {
|
if (lang && (!specialCodeBlock(lang) || highlightSpecialBlocks)) {
|
||||||
if (hljs.getLanguage(lang)) {
|
if (hljs.getLanguage(lang)) {
|
||||||
return hljs.highlight(lang, str, true).value;
|
return hljs.highlight(lang, str, true).value;
|
||||||
} else {
|
} else {
|
||||||
@ -134,7 +134,9 @@ var updateText = function(text) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var highlightText = function(text, id, timeStamp) {
|
var highlightText = function(text, id, timeStamp) {
|
||||||
|
highlightSpecialBlocks = true;
|
||||||
var html = mdit.render(text);
|
var html = mdit.render(text);
|
||||||
|
highlightSpecialBlocks = false;
|
||||||
content.highlightTextCB(html, id, timeStamp);
|
content.highlightTextCB(html, id, timeStamp);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
|
|
||||||
<div id="preview-div" style="display:none;"></div>
|
<div id="preview-div" style="display:none;"></div>
|
||||||
|
|
||||||
|
<div id="inplace-preview-div" style="display:none;"></div>
|
||||||
|
|
||||||
<div id="text-html-div" style="display:none;"></div>
|
<div id="text-html-div" style="display:none;"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -4,6 +4,8 @@ var contentDiv = document.getElementById('content-div');
|
|||||||
|
|
||||||
var previewDiv = document.getElementById('preview-div');
|
var previewDiv = document.getElementById('preview-div');
|
||||||
|
|
||||||
|
var inplacePreviewDiv = document.getElementById('inplace-preview-div');
|
||||||
|
|
||||||
var textHtmlDiv = document.getElementById('text-html-div');
|
var textHtmlDiv = document.getElementById('text-html-div');
|
||||||
|
|
||||||
var content;
|
var content;
|
||||||
@ -85,6 +87,9 @@ if (typeof VAddTOC == 'undefined') {
|
|||||||
VAddTOC = false;
|
VAddTOC = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whether highlight special blocks like puml, flowchart.
|
||||||
|
var highlightSpecialBlocks = false;
|
||||||
|
|
||||||
var getUrlScheme = function(url) {
|
var getUrlScheme = function(url) {
|
||||||
var idx = url.indexOf(':');
|
var idx = url.indexOf(':');
|
||||||
if (idx > -1) {
|
if (idx > -1) {
|
||||||
@ -1204,6 +1209,7 @@ var initStylesToInline = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Embed the CSS styles of @ele and all its children.
|
// Embed the CSS styles of @ele and all its children.
|
||||||
|
// StylesToInline need to be init before.
|
||||||
var embedInlineStyles = function(ele) {
|
var embedInlineStyles = function(ele) {
|
||||||
var tagName = ele.tagName.toLowerCase();
|
var tagName = ele.tagName.toLowerCase();
|
||||||
var props = StylesToInline.get(tagName);
|
var props = StylesToInline.get(tagName);
|
||||||
@ -1373,7 +1379,7 @@ var setPreviewEnabled = function(enabled) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var previewCodeBlock = function(id, lang, text, isLivePreview) {
|
var previewCodeBlock = function(id, lang, text, isLivePreview) {
|
||||||
var div = previewDiv;
|
var div = isLivePreview ? previewDiv : inplacePreviewDiv;
|
||||||
div.innerHTML = '';
|
div.innerHTML = '';
|
||||||
div.className = '';
|
div.className = '';
|
||||||
|
|
||||||
@ -1381,7 +1387,7 @@ var previewCodeBlock = function(id, lang, text, isLivePreview) {
|
|||||||
|| (lang != 'flow'
|
|| (lang != 'flow'
|
||||||
&& lang != 'flowchart'
|
&& lang != 'flowchart'
|
||||||
&& lang != 'mermaid'
|
&& lang != 'mermaid'
|
||||||
&& (lang != 'puml' || VPlantUMLMode != 1))) {
|
&& (lang != 'puml' || VPlantUMLMode != 1 || !isLivePreview))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1399,6 +1405,16 @@ var previewCodeBlock = function(id, lang, text, isLivePreview) {
|
|||||||
} else if (lang == 'puml') {
|
} else if (lang == 'puml') {
|
||||||
renderPlantUMLOneOnline(code);
|
renderPlantUMLOneOnline(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isLivePreview) {
|
||||||
|
var children = div.children;
|
||||||
|
if (children.length > 0) {
|
||||||
|
content.previewCodeBlockCB(id, lang, children[0].innerHTML);
|
||||||
|
}
|
||||||
|
|
||||||
|
div.innerHTML = '';
|
||||||
|
div.className = '';
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var setPreviewContent = function(lang, html) {
|
var setPreviewContent = function(lang, html) {
|
||||||
|
@ -16,7 +16,7 @@ renderer.heading = function(text, level) {
|
|||||||
// Highlight.js to highlight code block
|
// Highlight.js to highlight code block
|
||||||
marked.setOptions({
|
marked.setOptions({
|
||||||
highlight: function(code, lang) {
|
highlight: function(code, lang) {
|
||||||
if (lang && !specialCodeBlock(lang)) {
|
if (lang && (!specialCodeBlock(lang) || highlightSpecialBlocks)) {
|
||||||
if (hljs.getLanguage(lang)) {
|
if (hljs.getLanguage(lang)) {
|
||||||
return hljs.highlight(lang, code, true).value;
|
return hljs.highlight(lang, code, true).value;
|
||||||
} else {
|
} else {
|
||||||
@ -78,7 +78,9 @@ var updateText = function(text) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var highlightText = function(text, id, timeStamp) {
|
var highlightText = function(text, id, timeStamp) {
|
||||||
|
highlightSpecialBlocks = true;
|
||||||
var html = marked(text);
|
var html = marked(text);
|
||||||
|
highlightSpecialBlocks = false;
|
||||||
content.highlightTextCB(html, id, timeStamp);
|
content.highlightTextCB(html, id, timeStamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ var highlightText = function(text, id, timeStamp) {
|
|||||||
|
|
||||||
var parser = new DOMParser();
|
var parser = new DOMParser();
|
||||||
var htmlDoc = parser.parseFromString("<div id=\"showdown-container\">" + html + "</div>", 'text/html');
|
var htmlDoc = parser.parseFromString("<div id=\"showdown-container\">" + html + "</div>", 'text/html');
|
||||||
highlightCodeBlocks(htmlDoc, false, false, false);
|
highlightCodeBlocks(htmlDoc, false, false, false, false, false);
|
||||||
|
|
||||||
html = htmlDoc.getElementById('showdown-container').innerHTML;
|
html = htmlDoc.getElementById('showdown-container').innerHTML;
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ var textToHtml = function(text) {
|
|||||||
|
|
||||||
var parser = new DOMParser();
|
var parser = new DOMParser();
|
||||||
var htmlDoc = parser.parseFromString("<div id=\"showdown-container\">" + html + "</div>", 'text/html');
|
var htmlDoc = parser.parseFromString("<div id=\"showdown-container\">" + html + "</div>", 'text/html');
|
||||||
highlightCodeBlocks(htmlDoc, false, false, false);
|
highlightCodeBlocks(htmlDoc, false, false, false, false, false);
|
||||||
|
|
||||||
html = htmlDoc.getElementById('showdown-container').innerHTML;
|
html = htmlDoc.getElementById('showdown-container').innerHTML;
|
||||||
|
|
||||||
|
@ -17,6 +17,13 @@ public:
|
|||||||
VCodeBlockHighlightHelper(HGMarkdownHighlighter *p_highlighter,
|
VCodeBlockHighlightHelper(HGMarkdownHighlighter *p_highlighter,
|
||||||
VDocument *p_vdoc, MarkdownConverterType p_type);
|
VDocument *p_vdoc, MarkdownConverterType p_type);
|
||||||
|
|
||||||
|
// @p_text: text of fenced code block.
|
||||||
|
// Get the indent level of the first line (fence) and unindent the whole block
|
||||||
|
// to make the fence at the highest indent level.
|
||||||
|
// This operation is to make sure JS could handle the code block correctly
|
||||||
|
// without any context.
|
||||||
|
static QString unindentCodeBlock(const QString &p_text);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleCodeBlocksUpdated(const QVector<VCodeBlock> &p_codeBlocks);
|
void handleCodeBlocksUpdated(const QVector<VCodeBlock> &p_codeBlocks);
|
||||||
|
|
||||||
@ -47,13 +54,6 @@ private:
|
|||||||
const QString &p_text, int &p_index,
|
const QString &p_text, int &p_index,
|
||||||
QVector<HLUnitPos> &p_units);
|
QVector<HLUnitPos> &p_units);
|
||||||
|
|
||||||
// @p_text: text of fenced code block.
|
|
||||||
// Get the indent level of the first line (fence) and unindent the whole block
|
|
||||||
// to make the fence at the highest indent level.
|
|
||||||
// This operation is to make sure JS could handle the code block correctly
|
|
||||||
// without any context.
|
|
||||||
QString unindentCodeBlock(const QString &p_text);
|
|
||||||
|
|
||||||
void updateHighlightResults(int p_startPos, QVector<HLUnitPos> p_units);
|
void updateHighlightResults(int p_startPos, QVector<HLUnitPos> p_units);
|
||||||
|
|
||||||
void addToHighlightCache(const QString &p_text,
|
void addToHighlightCache(const QString &p_text,
|
||||||
|
@ -178,3 +178,8 @@ void VDocument::setPreviewContent(const QString &p_lang, const QString &p_html)
|
|||||||
{
|
{
|
||||||
emit requestSetPreviewContent(p_lang, p_html);
|
emit requestSetPreviewContent(p_lang, p_html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VDocument::previewCodeBlockCB(int p_id, const QString &p_lang, const QString &p_html)
|
||||||
|
{
|
||||||
|
emit codeBlockPreviewReady(p_id, p_lang, p_html);
|
||||||
|
}
|
||||||
|
@ -104,6 +104,8 @@ public slots:
|
|||||||
// Web-side call this to process Graphviz locally.
|
// Web-side call this to process Graphviz locally.
|
||||||
void processGraphviz(int p_id, const QString &p_format, const QString &p_text);
|
void processGraphviz(int p_id, const QString &p_format, const QString &p_text);
|
||||||
|
|
||||||
|
void previewCodeBlockCB(int p_id, const QString &p_lang, const QString &p_html);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void textChanged(const QString &text);
|
void textChanged(const QString &text);
|
||||||
|
|
||||||
@ -153,6 +155,8 @@ signals:
|
|||||||
|
|
||||||
void requestSetPreviewContent(const QString &p_lang, const QString &p_html);
|
void requestSetPreviewContent(const QString &p_lang, const QString &p_html);
|
||||||
|
|
||||||
|
void codeBlockPreviewReady(int p_id, const QString &p_lang, const QString &p_html);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_toc;
|
QString m_toc;
|
||||||
QString m_header;
|
QString m_header;
|
||||||
|
@ -159,6 +159,8 @@ public:
|
|||||||
|
|
||||||
virtual QTextDocument *documentW() const = 0;
|
virtual QTextDocument *documentW() const = 0;
|
||||||
|
|
||||||
|
virtual int tabStopWidthW() const = 0;
|
||||||
|
|
||||||
virtual void setTabStopWidthW(int p_width) = 0;
|
virtual void setTabStopWidthW(int p_width) = 0;
|
||||||
|
|
||||||
virtual QTextCursor textCursorW() const = 0;
|
virtual QTextCursor textCursorW() const = 0;
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vgraphvizhelper.h"
|
#include "vgraphvizhelper.h"
|
||||||
#include "vplantumlhelper.h"
|
#include "vplantumlhelper.h"
|
||||||
|
#include "vcodeblockhighlighthelper.h"
|
||||||
|
|
||||||
extern VConfigManager *g_config;
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
@ -22,24 +23,88 @@ extern VConfigManager *g_config;
|
|||||||
|
|
||||||
#define INDEX_MASK 0x00ffffffUL
|
#define INDEX_MASK 0x00ffffffUL
|
||||||
|
|
||||||
|
CodeBlockPreviewInfo::CodeBlockPreviewInfo()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeBlockPreviewInfo::CodeBlockPreviewInfo(const VCodeBlock &p_cb)
|
||||||
|
: m_codeBlock(p_cb)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeBlockPreviewInfo::clearImageData()
|
||||||
|
{
|
||||||
|
m_imgData.clear();
|
||||||
|
m_inplacePreview.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeBlockPreviewInfo::updateNonContent(const QTextDocument *p_doc,
|
||||||
|
const VCodeBlock &p_cb)
|
||||||
|
{
|
||||||
|
m_codeBlock.updateNonContent(p_cb);
|
||||||
|
if (m_inplacePreview.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextBlock block = p_doc->findBlockByNumber(m_codeBlock.m_endBlock);
|
||||||
|
if (block.isValid()) {
|
||||||
|
m_inplacePreview->m_startPos = block.position();
|
||||||
|
m_inplacePreview->m_endPos = block.position() + block.length();
|
||||||
|
m_inplacePreview->m_blockPos = block.position();
|
||||||
|
m_inplacePreview->m_blockNumber = m_codeBlock.m_endBlock;
|
||||||
|
} else {
|
||||||
|
m_inplacePreview->clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update inplace preview according to m_imgData.
|
||||||
|
void CodeBlockPreviewInfo::updateInplacePreview(const VEditor *p_editor,
|
||||||
|
const QTextDocument *p_doc)
|
||||||
|
{
|
||||||
|
QTextBlock block = p_doc->findBlockByNumber(m_codeBlock.m_endBlock);
|
||||||
|
if (block.isValid()) {
|
||||||
|
if (m_inplacePreview.isNull()) {
|
||||||
|
m_inplacePreview.reset(new VImageToPreview());
|
||||||
|
}
|
||||||
|
|
||||||
|
// m_image will be generated when signaling out.
|
||||||
|
m_inplacePreview->m_startPos = block.position();
|
||||||
|
m_inplacePreview->m_endPos = block.position() + block.length();
|
||||||
|
m_inplacePreview->m_blockPos = block.position();
|
||||||
|
m_inplacePreview->m_blockNumber = m_codeBlock.m_endBlock;
|
||||||
|
m_inplacePreview->m_padding = VPreviewManager::calculateBlockMargin(block,
|
||||||
|
p_editor->tabStopWidthW());
|
||||||
|
m_inplacePreview->m_name = QString::number(getImageIndex());
|
||||||
|
m_inplacePreview->m_isBlock = true;
|
||||||
|
} else {
|
||||||
|
m_inplacePreview->clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
VLivePreviewHelper::VLivePreviewHelper(VEditor *p_editor,
|
VLivePreviewHelper::VLivePreviewHelper(VEditor *p_editor,
|
||||||
VDocument *p_document,
|
VDocument *p_document,
|
||||||
QObject *p_parent)
|
QObject *p_parent)
|
||||||
: QObject(p_parent),
|
: QObject(p_parent),
|
||||||
m_editor(p_editor),
|
m_editor(p_editor),
|
||||||
m_document(p_document),
|
m_document(p_document),
|
||||||
|
m_doc(p_editor->documentW()),
|
||||||
m_cbIndex(-1),
|
m_cbIndex(-1),
|
||||||
m_livePreviewEnabled(false),
|
m_livePreviewEnabled(false),
|
||||||
|
m_inplacePreviewEnabled(false),
|
||||||
m_graphvizHelper(NULL),
|
m_graphvizHelper(NULL),
|
||||||
m_plantUMLHelper(NULL)
|
m_plantUMLHelper(NULL)
|
||||||
{
|
{
|
||||||
connect(m_editor->object(), &VEditorObject::cursorPositionChanged,
|
connect(m_editor->object(), &VEditorObject::cursorPositionChanged,
|
||||||
this, &VLivePreviewHelper::handleCursorPositionChanged);
|
this, &VLivePreviewHelper::handleCursorPositionChanged);
|
||||||
|
connect(m_document, &VDocument::codeBlockPreviewReady,
|
||||||
|
this, &VLivePreviewHelper::webAsyncResultReady);
|
||||||
|
|
||||||
m_flowchartEnabled = g_config->getEnableFlowchart();
|
m_flowchartEnabled = g_config->getEnableFlowchart();
|
||||||
m_mermaidEnabled = g_config->getEnableMermaid();
|
m_mermaidEnabled = g_config->getEnableMermaid();
|
||||||
m_plantUMLMode = g_config->getPlantUMLMode();
|
m_plantUMLMode = g_config->getPlantUMLMode();
|
||||||
m_graphvizEnabled = g_config->getEnableGraphviz();
|
m_graphvizEnabled = g_config->getEnableGraphviz();
|
||||||
|
m_mathjaxEnabled = g_config->getEnableMathjax();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VLivePreviewHelper::isPreviewLang(const QString &p_lang) const
|
bool VLivePreviewHelper::isPreviewLang(const QString &p_lang) const
|
||||||
@ -47,12 +112,13 @@ bool VLivePreviewHelper::isPreviewLang(const QString &p_lang) const
|
|||||||
return (m_flowchartEnabled && (p_lang == "flow" || p_lang == "flowchart"))
|
return (m_flowchartEnabled && (p_lang == "flow" || p_lang == "flowchart"))
|
||||||
|| (m_mermaidEnabled && p_lang == "mermaid")
|
|| (m_mermaidEnabled && p_lang == "mermaid")
|
||||||
|| (m_plantUMLMode != PlantUMLMode::DisablePlantUML && p_lang == "puml")
|
|| (m_plantUMLMode != PlantUMLMode::DisablePlantUML && p_lang == "puml")
|
||||||
|| (m_graphvizEnabled && p_lang == "dot");
|
|| (m_graphvizEnabled && p_lang == "dot")
|
||||||
|
|| (m_mathjaxEnabled && p_lang == "mathjax");
|
||||||
}
|
}
|
||||||
|
|
||||||
void VLivePreviewHelper::updateCodeBlocks(const QVector<VCodeBlock> &p_codeBlocks)
|
void VLivePreviewHelper::updateCodeBlocks(const QVector<VCodeBlock> &p_codeBlocks)
|
||||||
{
|
{
|
||||||
if (!m_livePreviewEnabled) {
|
if (!m_livePreviewEnabled && !m_inplacePreviewEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,29 +127,32 @@ void VLivePreviewHelper::updateCodeBlocks(const QVector<VCodeBlock> &p_codeBlock
|
|||||||
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 = true;
|
||||||
int nrCached = 0;
|
for (auto const & vcb : p_codeBlocks) {
|
||||||
for (auto const & cb : p_codeBlocks) {
|
if (!isPreviewLang(vcb.m_lang)) {
|
||||||
if (!isPreviewLang(cb.m_lang)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cached = false;
|
bool cached = false;
|
||||||
if (idx < m_codeBlocks.size()) {
|
if (idx < m_codeBlocks.size()) {
|
||||||
CodeBlock &vcb = m_codeBlocks[idx];
|
CodeBlockPreviewInfo &cb = m_codeBlocks[idx];
|
||||||
if (vcb.m_codeBlock.equalContent(cb)) {
|
if (cb.codeBlock().equalContent(vcb)) {
|
||||||
vcb.m_codeBlock.updateNonContent(cb);
|
cb.updateNonContent(m_doc, vcb);
|
||||||
cached = true;
|
cached = true;
|
||||||
++nrCached;
|
|
||||||
} else {
|
} else {
|
||||||
vcb.m_codeBlock = cb;
|
cb.setCodeBlock(vcb);
|
||||||
vcb.m_cachedResult.clear();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
m_codeBlocks.append(CodeBlock());
|
m_codeBlocks.append(CodeBlockPreviewInfo(vcb));
|
||||||
m_codeBlocks[idx].m_codeBlock = cb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cb.m_startBlock <= cursorBlock && cb.m_endBlock >= cursorBlock) {
|
if (m_inplacePreviewEnabled
|
||||||
|
&& !m_codeBlocks[idx].inplacePreviewReady()) {
|
||||||
|
processForInplacePreview(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_livePreviewEnabled
|
||||||
|
&& vcb.m_startBlock <= cursorBlock
|
||||||
|
&& vcb.m_endBlock >= cursorBlock) {
|
||||||
if (lastIndex == idx && cached) {
|
if (lastIndex == idx && cached) {
|
||||||
needUpdate = false;
|
needUpdate = false;
|
||||||
}
|
}
|
||||||
@ -96,9 +165,7 @@ void VLivePreviewHelper::updateCodeBlocks(const QVector<VCodeBlock> &p_codeBlock
|
|||||||
|
|
||||||
m_codeBlocks.resize(idx);
|
m_codeBlocks.resize(idx);
|
||||||
|
|
||||||
qDebug() << "VLivePreviewHelper cache" << nrCached << "code blocks of" << m_codeBlocks.size();
|
if (m_livePreviewEnabled && needUpdate) {
|
||||||
|
|
||||||
if (needUpdate) {
|
|
||||||
updateLivePreview();
|
updateLivePreview();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,11 +182,11 @@ void VLivePreviewHelper::handleCursorPositionChanged()
|
|||||||
int mid = left;
|
int mid = left;
|
||||||
while (left <= right) {
|
while (left <= right) {
|
||||||
mid = (left + right) / 2;
|
mid = (left + right) / 2;
|
||||||
const CodeBlock &cb = m_codeBlocks[mid];
|
const CodeBlockPreviewInfo &cb = m_codeBlocks[mid];
|
||||||
|
const VCodeBlock &vcb = cb.codeBlock();
|
||||||
if (cb.m_codeBlock.m_startBlock <= cursorBlock && cb.m_codeBlock.m_endBlock >= cursorBlock) {
|
if (vcb.m_startBlock <= cursorBlock && vcb.m_endBlock >= cursorBlock) {
|
||||||
break;
|
break;
|
||||||
} else if (cb.m_codeBlock.m_startBlock > cursorBlock) {
|
} else if (vcb.m_startBlock > cursorBlock) {
|
||||||
right = mid - 1;
|
right = mid - 1;
|
||||||
} else {
|
} else {
|
||||||
left = mid + 1;
|
left = mid + 1;
|
||||||
@ -136,9 +203,10 @@ void VLivePreviewHelper::handleCursorPositionChanged()
|
|||||||
|
|
||||||
static QString removeFence(const QString &p_text)
|
static QString removeFence(const QString &p_text)
|
||||||
{
|
{
|
||||||
Q_ASSERT(p_text.startsWith("```") && p_text.endsWith("```"));
|
QString text = VCodeBlockHighlightHelper::unindentCodeBlock(p_text);
|
||||||
int idx = p_text.indexOf('\n') + 1;
|
Q_ASSERT(text.startsWith("```") && text.endsWith("```"));
|
||||||
return p_text.mid(idx, p_text.size() - idx - 3);
|
int idx = text.indexOf('\n') + 1;
|
||||||
|
return text.mid(idx, text.size() - idx - 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VLivePreviewHelper::updateLivePreview()
|
void VLivePreviewHelper::updateLivePreview()
|
||||||
@ -148,43 +216,41 @@ void VLivePreviewHelper::updateLivePreview()
|
|||||||
}
|
}
|
||||||
|
|
||||||
Q_ASSERT(!(m_cbIndex & ~INDEX_MASK));
|
Q_ASSERT(!(m_cbIndex & ~INDEX_MASK));
|
||||||
|
const CodeBlockPreviewInfo &cb = m_codeBlocks[m_cbIndex];
|
||||||
const CodeBlock &cb = m_codeBlocks[m_cbIndex];
|
const VCodeBlock &vcb = cb.codeBlock();
|
||||||
QString text = removeFence(cb.m_codeBlock.m_text);
|
if (vcb.m_lang == "dot") {
|
||||||
qDebug() << "updateLivePreview" << m_cbIndex << cb.m_codeBlock.m_lang;
|
|
||||||
|
|
||||||
if (cb.m_codeBlock.m_lang == "dot") {
|
|
||||||
if (!m_graphvizHelper) {
|
if (!m_graphvizHelper) {
|
||||||
m_graphvizHelper = new VGraphvizHelper(this);
|
m_graphvizHelper = new VGraphvizHelper(this);
|
||||||
connect(m_graphvizHelper, &VGraphvizHelper::resultReady,
|
connect(m_graphvizHelper, &VGraphvizHelper::resultReady,
|
||||||
this, &VLivePreviewHelper::localAsyncResultReady);
|
this, &VLivePreviewHelper::localAsyncResultReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cb.m_cachedResult.isEmpty()) {
|
if (!cb.hasImageData()) {
|
||||||
m_graphvizHelper->processAsync(m_cbIndex | LANG_PREFIX_GRAPHVIZ | TYPE_LIVE_PREVIEW,
|
m_graphvizHelper->processAsync(m_cbIndex | LANG_PREFIX_GRAPHVIZ | TYPE_LIVE_PREVIEW,
|
||||||
"svg",
|
"svg",
|
||||||
text);
|
removeFence(vcb.m_text));
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "use cached preview result of code block" << m_cbIndex;
|
m_document->setPreviewContent(vcb.m_lang, cb.imageData());
|
||||||
m_document->setPreviewContent(cb.m_codeBlock.m_lang, cb.m_cachedResult);
|
|
||||||
}
|
}
|
||||||
} else if (cb.m_codeBlock.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);
|
||||||
connect(m_plantUMLHelper, &VPlantUMLHelper::resultReady,
|
connect(m_plantUMLHelper, &VPlantUMLHelper::resultReady,
|
||||||
this, &VLivePreviewHelper::localAsyncResultReady);
|
this, &VLivePreviewHelper::localAsyncResultReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cb.m_cachedResult.isEmpty()) {
|
if (!cb.hasImageData()) {
|
||||||
m_plantUMLHelper->processAsync(m_cbIndex | LANG_PREFIX_PLANTUML | TYPE_LIVE_PREVIEW,
|
m_plantUMLHelper->processAsync(m_cbIndex | LANG_PREFIX_PLANTUML | TYPE_LIVE_PREVIEW,
|
||||||
"svg",
|
"svg",
|
||||||
text);
|
removeFence(vcb.m_text));
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "use cached preview result of code block" << m_cbIndex;
|
m_document->setPreviewContent(vcb.m_lang, cb.imageData());
|
||||||
m_document->setPreviewContent(cb.m_codeBlock.m_lang, cb.m_cachedResult);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else if (vcb.m_lang != "puml") {
|
||||||
m_document->previewCodeBlock(m_cbIndex, cb.m_codeBlock.m_lang, text, true);
|
m_document->previewCodeBlock(m_cbIndex,
|
||||||
|
vcb.m_lang,
|
||||||
|
removeFence(vcb.m_text),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,6 +268,20 @@ void VLivePreviewHelper::setLivePreviewEnabled(bool p_enabled)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VLivePreviewHelper::setInplacePreviewEnabled(bool p_enabled)
|
||||||
|
{
|
||||||
|
if (m_inplacePreviewEnabled == p_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_inplacePreviewEnabled = p_enabled;
|
||||||
|
if (!m_livePreviewEnabled) {
|
||||||
|
for (auto & cb : m_codeBlocks) {
|
||||||
|
cb.clearImageData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void VLivePreviewHelper::localAsyncResultReady(int p_id,
|
void VLivePreviewHelper::localAsyncResultReady(int p_id,
|
||||||
const QString &p_format,
|
const QString &p_format,
|
||||||
const QString &p_result)
|
const QString &p_result)
|
||||||
@ -211,6 +291,7 @@ void VLivePreviewHelper::localAsyncResultReady(int p_id,
|
|||||||
int idx = p_id & INDEX_MASK;
|
int idx = p_id & INDEX_MASK;
|
||||||
bool livePreview = (p_id & TYPE_MASK) == TYPE_LIVE_PREVIEW;
|
bool livePreview = (p_id & TYPE_MASK) == TYPE_LIVE_PREVIEW;
|
||||||
QString lang;
|
QString lang;
|
||||||
|
|
||||||
switch (p_id & LANG_PREFIX_MASK) {
|
switch (p_id & LANG_PREFIX_MASK) {
|
||||||
case LANG_PREFIX_PLANTUML:
|
case LANG_PREFIX_PLANTUML:
|
||||||
lang = "puml";
|
lang = "puml";
|
||||||
@ -224,12 +305,105 @@ void VLivePreviewHelper::localAsyncResultReady(int p_id,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (idx >= m_codeBlocks.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeBlockPreviewInfo &cb = m_codeBlocks[idx];
|
||||||
|
cb.setImageData(p_format, p_result);
|
||||||
if (livePreview) {
|
if (livePreview) {
|
||||||
if (idx != m_cbIndex) {
|
if (idx != m_cbIndex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_codeBlocks[idx].m_cachedResult = p_result;
|
|
||||||
m_document->setPreviewContent(lang, p_result);
|
m_document->setPreviewContent(lang, p_result);
|
||||||
|
} else {
|
||||||
|
// Inplace preview.
|
||||||
|
cb.updateInplacePreview(m_editor, m_doc);
|
||||||
|
|
||||||
|
updateInplacePreview();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VLivePreviewHelper::processForInplacePreview(int p_idx)
|
||||||
|
{
|
||||||
|
CodeBlockPreviewInfo &cb = m_codeBlocks[p_idx];
|
||||||
|
const VCodeBlock &vcb = cb.codeBlock();
|
||||||
|
if (vcb.m_lang == "dot") {
|
||||||
|
if (!m_graphvizHelper) {
|
||||||
|
m_graphvizHelper = new VGraphvizHelper(this);
|
||||||
|
connect(m_graphvizHelper, &VGraphvizHelper::resultReady,
|
||||||
|
this, &VLivePreviewHelper::localAsyncResultReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb.hasImageData()) {
|
||||||
|
cb.updateInplacePreview(m_editor, m_doc);
|
||||||
|
updateInplacePreview();
|
||||||
|
} else {
|
||||||
|
m_graphvizHelper->processAsync(p_idx | LANG_PREFIX_GRAPHVIZ | TYPE_INPLACE_PREVIEW,
|
||||||
|
"svg",
|
||||||
|
removeFence(vcb.m_text));
|
||||||
|
}
|
||||||
|
} else if (vcb.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.hasImageData()) {
|
||||||
|
cb.updateInplacePreview(m_editor, m_doc);
|
||||||
|
updateInplacePreview();
|
||||||
|
} else {
|
||||||
|
m_plantUMLHelper->processAsync(p_idx | LANG_PREFIX_PLANTUML | TYPE_INPLACE_PREVIEW,
|
||||||
|
"svg",
|
||||||
|
removeFence(vcb.m_text));
|
||||||
|
}
|
||||||
|
} else if (vcb.m_lang == "flow"
|
||||||
|
|| vcb.m_lang == "flowchart") {
|
||||||
|
m_document->previewCodeBlock(p_idx,
|
||||||
|
vcb.m_lang,
|
||||||
|
removeFence(vcb.m_text),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VLivePreviewHelper::updateInplacePreview()
|
||||||
|
{
|
||||||
|
QVector<QSharedPointer<VImageToPreview> > images;
|
||||||
|
for (int i = 0; i < m_codeBlocks.size(); ++i) {
|
||||||
|
CodeBlockPreviewInfo &cb = m_codeBlocks[i];
|
||||||
|
if (cb.inplacePreviewReady() && cb.hasImageData()) {
|
||||||
|
Q_ASSERT(!cb.inplacePreview().isNull());
|
||||||
|
// Generate the image.
|
||||||
|
cb.inplacePreview()->m_image.loadFromData(cb.imageData().toUtf8(),
|
||||||
|
cb.imageFormat().toLocal8Bit().data());
|
||||||
|
images.append(cb.inplacePreview());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit inplacePreviewCodeBlockUpdated(images);
|
||||||
|
|
||||||
|
// Clear image.
|
||||||
|
for (int i = 0; i < m_codeBlocks.size(); ++i) {
|
||||||
|
CodeBlockPreviewInfo &cb = m_codeBlocks[i];
|
||||||
|
if (cb.inplacePreviewReady() && cb.hasImageData()) {
|
||||||
|
cb.inplacePreview()->m_image = QPixmap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VLivePreviewHelper::webAsyncResultReady(int p_id,
|
||||||
|
const QString &p_lang,
|
||||||
|
const QString &p_result)
|
||||||
|
{
|
||||||
|
Q_UNUSED(p_lang);
|
||||||
|
if (p_id >= m_codeBlocks.size() || p_result.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeBlockPreviewInfo &cb = m_codeBlocks[p_id];
|
||||||
|
cb.setImageData(QStringLiteral("svg"), p_result);
|
||||||
|
cb.updateInplacePreview(m_editor, m_doc);
|
||||||
|
updateInplacePreview();
|
||||||
|
}
|
||||||
|
@ -2,14 +2,95 @@
|
|||||||
#define VLIVEPREVIEWHELPER_H
|
#define VLIVEPREVIEWHELPER_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QTextDocument>
|
||||||
|
|
||||||
#include "hgmarkdownhighlighter.h"
|
#include "hgmarkdownhighlighter.h"
|
||||||
|
#include "vpreviewmanager.h"
|
||||||
|
|
||||||
class VEditor;
|
class VEditor;
|
||||||
class VDocument;
|
class VDocument;
|
||||||
class VGraphvizHelper;
|
class VGraphvizHelper;
|
||||||
class VPlantUMLHelper;
|
class VPlantUMLHelper;
|
||||||
|
|
||||||
|
class CodeBlockPreviewInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CodeBlockPreviewInfo();
|
||||||
|
|
||||||
|
explicit CodeBlockPreviewInfo(const VCodeBlock &p_cb);
|
||||||
|
|
||||||
|
void clearImageData();
|
||||||
|
|
||||||
|
void updateNonContent(const QTextDocument *p_doc, const VCodeBlock &p_cb);
|
||||||
|
|
||||||
|
void updateInplacePreview(const VEditor *p_editor, const QTextDocument *p_doc);
|
||||||
|
|
||||||
|
VCodeBlock &codeBlock()
|
||||||
|
{
|
||||||
|
return m_codeBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VCodeBlock &codeBlock() const
|
||||||
|
{
|
||||||
|
return m_codeBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCodeBlock(const VCodeBlock &p_cb)
|
||||||
|
{
|
||||||
|
m_codeBlock = p_cb;
|
||||||
|
|
||||||
|
clearImageData();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inplacePreviewReady() const
|
||||||
|
{
|
||||||
|
return !m_inplacePreview.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasImageData() const
|
||||||
|
{
|
||||||
|
return !m_imgData.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &imageData() const
|
||||||
|
{
|
||||||
|
return m_imgData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &imageFormat() const
|
||||||
|
{
|
||||||
|
return m_imgFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setImageData(const QString &p_format, const QString &p_data)
|
||||||
|
{
|
||||||
|
m_imgFormat = p_format;
|
||||||
|
m_imgData = p_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QSharedPointer<VImageToPreview> inplacePreview() const
|
||||||
|
{
|
||||||
|
return m_inplacePreview;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int getImageIndex()
|
||||||
|
{
|
||||||
|
static int index = 0;
|
||||||
|
return ++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
VCodeBlock m_codeBlock;
|
||||||
|
|
||||||
|
QString m_imgData;
|
||||||
|
|
||||||
|
QString m_imgFormat;
|
||||||
|
|
||||||
|
QSharedPointer<VImageToPreview> m_inplacePreview;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Manage live preview and inplace preview.
|
||||||
class VLivePreviewHelper : public QObject
|
class VLivePreviewHelper : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -22,30 +103,42 @@ public:
|
|||||||
|
|
||||||
void setLivePreviewEnabled(bool p_enabled);
|
void setLivePreviewEnabled(bool p_enabled);
|
||||||
|
|
||||||
|
void setInplacePreviewEnabled(bool p_enabled);
|
||||||
|
|
||||||
|
bool isPreviewEnabled() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateCodeBlocks(const QVector<VCodeBlock> &p_codeBlocks);
|
void updateCodeBlocks(const QVector<VCodeBlock> &p_codeBlocks);
|
||||||
|
|
||||||
|
void webAsyncResultReady(int p_id, const QString &p_lang, const QString &p_result);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void inplacePreviewCodeBlockUpdated(const QVector<QSharedPointer<VImageToPreview> > &p_images);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleCursorPositionChanged();
|
void handleCursorPositionChanged();
|
||||||
|
|
||||||
void localAsyncResultReady(int p_id, const QString &p_format, const QString &p_result);
|
void localAsyncResultReady(int p_id, const QString &p_format, const QString &p_result);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool isPreviewLang(const QString &p_lang) const;
|
bool isPreviewLang(const QString &p_lang) const;
|
||||||
|
|
||||||
struct CodeBlock
|
// Get image data for this code block for inplace preview.
|
||||||
{
|
void processForInplacePreview(int p_idx);
|
||||||
VCodeBlock m_codeBlock;
|
|
||||||
QString m_cachedResult;
|
// Emit signal to update inplace preview.
|
||||||
};
|
void updateInplacePreview();
|
||||||
|
|
||||||
// Sorted by m_startBlock in ascending order.
|
// Sorted by m_startBlock in ascending order.
|
||||||
QVector<CodeBlock> m_codeBlocks;
|
QVector<CodeBlockPreviewInfo> m_codeBlocks;
|
||||||
|
|
||||||
VEditor *m_editor;
|
VEditor *m_editor;
|
||||||
|
|
||||||
VDocument *m_document;
|
VDocument *m_document;
|
||||||
|
|
||||||
|
QTextDocument *m_doc;
|
||||||
|
|
||||||
// Current previewed code block index in m_codeBlocks.
|
// Current previewed code block index in m_codeBlocks.
|
||||||
int m_cbIndex;
|
int m_cbIndex;
|
||||||
|
|
||||||
@ -53,11 +146,18 @@ private:
|
|||||||
bool m_mermaidEnabled;
|
bool m_mermaidEnabled;
|
||||||
int m_plantUMLMode;
|
int m_plantUMLMode;
|
||||||
bool m_graphvizEnabled;
|
bool m_graphvizEnabled;
|
||||||
|
bool m_mathjaxEnabled;
|
||||||
|
|
||||||
bool m_livePreviewEnabled;
|
bool m_livePreviewEnabled;
|
||||||
|
|
||||||
|
bool m_inplacePreviewEnabled;
|
||||||
|
|
||||||
VGraphvizHelper *m_graphvizHelper;
|
VGraphvizHelper *m_graphvizHelper;
|
||||||
VPlantUMLHelper *m_plantUMLHelper;
|
VPlantUMLHelper *m_plantUMLHelper;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline bool VLivePreviewHelper::isPreviewEnabled() const
|
||||||
|
{
|
||||||
|
return m_inplacePreviewEnabled || m_livePreviewEnabled;
|
||||||
|
}
|
||||||
#endif // VLIVEPREVIEWHELPER_H
|
#endif // VLIVEPREVIEWHELPER_H
|
||||||
|
@ -85,7 +85,7 @@ VMdEditor::VMdEditor(VFile *p_file,
|
|||||||
|
|
||||||
m_previewMgr = new VPreviewManager(this, m_mdHighlighter);
|
m_previewMgr = new VPreviewManager(this, m_mdHighlighter);
|
||||||
connect(m_mdHighlighter, &HGMarkdownHighlighter::imageLinksUpdated,
|
connect(m_mdHighlighter, &HGMarkdownHighlighter::imageLinksUpdated,
|
||||||
m_previewMgr, &VPreviewManager::imageLinksUpdated);
|
m_previewMgr, &VPreviewManager::updateImageLinks);
|
||||||
connect(m_previewMgr, &VPreviewManager::requestUpdateImageLinks,
|
connect(m_previewMgr, &VPreviewManager::requestUpdateImageLinks,
|
||||||
m_mdHighlighter, &HGMarkdownHighlighter::updateHighlight);
|
m_mdHighlighter, &HGMarkdownHighlighter::updateHighlight);
|
||||||
|
|
||||||
|
@ -74,6 +74,8 @@ public:
|
|||||||
|
|
||||||
HGMarkdownHighlighter *getMarkdownHighlighter() const;
|
HGMarkdownHighlighter *getMarkdownHighlighter() const;
|
||||||
|
|
||||||
|
VPreviewManager *getPreviewManager() 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;
|
||||||
|
|
||||||
@ -91,6 +93,11 @@ public:
|
|||||||
return document();
|
return document();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int tabStopWidthW() const Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
return tabStopWidth();
|
||||||
|
}
|
||||||
|
|
||||||
void setTabStopWidthW(int p_width) Q_DECL_OVERRIDE
|
void setTabStopWidthW(int p_width) Q_DECL_OVERRIDE
|
||||||
{
|
{
|
||||||
setTabStopWidth(p_width);
|
setTabStopWidth(p_width);
|
||||||
@ -273,4 +280,9 @@ inline HGMarkdownHighlighter *VMdEditor::getMarkdownHighlighter() const
|
|||||||
{
|
{
|
||||||
return m_mdHighlighter;
|
return m_mdHighlighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline VPreviewManager *VMdEditor::getPreviewManager() const
|
||||||
|
{
|
||||||
|
return m_previewMgr;
|
||||||
|
}
|
||||||
#endif // VMDEDITOR_H
|
#endif // VMDEDITOR_H
|
||||||
|
@ -515,6 +515,15 @@ void VMdTab::setupMarkdownEditor()
|
|||||||
enableHeadingSequence(m_enableHeadingSequence);
|
enableHeadingSequence(m_enableHeadingSequence);
|
||||||
m_editor->reloadFile();
|
m_editor->reloadFile();
|
||||||
m_splitter->insertWidget(0, m_editor);
|
m_splitter->insertWidget(0, m_editor);
|
||||||
|
|
||||||
|
m_livePreviewHelper = new VLivePreviewHelper(m_editor, m_document, this);
|
||||||
|
connect(m_editor->getMarkdownHighlighter(), &HGMarkdownHighlighter::codeBlocksUpdated,
|
||||||
|
m_livePreviewHelper, &VLivePreviewHelper::updateCodeBlocks);
|
||||||
|
connect(m_editor->getPreviewManager(), &VPreviewManager::previewEnabledChanged,
|
||||||
|
m_livePreviewHelper, &VLivePreviewHelper::setInplacePreviewEnabled);
|
||||||
|
connect(m_livePreviewHelper, &VLivePreviewHelper::inplacePreviewCodeBlockUpdated,
|
||||||
|
m_editor->getPreviewManager(), &VPreviewManager::updateCodeBlocks);
|
||||||
|
m_livePreviewHelper->setInplacePreviewEnabled(m_editor->getPreviewManager()->isPreviewEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdTab::updateOutlineFromHtml(const QString &p_tocHtml)
|
void VMdTab::updateOutlineFromHtml(const QString &p_tocHtml)
|
||||||
@ -1019,6 +1028,13 @@ void VMdTab::tabIsReady(TabReady p_mode)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_editor
|
||||||
|
&& p_mode == TabReady::ReadMode
|
||||||
|
&& m_livePreviewHelper->isPreviewEnabled()) {
|
||||||
|
// Need to re-preview.
|
||||||
|
m_editor->getMarkdownHighlighter()->updateHighlight();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdTab::writeBackupFile()
|
void VMdTab::writeBackupFile()
|
||||||
@ -1375,11 +1391,6 @@ void VMdTab::setCurrentMode(Mode p_mode)
|
|||||||
newSizes.append(a);
|
newSizes.append(a);
|
||||||
newSizes.append(b);
|
newSizes.append(b);
|
||||||
m_splitter->setSizes(newSizes);
|
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) {
|
} else if (factor != m_previewWebViewState->m_zoomFactor) {
|
||||||
m_webViewer->setZoomFactor(m_previewWebViewState->m_zoomFactor);
|
m_webViewer->setZoomFactor(m_previewWebViewState->m_zoomFactor);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
#include <QTextLayout>
|
||||||
|
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
#include "vdownloader.h"
|
#include "vdownloader.h"
|
||||||
@ -17,24 +19,25 @@ VPreviewManager::VPreviewManager(VMdEditor *p_editor, HGMarkdownHighlighter *p_h
|
|||||||
m_editor(p_editor),
|
m_editor(p_editor),
|
||||||
m_document(p_editor->document()),
|
m_document(p_editor->document()),
|
||||||
m_highlighter(p_highlighter),
|
m_highlighter(p_highlighter),
|
||||||
m_previewEnabled(false),
|
m_previewEnabled(false)
|
||||||
m_timeStamp(0)
|
|
||||||
{
|
{
|
||||||
|
for (int i = 0; i < (int)PreviewSource::MaxNumberOfSources; ++i) {
|
||||||
|
m_timeStamps[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
m_downloader = new VDownloader(this);
|
m_downloader = new VDownloader(this);
|
||||||
connect(m_downloader, &VDownloader::downloadFinished,
|
connect(m_downloader, &VDownloader::downloadFinished,
|
||||||
this, &VPreviewManager::imageDownloaded);
|
this, &VPreviewManager::imageDownloaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VPreviewManager::imageLinksUpdated(const QVector<VElementRegion> &p_imageRegions)
|
void VPreviewManager::updateImageLinks(const QVector<VElementRegion> &p_imageRegions)
|
||||||
{
|
{
|
||||||
if (!m_previewEnabled) {
|
if (!m_previewEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TS ts = ++m_timeStamp;
|
TS ts = ++timeStamp(PreviewSource::ImageLink);
|
||||||
m_imageRegions = p_imageRegions;
|
previewImages(ts, p_imageRegions);
|
||||||
|
|
||||||
previewImages(ts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VPreviewManager::imageDownloaded(const QByteArray &p_data, const QString &p_url)
|
void VPreviewManager::imageDownloaded(const QByteArray &p_data, const QString &p_url)
|
||||||
@ -70,6 +73,8 @@ void VPreviewManager::setPreviewEnabled(bool p_enabled)
|
|||||||
if (m_previewEnabled != p_enabled) {
|
if (m_previewEnabled != p_enabled) {
|
||||||
m_previewEnabled = p_enabled;
|
m_previewEnabled = p_enabled;
|
||||||
|
|
||||||
|
emit previewEnabledChanged(p_enabled);
|
||||||
|
|
||||||
if (!m_previewEnabled) {
|
if (!m_previewEnabled) {
|
||||||
clearPreview();
|
clearPreview();
|
||||||
} else {
|
} else {
|
||||||
@ -80,21 +85,17 @@ void VPreviewManager::setPreviewEnabled(bool p_enabled)
|
|||||||
|
|
||||||
void VPreviewManager::clearPreview()
|
void VPreviewManager::clearPreview()
|
||||||
{
|
{
|
||||||
m_imageRegions.clear();
|
|
||||||
|
|
||||||
long long ts = ++m_timeStamp;
|
|
||||||
|
|
||||||
for (int i = 0; i < (int)PreviewSource::MaxNumberOfSources; ++i) {
|
for (int i = 0; i < (int)PreviewSource::MaxNumberOfSources; ++i) {
|
||||||
|
TS ts = ++timeStamp(static_cast<PreviewSource>(i));
|
||||||
clearBlockObsoletePreviewInfo(ts, static_cast<PreviewSource>(i));
|
clearBlockObsoletePreviewInfo(ts, static_cast<PreviewSource>(i));
|
||||||
|
|
||||||
clearObsoleteImages(ts, static_cast<PreviewSource>(i));
|
clearObsoleteImages(ts, static_cast<PreviewSource>(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VPreviewManager::previewImages(TS p_timeStamp)
|
void VPreviewManager::previewImages(TS p_timeStamp, const QVector<VElementRegion> &p_imageRegions)
|
||||||
{
|
{
|
||||||
QVector<ImageLinkInfo> imageLinks;
|
QVector<ImageLinkInfo> imageLinks;
|
||||||
fetchImageLinksFromRegions(imageLinks);
|
fetchImageLinksFromRegions(p_imageRegions, imageLinks);
|
||||||
|
|
||||||
updateBlockPreviewInfo(p_timeStamp, imageLinks);
|
updateBlockPreviewInfo(p_timeStamp, imageLinks);
|
||||||
|
|
||||||
@ -116,20 +117,21 @@ static bool isAllSpaces(const QString &p_text, int p_start, int p_end)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VPreviewManager::fetchImageLinksFromRegions(QVector<ImageLinkInfo> &p_imageLinks)
|
void VPreviewManager::fetchImageLinksFromRegions(QVector<VElementRegion> p_imageRegions,
|
||||||
|
QVector<ImageLinkInfo> &p_imageLinks)
|
||||||
{
|
{
|
||||||
p_imageLinks.clear();
|
p_imageLinks.clear();
|
||||||
|
|
||||||
if (m_imageRegions.isEmpty()) {
|
if (p_imageRegions.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
p_imageLinks.reserve(m_imageRegions.size());
|
p_imageLinks.reserve(p_imageRegions.size());
|
||||||
|
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->document();
|
||||||
|
|
||||||
for (int i = 0; i < m_imageRegions.size(); ++i) {
|
for (int i = 0; i < p_imageRegions.size(); ++i) {
|
||||||
VElementRegion ® = m_imageRegions[i];
|
VElementRegion ® = p_imageRegions[i];
|
||||||
QTextBlock block = doc->findBlock(reg.m_startPos);
|
QTextBlock block = doc->findBlock(reg.m_startPos);
|
||||||
if (!block.isValid()) {
|
if (!block.isValid()) {
|
||||||
continue;
|
continue;
|
||||||
@ -143,7 +145,7 @@ void VPreviewManager::fetchImageLinksFromRegions(QVector<ImageLinkInfo> &p_image
|
|||||||
reg.m_endPos,
|
reg.m_endPos,
|
||||||
blockStart,
|
blockStart,
|
||||||
block.blockNumber(),
|
block.blockNumber(),
|
||||||
calculateBlockMargin(block));
|
calculateBlockMargin(block, m_editor->tabStopWidthW()));
|
||||||
if ((reg.m_startPos == blockStart
|
if ((reg.m_startPos == blockStart
|
||||||
|| isAllSpaces(text, 0, reg.m_startPos - blockStart))
|
|| isAllSpaces(text, 0, reg.m_startPos - blockStart))
|
||||||
&& (reg.m_endPos == blockEnd
|
&& (reg.m_endPos == blockEnd
|
||||||
@ -256,7 +258,23 @@ QString VPreviewManager::imageResourceName(const ImageLinkInfo &p_link)
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VPreviewManager::calculateBlockMargin(const QTextBlock &p_block)
|
QString VPreviewManager::imageResourceNameFromCodeBlock(const QSharedPointer<VImageToPreview> &p_image)
|
||||||
|
{
|
||||||
|
QString name = "CODE_BLOCK_" + p_image->m_name;
|
||||||
|
if (m_editor->containsImage(name)) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add it to the resource.
|
||||||
|
if (p_image->m_image.isNull()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_editor->addImage(name, p_image->m_image);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
int VPreviewManager::calculateBlockMargin(const QTextBlock &p_block, int p_tabStopWidth)
|
||||||
{
|
{
|
||||||
static QHash<QString, int> spaceWidthOfFonts;
|
static QHash<QString, int> spaceWidthOfFonts;
|
||||||
|
|
||||||
@ -272,7 +290,7 @@ int VPreviewManager::calculateBlockMargin(const QTextBlock &p_block)
|
|||||||
} else if (text[i] == ' ') {
|
} else if (text[i] == ' ') {
|
||||||
++nrSpaces;
|
++nrSpaces;
|
||||||
} else if (text[i] == '\t') {
|
} else if (text[i] == '\t') {
|
||||||
nrSpaces += m_editor->tabStopWidth();
|
nrSpaces += p_tabStopWidth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,7 +299,14 @@ int VPreviewManager::calculateBlockMargin(const QTextBlock &p_block)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int spaceWidth = 0;
|
int spaceWidth = 0;
|
||||||
QFont font = p_block.charFormat().font();
|
QFont font;
|
||||||
|
QVector<QTextLayout::FormatRange> fmts = p_block.layout()->formats();
|
||||||
|
if (fmts.isEmpty()) {
|
||||||
|
font = p_block.charFormat().font();
|
||||||
|
} else {
|
||||||
|
font = fmts.first().format.font();
|
||||||
|
}
|
||||||
|
|
||||||
QString fontName = font.toString();
|
QString fontName = font.toString();
|
||||||
auto it = spaceWidthOfFonts.find(fontName);
|
auto it = spaceWidthOfFonts.find(fontName);
|
||||||
if (it != spaceWidthOfFonts.end()) {
|
if (it != spaceWidthOfFonts.end()) {
|
||||||
@ -327,11 +352,57 @@ void VPreviewManager::updateBlockPreviewInfo(TS p_timeStamp,
|
|||||||
<< imageCache(PreviewSource::ImageLink).size()
|
<< imageCache(PreviewSource::ImageLink).size()
|
||||||
<< blockData->toString();
|
<< blockData->toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: may need to call m_editor->update()?
|
||||||
|
}
|
||||||
|
|
||||||
|
void VPreviewManager::updateBlockPreviewInfo(TS p_timeStamp,
|
||||||
|
PreviewSource p_source,
|
||||||
|
const QVector<QSharedPointer<VImageToPreview> > &p_images)
|
||||||
|
{
|
||||||
|
QSet<int> affectedBlocks;
|
||||||
|
for (auto const & img : p_images) {
|
||||||
|
if (img.isNull()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextBlock block = m_document->findBlockByNumber(img->m_blockNumber);
|
||||||
|
if (!block.isValid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString name = imageResourceNameFromCodeBlock(img);
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
VTextBlockData *blockData = dynamic_cast<VTextBlockData *>(block.userData());
|
||||||
|
Q_ASSERT(blockData);
|
||||||
|
VPreviewInfo *info = new VPreviewInfo(p_source,
|
||||||
|
p_timeStamp,
|
||||||
|
img->m_startPos - img->m_blockPos,
|
||||||
|
img->m_endPos - img->m_blockPos,
|
||||||
|
img->m_padding,
|
||||||
|
!img->m_isBlock,
|
||||||
|
name,
|
||||||
|
m_editor->imageSize(name));
|
||||||
|
bool tsUpdated = blockData->insertPreviewInfo(info);
|
||||||
|
imageCache(p_source).insert(name, p_timeStamp);
|
||||||
|
if (!tsUpdated) {
|
||||||
|
// No need to relayout the block if only timestamp is updated.
|
||||||
|
affectedBlocks.insert(img->m_blockNumber);
|
||||||
|
m_highlighter->addPossiblePreviewBlock(img->m_blockNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relayout these blocks since they may not have been changed.
|
||||||
|
m_editor->relayout(affectedBlocks);
|
||||||
|
m_editor->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VPreviewManager::clearObsoleteImages(long long p_timeStamp, PreviewSource p_source)
|
void VPreviewManager::clearObsoleteImages(long long p_timeStamp, PreviewSource p_source)
|
||||||
{
|
{
|
||||||
auto cache = imageCache(p_source);
|
QHash<QString, long long> &cache = imageCache(p_source);
|
||||||
|
|
||||||
for (auto it = cache.begin(); it != cache.end();) {
|
for (auto it = cache.begin(); it != cache.end();) {
|
||||||
if (it.value() < p_timeStamp) {
|
if (it.value() < p_timeStamp) {
|
||||||
@ -348,7 +419,7 @@ void VPreviewManager::clearBlockObsoletePreviewInfo(long long p_timeStamp,
|
|||||||
{
|
{
|
||||||
QSet<int> affectedBlocks;
|
QSet<int> affectedBlocks;
|
||||||
QVector<int> obsoleteBlocks;
|
QVector<int> obsoleteBlocks;
|
||||||
auto blocks = m_highlighter->getPossiblePreviewBlocks();
|
const QSet<int> &blocks = m_highlighter->getPossiblePreviewBlocks();
|
||||||
qDebug() << "possible preview blocks" << blocks;
|
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);
|
||||||
@ -384,5 +455,21 @@ void VPreviewManager::refreshPreview()
|
|||||||
|
|
||||||
clearPreview();
|
clearPreview();
|
||||||
|
|
||||||
|
// No need to request updating code blocks since this will also update them.
|
||||||
requestUpdateImageLinks();
|
requestUpdateImageLinks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VPreviewManager::updateCodeBlocks(const QVector<QSharedPointer<VImageToPreview> > &p_images)
|
||||||
|
{
|
||||||
|
if (!m_previewEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TS ts = ++timeStamp(PreviewSource::CodeBlock);
|
||||||
|
|
||||||
|
updateBlockPreviewInfo(ts, PreviewSource::CodeBlock, p_images);
|
||||||
|
|
||||||
|
clearBlockObsoletePreviewInfo(ts, PreviewSource::CodeBlock);
|
||||||
|
|
||||||
|
clearObsoleteImages(ts, PreviewSource::CodeBlock);
|
||||||
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include <QTextBlock>
|
#include <QTextBlock>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
#include "hgmarkdownhighlighter.h"
|
#include "hgmarkdownhighlighter.h"
|
||||||
#include "vmdeditor.h"
|
#include "vmdeditor.h"
|
||||||
#include "vtextblockdata.h"
|
#include "vtextblockdata.h"
|
||||||
@ -14,7 +16,41 @@ class VDownloader;
|
|||||||
|
|
||||||
typedef long long TS;
|
typedef long long TS;
|
||||||
|
|
||||||
|
// Info about image to preview.
|
||||||
|
struct VImageToPreview
|
||||||
|
{
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_startPos = m_endPos = m_blockPos = m_blockNumber = -1;
|
||||||
|
m_padding = 0;
|
||||||
|
m_image = QPixmap();
|
||||||
|
m_name.clear();
|
||||||
|
m_isBlock = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
int m_startPos;
|
||||||
|
|
||||||
|
int m_endPos;
|
||||||
|
|
||||||
|
// Position of this block.
|
||||||
|
int m_blockPos;
|
||||||
|
|
||||||
|
int m_blockNumber;
|
||||||
|
|
||||||
|
// Left padding of this block in pixels.
|
||||||
|
int m_padding;
|
||||||
|
|
||||||
|
QPixmap m_image;
|
||||||
|
|
||||||
|
// If @m_name are the same, then they are the same imges.
|
||||||
|
QString m_name;
|
||||||
|
|
||||||
|
// Whether it is an image block.
|
||||||
|
bool m_isBlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Manage inplace preview.
|
||||||
class VPreviewManager : public QObject
|
class VPreviewManager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -29,14 +65,23 @@ public:
|
|||||||
// Refresh all the preview.
|
// Refresh all the preview.
|
||||||
void refreshPreview();
|
void refreshPreview();
|
||||||
|
|
||||||
|
bool isPreviewEnabled() const;
|
||||||
|
|
||||||
|
// Calculate the block margin (prefix spaces) in pixels.
|
||||||
|
static int calculateBlockMargin(const QTextBlock &p_block, int p_tabStopWidth);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
// Image links were updated from the highlighter.
|
// Image links were updated from the highlighter.
|
||||||
void imageLinksUpdated(const QVector<VElementRegion> &p_imageRegions);
|
void updateImageLinks(const QVector<VElementRegion> &p_imageRegions);
|
||||||
|
|
||||||
|
void updateCodeBlocks(const QVector<QSharedPointer<VImageToPreview> > &p_images);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
// Request highlighter to update image links.
|
// Request highlighter to update image links.
|
||||||
void requestUpdateImageLinks();
|
void requestUpdateImageLinks();
|
||||||
|
|
||||||
|
void previewEnabledChanged(bool p_enabled);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// Non-local image downloaded for preview.
|
// Non-local image downloaded for preview.
|
||||||
void imageDownloaded(const QByteArray &p_data, const QString &p_url);
|
void imageDownloaded(const QByteArray &p_data, const QString &p_url);
|
||||||
@ -92,11 +137,12 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Start to preview images according to image links.
|
// Start to preview images according to image links.
|
||||||
void previewImages(TS p_timeStamp);
|
void previewImages(TS p_timeStamp, const QVector<VElementRegion> &p_imageRegions);
|
||||||
|
|
||||||
// According to m_imageRegions, fetch the image link Url.
|
// According to p_imageRegions, fetch the image link Url.
|
||||||
// @p_imageRegions: output.
|
// @p_imageRegions: output.
|
||||||
void fetchImageLinksFromRegions(QVector<ImageLinkInfo> &p_imageLinks);
|
void fetchImageLinksFromRegions(QVector<VElementRegion> p_imageRegions,
|
||||||
|
QVector<ImageLinkInfo> &p_imageLinks);
|
||||||
|
|
||||||
// Fetch the image link's URL if there is only one link.
|
// Fetch the image link's URL if there is only one link.
|
||||||
QString fetchImageUrlToPreview(const QString &p_text);
|
QString fetchImageUrlToPreview(const QString &p_text);
|
||||||
@ -108,13 +154,17 @@ private:
|
|||||||
// Update the preview info of related blocks according to @p_imageLinks.
|
// Update the preview info of related blocks according to @p_imageLinks.
|
||||||
void updateBlockPreviewInfo(TS p_timeStamp, const QVector<ImageLinkInfo> &p_imageLinks);
|
void updateBlockPreviewInfo(TS p_timeStamp, const QVector<ImageLinkInfo> &p_imageLinks);
|
||||||
|
|
||||||
|
// Update the preview info of related blocks according to @p_images.
|
||||||
|
void updateBlockPreviewInfo(TS p_timeStamp,
|
||||||
|
PreviewSource p_source,
|
||||||
|
const QVector<QSharedPointer<VImageToPreview> > &p_images);
|
||||||
|
|
||||||
// Get the name of the image in the resource manager.
|
// Get the name of the image in the resource manager.
|
||||||
// Will add the image to the resource manager if not exists.
|
// Will add the image to the resource manager if not exists.
|
||||||
// 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);
|
||||||
|
|
||||||
// Calculate the block margin (prefix spaces) in pixels.
|
QString imageResourceNameFromCodeBlock(const QSharedPointer<VImageToPreview> &p_image);
|
||||||
int calculateBlockMargin(const QTextBlock &p_block);
|
|
||||||
|
|
||||||
QHash<QString, long long> &imageCache(PreviewSource p_source);
|
QHash<QString, long long> &imageCache(PreviewSource p_source);
|
||||||
|
|
||||||
@ -122,6 +172,8 @@ private:
|
|||||||
|
|
||||||
void clearBlockObsoletePreviewInfo(long long p_timeStamp, PreviewSource p_source);
|
void clearBlockObsoletePreviewInfo(long long p_timeStamp, PreviewSource p_source);
|
||||||
|
|
||||||
|
TS &timeStamp(PreviewSource p_source);
|
||||||
|
|
||||||
VMdEditor *m_editor;
|
VMdEditor *m_editor;
|
||||||
|
|
||||||
QTextDocument *m_document;
|
QTextDocument *m_document;
|
||||||
@ -133,14 +185,12 @@ private:
|
|||||||
// Whether preview is enabled.
|
// Whether preview is enabled.
|
||||||
bool m_previewEnabled;
|
bool m_previewEnabled;
|
||||||
|
|
||||||
// Regions of all the image links.
|
|
||||||
QVector<VElementRegion> m_imageRegions;
|
|
||||||
|
|
||||||
// Map from URL to name in the resource manager.
|
// Map from URL to name in the resource manager.
|
||||||
// Used for downloading images.
|
// Used for downloading images.
|
||||||
QHash<QString, QString> m_urlToName;
|
QHash<QString, QString> m_urlToName;
|
||||||
|
|
||||||
TS m_timeStamp;
|
// Timestamp per each preview source.
|
||||||
|
TS m_timeStamps[(int)PreviewSource::MaxNumberOfSources];
|
||||||
|
|
||||||
// Used to discard obsolete images. One per each preview source.
|
// Used to discard obsolete images. One per each preview source.
|
||||||
QHash<QString, long long> m_imageCaches[(int)PreviewSource::MaxNumberOfSources];
|
QHash<QString, long long> m_imageCaches[(int)PreviewSource::MaxNumberOfSources];
|
||||||
@ -150,4 +200,14 @@ inline QHash<QString, long long> &VPreviewManager::imageCache(PreviewSource p_so
|
|||||||
{
|
{
|
||||||
return m_imageCaches[(int)p_source];
|
return m_imageCaches[(int)p_source];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline TS &VPreviewManager::timeStamp(PreviewSource p_source)
|
||||||
|
{
|
||||||
|
return m_timeStamps[(int)p_source];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool VPreviewManager::isPreviewEnabled() const
|
||||||
|
{
|
||||||
|
return m_previewEnabled;
|
||||||
|
}
|
||||||
#endif // VPREVIEWMANAGER_H
|
#endif // VPREVIEWMANAGER_H
|
||||||
|
@ -17,8 +17,9 @@ VTextBlockData::~VTextBlockData()
|
|||||||
m_previews.clear();
|
m_previews.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VTextBlockData::insertPreviewInfo(VPreviewInfo *p_info)
|
bool VTextBlockData::insertPreviewInfo(VPreviewInfo *p_info)
|
||||||
{
|
{
|
||||||
|
bool tsUpdated = false;
|
||||||
bool inserted = false;
|
bool inserted = false;
|
||||||
for (auto it = m_previews.begin(); it != m_previews.end();) {
|
for (auto it = m_previews.begin(); it != m_previews.end();) {
|
||||||
VPreviewInfo *ele = *it;
|
VPreviewInfo *ele = *it;
|
||||||
@ -33,6 +34,7 @@ void VTextBlockData::insertPreviewInfo(VPreviewInfo *p_info)
|
|||||||
delete ele;
|
delete ele;
|
||||||
*it = p_info;
|
*it = p_info;
|
||||||
inserted = true;
|
inserted = true;
|
||||||
|
tsUpdated = true;
|
||||||
qDebug() << "update eixsting image's timestamp" << p_info->m_imageInfo.toString();
|
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)) {
|
||||||
@ -53,6 +55,8 @@ void VTextBlockData::insertPreviewInfo(VPreviewInfo *p_info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Q_ASSERT(checkOrder());
|
Q_ASSERT(checkOrder());
|
||||||
|
|
||||||
|
return tsUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString VTextBlockData::toString() const
|
QString VTextBlockData::toString() const
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
enum class PreviewSource
|
enum class PreviewSource
|
||||||
{
|
{
|
||||||
ImageLink = 0,
|
ImageLink = 0,
|
||||||
|
CodeBlock,
|
||||||
MaxNumberOfSources
|
MaxNumberOfSources
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -137,7 +138,8 @@ public:
|
|||||||
~VTextBlockData();
|
~VTextBlockData();
|
||||||
|
|
||||||
// Insert @p_info into m_previews, preserving the order.
|
// Insert @p_info into m_previews, preserving the order.
|
||||||
void insertPreviewInfo(VPreviewInfo *p_info);
|
// Returns true if only timestamp is updated.
|
||||||
|
bool insertPreviewInfo(VPreviewInfo *p_info);
|
||||||
|
|
||||||
// For degub only.
|
// For degub only.
|
||||||
QString toString() const;
|
QString toString() const;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user