From dc0a9b1a7dfd243a601f19b98f92960c4dd2749d Mon Sep 17 00:00:00 2001 From: Le Tan Date: Wed, 3 Nov 2021 07:45:06 +0800 Subject: [PATCH] MarkdownEditor: add option for image align center --- src/core/configmgr.cpp | 2 +- src/core/htmltemplatehelper.cpp | 2 + src/core/htmltemplatehelper.h | 2 + src/core/markdowneditorconfig.cpp | 12 +++ src/core/markdowneditorconfig.h | 5 + src/data/core/vnotex.json | 4 +- src/data/extra/extra.qrc | 1 + src/data/extra/web/css/globalstyles.css | 24 +++++ src/data/extra/web/js/markdown-it/README.md | 3 + .../markdown-it-implicit-figure.js | 93 +++++++++++++++++++ src/data/extra/web/js/markdownit.js | 4 + src/data/extra/web/js/vnotex.js | 2 + .../dialogs/settings/markdowneditorpage.cpp | 14 +++ .../dialogs/settings/markdowneditorpage.h | 2 + 14 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 src/data/extra/web/js/markdown-it/markdown-it-implicit-figure.js diff --git a/src/core/configmgr.cpp b/src/core/configmgr.cpp index b98b7b43..53e640fe 100644 --- a/src/core/configmgr.cpp +++ b/src/core/configmgr.cpp @@ -25,7 +25,7 @@ using namespace vnotex; #ifndef QT_NO_DEBUG - // #define VX_DEBUG_WEB + #define VX_DEBUG_WEB #endif const QString ConfigMgr::c_orgName = "VNote"; diff --git a/src/core/htmltemplatehelper.cpp b/src/core/htmltemplatehelper.cpp index e2ef4650..17d151d3 100644 --- a/src/core/htmltemplatehelper.cpp +++ b/src/core/htmltemplatehelper.cpp @@ -22,6 +22,7 @@ QString WebGlobalOptions::toJavascriptObject() const + QString("webPlantUml: %1,\n").arg(Utils::boolToString(m_webPlantUml)) + QString("webGraphviz: %1,\n").arg(Utils::boolToString(m_webGraphviz)) + QString("constrainImageWidthEnabled: %1,\n").arg(Utils::boolToString(m_constrainImageWidthEnabled)) + + QString("imageAlignCenterEnabled: %1,\n").arg(Utils::boolToString(m_imageAlignCenterEnabled)) + QString("protectFromXss: %1,\n").arg(Utils::boolToString(m_protectFromXss)) + QString("htmlTagEnabled: %1,\n").arg(Utils::boolToString(m_htmlTagEnabled)) + QString("autoBreakEnabled: %1,\n").arg(Utils::boolToString(m_autoBreakEnabled)) @@ -214,6 +215,7 @@ QString HtmlTemplateHelper::generateMarkdownViewerTemplate(const MarkdownEditorC opts.m_sectionNumberEnabled = p_config.getSectionNumberMode() == MarkdownEditorConfig::SectionNumberMode::Read; opts.m_sectionNumberBaseLevel = p_config.getSectionNumberBaseLevel(); opts.m_constrainImageWidthEnabled = p_config.getConstrainImageWidthEnabled(); + opts.m_imageAlignCenterEnabled = p_config.getImageAlignCenterEnabled(); opts.m_protectFromXss = p_config.getProtectFromXss(); opts.m_htmlTagEnabled = p_config.getHtmlTagEnabled(); opts.m_autoBreakEnabled = p_config.getAutoBreakEnabled(); diff --git a/src/core/htmltemplatehelper.h b/src/core/htmltemplatehelper.h index 6f91ff29..dc9ee111 100644 --- a/src/core/htmltemplatehelper.h +++ b/src/core/htmltemplatehelper.h @@ -21,6 +21,8 @@ namespace vnotex bool m_constrainImageWidthEnabled = true; + bool m_imageAlignCenterEnabled = true; + bool m_protectFromXss = false; bool m_htmlTagEnabled = true; diff --git a/src/core/markdowneditorconfig.cpp b/src/core/markdowneditorconfig.cpp index 827d6719..b52e4a24 100644 --- a/src/core/markdowneditorconfig.cpp +++ b/src/core/markdowneditorconfig.cpp @@ -48,6 +48,7 @@ void MarkdownEditorConfig::init(const QJsonObject &p_app, const QJsonObject &p_u m_sectionNumberStyle = stringToSectionNumberStyle(READSTR(QStringLiteral("section_number_style"))); m_constrainImageWidthEnabled = READBOOL(QStringLiteral("constrain_image_width")); + m_imageAlignCenterEnabled = READBOOL(QStringLiteral("image_align_center")); m_constrainInplacePreviewWidthEnabled = READBOOL(QStringLiteral("constrain_inplace_preview_width")); m_zoomFactorInReadMode = READREAL(QStringLiteral("zoom_factor_in_read_mode")); m_fetchImagesInParseAndPaste = READBOOL(QStringLiteral("fetch_images_in_parse_and_paste")); @@ -93,6 +94,7 @@ QJsonObject MarkdownEditorConfig::toJson() const obj[QStringLiteral("section_number_style")] = sectionNumberStyleToString(m_sectionNumberStyle); obj[QStringLiteral("constrain_image_width")] = m_constrainImageWidthEnabled; + obj[QStringLiteral("image_align_center")] = m_imageAlignCenterEnabled; obj[QStringLiteral("constrain_inplace_preview_width")] = m_constrainInplacePreviewWidthEnabled; obj[QStringLiteral("zoom_factor_in_read_mode")] = m_zoomFactorInReadMode; obj[QStringLiteral("fetch_images_in_parse_and_paste")] = m_fetchImagesInParseAndPaste; @@ -280,6 +282,16 @@ void MarkdownEditorConfig::setConstrainImageWidthEnabled(bool p_enabled) updateConfig(m_constrainImageWidthEnabled, p_enabled, this); } +bool MarkdownEditorConfig::getImageAlignCenterEnabled() const +{ + return m_imageAlignCenterEnabled; +} + +void MarkdownEditorConfig::setImageAlignCenterEnabled(bool p_enabled) +{ + updateConfig(m_imageAlignCenterEnabled, p_enabled, this); +} + bool MarkdownEditorConfig::getConstrainInplacePreviewWidthEnabled() const { return m_constrainInplacePreviewWidthEnabled; diff --git a/src/core/markdowneditorconfig.h b/src/core/markdowneditorconfig.h index 47987b48..025fc19f 100644 --- a/src/core/markdowneditorconfig.h +++ b/src/core/markdowneditorconfig.h @@ -90,6 +90,9 @@ namespace vnotex bool getConstrainImageWidthEnabled() const; void setConstrainImageWidthEnabled(bool p_enabled); + bool getImageAlignCenterEnabled() const; + void setImageAlignCenterEnabled(bool p_enabled); + bool getConstrainInplacePreviewWidthEnabled() const; void setConstrainInplacePreviewWidthEnabled(bool p_enabled); @@ -187,6 +190,8 @@ namespace vnotex // Whether enable image width constraint. bool m_constrainImageWidthEnabled = true; + bool m_imageAlignCenterEnabled = true; + // Whether enable in-place preview width constraint. bool m_constrainInplacePreviewWidthEnabled = false; diff --git a/src/data/core/vnotex.json b/src/data/core/vnotex.json index 7fcc7c94..cd8ab0ee 100644 --- a/src/data/core/vnotex.json +++ b/src/data/core/vnotex.json @@ -144,7 +144,7 @@ "spell_check": false }, "markdown_editor" : { - "override_viewer_resource" : false, + "override_viewer_resource" : true, "viewer_resource" : { "template" : "web/markdown-viewer-template.html", "resources" : [ @@ -203,6 +203,7 @@ "web/js/markdown-it/markdown-it-inject-linenumbers.js", "web/js/markdown-it/markdownItAnchor.umd.js", "web/js/markdown-it/markdownItTocDoneRight.umd.js", + "web/js/markdown-it/markdown-it-implicit-figure.js", "web/js/markdownit.js" ], "styles" : [ @@ -328,6 +329,7 @@ "section_number_style" : "digdotdigdot", "//comment" : "Whether enable image width constraint", "constrain_image_width" : true, + "image_align_center" : true, "//comment" : "Whether enable in-place preview width constraint", "constrain_inplace_preview_width" : false, "//comment" : "Zoom factor in read mode", diff --git a/src/data/extra/extra.qrc b/src/data/extra/extra.qrc index 6a8338a7..5666b499 100644 --- a/src/data/extra/extra.qrc +++ b/src/data/extra/extra.qrc @@ -49,6 +49,7 @@ web/js/markdown-it/markdown-it-texmath.js web/js/markdown-it/markdown-it-inject-linenumbers.js web/js/markdown-it/markdown-it-xss.min.js + web/js/markdown-it/markdown-it-implicit-figure.js web/js/markdown-it/markdown-it.min.js web/js/markdownit.js web/js/mermaid/mermaid.min.js diff --git a/src/data/extra/web/css/globalstyles.css b/src/data/extra/web/css/globalstyles.css index a4f94436..fcb111c6 100644 --- a/src/data/extra/web/css/globalstyles.css +++ b/src/data/extra/web/css/globalstyles.css @@ -82,6 +82,30 @@ height: auto !important; } +figure { + margin: auto; +} + +figcaption { + font-style: italic; +} + +#vx-content.vx-image-align-center figure { + text-align: center; +} + +#vx-content.vx-image-align-center figcaption { + text-align: center; +} + +#vx-content.vx-image-align-center div.vx-plantuml-graph, +#vx-content.vx-image-align-center div.vx-mermaid-graph, +#vx-content.vx-image-align-center div.vx-flowchartjs-graph, +#vx-content.vx-image-align-center div.vx-wavedrom-graph { + margin-left: auto; + margin-right: auto; +} + /* Table of Contents */ .vx-table-of-contents ol { list-style: none; diff --git a/src/data/extra/web/js/markdown-it/README.md b/src/data/extra/web/js/markdown-it/README.md index aea4444a..9cceedfd 100644 --- a/src/data/extra/web/js/markdown-it/README.md +++ b/src/data/extra/web/js/markdown-it/README.md @@ -53,3 +53,6 @@ v6.0.1 # [markdonw-it-toc-done-right](https://github.com/nagaozen/markdown-it-toc-done-right) v4.2.0 + +# [markdown-it-implicit-figures](https://github.com/arve0/markdown-it-implicit-figures) +v0.10.0 diff --git a/src/data/extra/web/js/markdown-it/markdown-it-implicit-figure.js b/src/data/extra/web/js/markdown-it/markdown-it-implicit-figure.js new file mode 100644 index 00000000..cd6dd730 --- /dev/null +++ b/src/data/extra/web/js/markdown-it/markdown-it-implicit-figure.js @@ -0,0 +1,93 @@ +/* https://github.com/arve0/markdown-it-implicit-figures @c709117 */ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.markdownitImplicitFigure = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o image -> link_close + if (!token.children || (token.children.length !== 1 && token.children.length !== 3)) { continue; } + // one child, should be img + if (token.children.length === 1 && token.children[0].type !== 'image') { continue; } + // three children, should be image enclosed in link + if (token.children.length === 3 && + (token.children[0].type !== 'link_open' || + token.children[1].type !== 'image' || + token.children[2].type !== 'link_close')) { + continue; + } + // prev token is paragraph open + if (i !== 0 && state.tokens[i - 1].type !== 'paragraph_open') { continue; } + // next token is paragraph close + if (i !== (l - 1) && state.tokens[i + 1].type !== 'paragraph_close') { continue; } + + // We have inline token containing an image only. + // Previous token is paragraph open. + // Next token is paragraph close. + // Lets replace the paragraph tokens with figure tokens. + var figure = state.tokens[i - 1]; + figure.type = 'figure_open'; + figure.tag = 'figure'; + state.tokens[i + 1].type = 'figure_close'; + state.tokens[i + 1].tag = 'figure'; + + if (options.dataType == true) { + state.tokens[i - 1].attrPush(['data-type', 'image']); + } + var image; + + if (options.link == true && token.children.length === 1) { + image = token.children[0]; + token.children.unshift( + new state.Token('link_open', 'a', 1) + ); + token.children[0].attrPush(['href', image.attrGet('src')]); + token.children.push( + new state.Token('link_close', 'a', -1) + ); + } + + // for linked images, image is one off + image = token.children.length === 1 ? token.children[0] : token.children[1]; + + if (options.figcaption == true) { + if (image.children && image.children.length) { + token.children.push( + new state.Token('figcaption_open', 'figcaption', 1) + ); + token.children.splice(token.children.length, 0, ...image.children); + token.children.push( + new state.Token('figcaption_close', 'figcaption', -1) + ); + image.children.length = 0; + } + } + + if (options.copyAttrs && image.attrs) { + const f = options.copyAttrs === true ? '' : options.copyAttrs + figure.attrs = image.attrs.filter(([k,v]) => k.match(f)) + } + + if (options.tabindex == true) { + // add a tabindex property + // you could use this with css-tricks.com/expanding-images-html5 + state.tokens[i - 1].attrPush(['tabindex', tabIndex]); + tabIndex++; + } + } + } + md.core.ruler.before('linkify', 'implicit_figures', implicitFigures); +}; + + +},{}]},{},[1])(1) +}); diff --git a/src/data/extra/web/js/markdownit.js b/src/data/extra/web/js/markdownit.js index 3b07b546..c24d3993 100644 --- a/src/data/extra/web/js/markdownit.js +++ b/src/data/extra/web/js/markdownit.js @@ -237,6 +237,10 @@ class MarkdownIt extends VxWorker { }, containerClass: 'vx-table-of-contents' }); + + this.mdit.use(window.markdownitImplicitFigure, { + figcaption: true + }); } registerInternal() { diff --git a/src/data/extra/web/js/vnotex.js b/src/data/extra/web/js/vnotex.js index 48db9e1d..87e77f38 100644 --- a/src/data/extra/web/js/vnotex.js +++ b/src/data/extra/web/js/vnotex.js @@ -65,6 +65,8 @@ class VNoteX extends EventEmitter { this.setContentContainerOption('vx-constrain-image-width', window.vxOptions.constrainImageWidthEnabled || !window.vxOptions.scrollable); + this.setContentContainerOption('vx-image-align-center', + window.vxOptions.imageAlignCenterEnabled); this.setContentContainerOption('vx-indent-first-line', window.vxOptions.indentFirstLineEnabled); this.setBodyOption('vx-transparent-background', diff --git a/src/widgets/dialogs/settings/markdowneditorpage.cpp b/src/widgets/dialogs/settings/markdowneditorpage.cpp index a14f59e1..ba9f859a 100644 --- a/src/widgets/dialogs/settings/markdowneditorpage.cpp +++ b/src/widgets/dialogs/settings/markdowneditorpage.cpp @@ -67,6 +67,8 @@ void MarkdownEditorPage::loadInternal() m_constrainImageWidthCheckBox->setChecked(markdownConfig.getConstrainImageWidthEnabled()); + m_imageAlignCenterCheckBox->setChecked(markdownConfig.getImageAlignCenterEnabled()); + m_zoomFactorSpinBox->setValue(markdownConfig.getZoomFactorInReadMode()); m_constrainInplacePreviewWidthCheckBox->setChecked(markdownConfig.getConstrainInplacePreviewWidthEnabled()); @@ -139,6 +141,8 @@ bool MarkdownEditorPage::saveInternal() markdownConfig.setConstrainImageWidthEnabled(m_constrainImageWidthCheckBox->isChecked()); + markdownConfig.setImageAlignCenterEnabled(m_imageAlignCenterCheckBox->isChecked()); + markdownConfig.setZoomFactorInReadMode(m_zoomFactorSpinBox->value()); markdownConfig.setConstrainInplacePreviewWidthEnabled(m_constrainInplacePreviewWidthCheckBox->isChecked()); @@ -210,6 +214,16 @@ QGroupBox *MarkdownEditorPage::setupReadGroup() this, &MarkdownEditorPage::pageIsChanged); } + { + const QString label(tr("Center image")); + m_imageAlignCenterCheckBox = WidgetsFactory::createCheckBox(label, box); + m_imageAlignCenterCheckBox->setToolTip(tr("Center images")); + layout->addRow(m_imageAlignCenterCheckBox); + addSearchItem(label, m_imageAlignCenterCheckBox->toolTip(), m_imageAlignCenterCheckBox); + connect(m_imageAlignCenterCheckBox, &QCheckBox::stateChanged, + this, &MarkdownEditorPage::pageIsChanged); + } + { m_zoomFactorSpinBox = WidgetsFactory::createDoubleSpinBox(box); m_zoomFactorSpinBox->setToolTip(tr("Zoom factor in read mode")); diff --git a/src/widgets/dialogs/settings/markdowneditorpage.h b/src/widgets/dialogs/settings/markdowneditorpage.h index 2fdfa00c..6574dc95 100644 --- a/src/widgets/dialogs/settings/markdowneditorpage.h +++ b/src/widgets/dialogs/settings/markdowneditorpage.h @@ -40,6 +40,8 @@ namespace vnotex QCheckBox *m_constrainImageWidthCheckBox = nullptr; + QCheckBox *m_imageAlignCenterCheckBox = nullptr; + QCheckBox *m_constrainInplacePreviewWidthCheckBox = nullptr; QCheckBox *m_inplacePreviewSourceImageLinkCheckBox = nullptr;