mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
LivePreview: smart live preview
This commit is contained in:
parent
13fb03bd11
commit
cda48a612a
@ -190,7 +190,9 @@ new QWebChannel(qt.webChannelTransport,
|
|||||||
content.requestPreviewEnabled.connect(setPreviewEnabled);
|
content.requestPreviewEnabled.connect(setPreviewEnabled);
|
||||||
|
|
||||||
content.requestPreviewCodeBlock.connect(previewCodeBlock);
|
content.requestPreviewCodeBlock.connect(previewCodeBlock);
|
||||||
|
|
||||||
content.requestSetPreviewContent.connect(setPreviewContent);
|
content.requestSetPreviewContent.connect(setPreviewContent);
|
||||||
|
content.requestPerformSmartLivePreview.connect(performSmartLivePreview);
|
||||||
|
|
||||||
if (typeof updateHtml == "function") {
|
if (typeof updateHtml == "function") {
|
||||||
updateHtml(content.html);
|
updateHtml(content.html);
|
||||||
@ -1577,3 +1579,77 @@ var htmlToText = function(identifier, id, timeStamp, html) {
|
|||||||
var markdown = ts.turndown(html);
|
var markdown = ts.turndown(html);
|
||||||
content.htmlToTextCB(identifier, id, timeStamp, markdown);
|
content.htmlToTextCB(identifier, id, timeStamp, markdown);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var performSmartLivePreview = function(lang, text) {
|
||||||
|
if (previewDiv.style.display == 'none') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lang != 'puml') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlantUML.
|
||||||
|
var targetNode = findNodeWithText(previewDiv, new RegExp(text));
|
||||||
|
if (!targetNode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (left, top) is relative to the viewport.
|
||||||
|
// Should add window.scrollX and window.scrollY to get the real content offset.
|
||||||
|
var trect = targetNode.getBoundingClientRect();
|
||||||
|
|
||||||
|
var vrect = {
|
||||||
|
left: document.documentElement.scrollLeft || document.body.scrollLeft || window.pageXOffset,
|
||||||
|
top: document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset,
|
||||||
|
width: document.documentElement.clientWidth || document.body.clientWidth,
|
||||||
|
height: document.documentElement.clientHeight || document.body.clientHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
var dx = 0, dy = 0;
|
||||||
|
|
||||||
|
// If target is already in, do not scroll.
|
||||||
|
if (trect.left < 0
|
||||||
|
|| trect.left + trect.width > vrect.width) {
|
||||||
|
if (trect.width >= vrect.width) {
|
||||||
|
dx = trect.left;
|
||||||
|
} else {
|
||||||
|
dx = trect.left - (vrect.width - trect.width) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trect.top < 0
|
||||||
|
|| trect.top + trect.height > vrect.height) {
|
||||||
|
if (trect.height >= vrect.height) {
|
||||||
|
dy = trect.top;
|
||||||
|
} else {
|
||||||
|
dy = trect.top - (vrect.height - trect.width) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.scrollBy(dx, dy);
|
||||||
|
}
|
||||||
|
|
||||||
|
var findNodeWithText = function(node, reg) {
|
||||||
|
var children = node.children;
|
||||||
|
if (children.length == 0) {
|
||||||
|
if (reg.test(node.textContent)) {
|
||||||
|
return node;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < children.length; ++i) {
|
||||||
|
var ret = findNodeWithText(children[i], reg);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reg.test(node.textContent)) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
@ -245,6 +245,9 @@ max_tag_label_length=10
|
|||||||
; Max number of tag labels to display
|
; Max number of tag labels to display
|
||||||
max_num_of_tag_labels=3
|
max_num_of_tag_labels=3
|
||||||
|
|
||||||
|
; Smart live preview
|
||||||
|
smart_live_preview=true
|
||||||
|
|
||||||
[editor]
|
[editor]
|
||||||
; Auto indent as previous line
|
; Auto indent as previous line
|
||||||
auto_indent=true
|
auto_indent=true
|
||||||
|
@ -311,6 +311,9 @@ void VConfigManager::initialize()
|
|||||||
m_maxNumOfTagLabels = getConfigFromSettings("global",
|
m_maxNumOfTagLabels = getConfigFromSettings("global",
|
||||||
"max_num_of_tag_labels").toInt();
|
"max_num_of_tag_labels").toInt();
|
||||||
|
|
||||||
|
m_smartLivePreview = getConfigFromSettings("global",
|
||||||
|
"smart_live_preview").toBool();
|
||||||
|
|
||||||
initEditorConfigs();
|
initEditorConfigs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,6 +547,8 @@ public:
|
|||||||
|
|
||||||
QChar getVimLeaderKey() const;
|
QChar getVimLeaderKey() const;
|
||||||
|
|
||||||
|
bool getSmartLivePreview() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Look up a config from user and default settings.
|
// Look up a config from user and default settings.
|
||||||
QVariant getConfigFromSettings(const QString §ion, const QString &key) const;
|
QVariant getConfigFromSettings(const QString §ion, const QString &key) const;
|
||||||
@ -984,6 +986,9 @@ private:
|
|||||||
// Vim leader key.
|
// Vim leader key.
|
||||||
QChar m_vimLeaderKey;
|
QChar m_vimLeaderKey;
|
||||||
|
|
||||||
|
// Smart live preview.
|
||||||
|
bool m_smartLivePreview;
|
||||||
|
|
||||||
// The name of the config file in each directory.
|
// The name of the config file in each directory.
|
||||||
static const QString c_dirConfigFile;
|
static const QString c_dirConfigFile;
|
||||||
|
|
||||||
@ -2539,4 +2544,9 @@ inline QChar VConfigManager::getVimLeaderKey() const
|
|||||||
{
|
{
|
||||||
return m_vimLeaderKey;
|
return m_vimLeaderKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool VConfigManager::getSmartLivePreview() const
|
||||||
|
{
|
||||||
|
return m_smartLivePreview;
|
||||||
|
}
|
||||||
#endif // VCONFIGMANAGER_H
|
#endif // VCONFIGMANAGER_H
|
||||||
|
@ -202,3 +202,10 @@ void VDocument::previewCodeBlockCB(int p_id, const QString &p_lang, const QStrin
|
|||||||
{
|
{
|
||||||
emit codeBlockPreviewReady(p_id, p_lang, p_html);
|
emit codeBlockPreviewReady(p_id, p_lang, p_html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VDocument::performSmartLivePreview(const QString &p_lang, const QString &p_text)
|
||||||
|
{
|
||||||
|
if (!p_text.isEmpty()) {
|
||||||
|
emit requestPerformSmartLivePreview(p_lang, p_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -75,6 +75,8 @@ public:
|
|||||||
|
|
||||||
void muteWebView(bool p_muted);
|
void muteWebView(bool p_muted);
|
||||||
|
|
||||||
|
void performSmartLivePreview(const QString &p_lang, const QString &p_text);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
// Will be called in the HTML side
|
// Will be called in the HTML side
|
||||||
|
|
||||||
@ -192,6 +194,8 @@ signals:
|
|||||||
|
|
||||||
void requestMuted(bool p_muted);
|
void requestMuted(bool p_muted);
|
||||||
|
|
||||||
|
void requestPerformSmartLivePreview(const QString &p_lang, const QString &p_text);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_toc;
|
QString m_toc;
|
||||||
QString m_header;
|
QString m_header;
|
||||||
|
@ -95,7 +95,7 @@ VLivePreviewHelper::VLivePreviewHelper(VEditor *p_editor,
|
|||||||
{
|
{
|
||||||
m_livePreviewTimer = new QTimer(this);
|
m_livePreviewTimer = new QTimer(this);
|
||||||
m_livePreviewTimer->setSingleShot(true);
|
m_livePreviewTimer->setSingleShot(true);
|
||||||
m_livePreviewTimer->setInterval(100);
|
m_livePreviewTimer->setInterval(500);
|
||||||
connect(m_livePreviewTimer, &QTimer::timeout,
|
connect(m_livePreviewTimer, &QTimer::timeout,
|
||||||
this, &VLivePreviewHelper::handleCursorPositionChanged);
|
this, &VLivePreviewHelper::handleCursorPositionChanged);
|
||||||
|
|
||||||
@ -214,6 +214,7 @@ void VLivePreviewHelper::updateCodeBlocks(TimeStamp p_timeStamp, const QVector<V
|
|||||||
|
|
||||||
if (needUpdate) {
|
if (needUpdate) {
|
||||||
updateLivePreview();
|
updateLivePreview();
|
||||||
|
performSmartLivePreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
clearObsoleteCache();
|
clearObsoleteCache();
|
||||||
@ -227,33 +228,33 @@ void VLivePreviewHelper::handleCursorPositionChanged()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int cursorBlock = m_editor->textCursorW().block().blockNumber();
|
int cursorBlock = m_editor->textCursorW().block().blockNumber();
|
||||||
if (m_lastCursorBlock == cursorBlock) {
|
if (m_lastCursorBlock != cursorBlock) {
|
||||||
return;
|
m_lastCursorBlock = cursorBlock;
|
||||||
}
|
|
||||||
|
|
||||||
m_lastCursorBlock = cursorBlock;
|
int left = 0, right = m_codeBlocks.size() - 1;
|
||||||
|
int mid = left;
|
||||||
|
while (left <= right) {
|
||||||
|
mid = (left + right) / 2;
|
||||||
|
const CodeBlockPreviewInfo &cb = m_codeBlocks[mid];
|
||||||
|
const VCodeBlock &vcb = cb.codeBlock();
|
||||||
|
if (vcb.m_startBlock <= cursorBlock && vcb.m_endBlock >= cursorBlock) {
|
||||||
|
break;
|
||||||
|
} else if (vcb.m_startBlock > cursorBlock) {
|
||||||
|
right = mid - 1;
|
||||||
|
} else {
|
||||||
|
left = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int left = 0, right = m_codeBlocks.size() - 1;
|
if (left <= right) {
|
||||||
int mid = left;
|
if (m_cbIndex != mid) {
|
||||||
while (left <= right) {
|
m_cbIndex = mid;
|
||||||
mid = (left + right) / 2;
|
updateLivePreview();
|
||||||
const CodeBlockPreviewInfo &cb = m_codeBlocks[mid];
|
}
|
||||||
const VCodeBlock &vcb = cb.codeBlock();
|
|
||||||
if (vcb.m_startBlock <= cursorBlock && vcb.m_endBlock >= cursorBlock) {
|
|
||||||
break;
|
|
||||||
} else if (vcb.m_startBlock > cursorBlock) {
|
|
||||||
right = mid - 1;
|
|
||||||
} else {
|
|
||||||
left = mid + 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (left <= right) {
|
performSmartLivePreview();
|
||||||
if (m_cbIndex != mid) {
|
|
||||||
m_cbIndex = mid;
|
|
||||||
updateLivePreview();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VLivePreviewHelper::updateLivePreview()
|
void VLivePreviewHelper::updateLivePreview()
|
||||||
@ -396,6 +397,7 @@ void VLivePreviewHelper::localAsyncResultReady(int p_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_document->setPreviewContent(lang, p_result);
|
m_document->setPreviewContent(lang, p_result);
|
||||||
|
performSmartLivePreview();
|
||||||
} else {
|
} else {
|
||||||
// Inplace preview.
|
// Inplace preview.
|
||||||
updateInplacePreview();
|
updateInplacePreview();
|
||||||
@ -523,3 +525,27 @@ void VLivePreviewHelper::clearObsoleteCache()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VLivePreviewHelper::performSmartLivePreview()
|
||||||
|
{
|
||||||
|
if (m_cbIndex < 0
|
||||||
|
|| m_cbIndex >= m_codeBlocks.size()
|
||||||
|
|| !g_config->getSmartLivePreview()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CodeBlockPreviewInfo &cb = m_codeBlocks[m_cbIndex];
|
||||||
|
const VCodeBlock &vcb = cb.codeBlock();
|
||||||
|
const QTextBlock block = m_editor->textCursorW().block();
|
||||||
|
if (block.blockNumber() <= vcb.m_startBlock
|
||||||
|
|| block.blockNumber() >= vcb.m_endBlock) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString keyword;
|
||||||
|
if (vcb.m_lang == "puml" && m_plantUMLMode == PlantUMLMode::LocalPlantUML) {
|
||||||
|
keyword = VPlantUMLHelper::keywordForSmartLivePreview(block.text());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_document->performSmartLivePreview(vcb.m_lang, keyword);
|
||||||
|
}
|
||||||
|
@ -233,6 +233,8 @@ private:
|
|||||||
|
|
||||||
void clearObsoleteCache();
|
void clearObsoleteCache();
|
||||||
|
|
||||||
|
void performSmartLivePreview();
|
||||||
|
|
||||||
// Sorted by m_startBlock in ascending order.
|
// Sorted by m_startBlock in ascending order.
|
||||||
QVector<CodeBlockPreviewInfo> m_codeBlocks;
|
QVector<CodeBlockPreviewInfo> m_codeBlocks;
|
||||||
|
|
||||||
|
@ -188,3 +188,40 @@ QByteArray VPlantUMLHelper::process(const QString &p_format, const QString &p_te
|
|||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool tryClassDiagram(QString &p_keyword)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
// class ABC #Pink {
|
||||||
|
QRegExp classDef1("class\\s*(\\w+)\\s*.*");
|
||||||
|
if (classDef1.indexIn(p_keyword) >= 0) {
|
||||||
|
p_keyword = classDef1.cap(1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// class "ABC DEF" as AD #Pink {
|
||||||
|
QRegExp classDef2("class\\s*\"([^\"]+)\"\\s*.*");
|
||||||
|
if (classDef2.indexIn(p_keyword) >= 0) {
|
||||||
|
p_keyword = classDef2.cap(1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString VPlantUMLHelper::keywordForSmartLivePreview(const QString &p_text)
|
||||||
|
{
|
||||||
|
QString kw = p_text.trimmed();
|
||||||
|
if (kw.isEmpty()) {
|
||||||
|
return kw;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tryClassDiagram(kw)) {
|
||||||
|
return kw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kw;
|
||||||
|
}
|
||||||
|
@ -23,6 +23,8 @@ public:
|
|||||||
|
|
||||||
static QByteArray process(const QString &p_format, const QString &p_text);
|
static QByteArray process(const QString &p_format, const QString &p_text);
|
||||||
|
|
||||||
|
static QString keywordForSmartLivePreview(const QString &p_text);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void resultReady(int p_id,
|
void resultReady(int p_id,
|
||||||
TimeStamp p_timeStamp,
|
TimeStamp p_timeStamp,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user