1
0
Эх сурвалжийг харах

support user.css for Markdown editor read mode style

Le Tan 4 жил өмнө
parent
commit
d7bfb956ec
33 өөрчлөгдсөн 211 нэмэгдсэн , 78 устгасан
  1. 2 0
      src/core/buffer/buffer.cpp
  2. 3 0
      src/core/buffer/buffer.h
  3. 38 3
      src/core/configmgr.cpp
  4. 7 1
      src/core/configmgr.h
  5. 10 0
      src/core/fileopenparameters.h
  6. 2 2
      src/core/htmltemplatehelper.cpp
  7. 1 1
      src/core/htmltemplatehelper.h
  8. 1 1
      src/core/mainconfig.cpp
  9. 2 1
      src/data/core/vnotex.json
  10. 1 0
      src/data/extra/extra.qrc
  11. 0 1
      src/data/extra/themes/moonlight/highlight.css
  12. 3 1
      src/data/extra/themes/moonlight/interface.qss
  13. 4 0
      src/data/extra/themes/moonlight/palette.json
  14. 1 1
      src/data/extra/themes/moonlight/text-editor.theme
  15. 3 3
      src/data/extra/themes/moonlight/web.css
  16. 0 1
      src/data/extra/themes/native/highlight.css
  17. 1 1
      src/data/extra/themes/native/interface.qss
  18. 1 1
      src/data/extra/themes/native/text-editor.theme
  19. 3 3
      src/data/extra/themes/native/web.css
  20. 0 1
      src/data/extra/themes/pure/highlight.css
  21. 3 1
      src/data/extra/themes/pure/interface.qss
  22. 4 0
      src/data/extra/themes/pure/palette.json
  23. 1 1
      src/data/extra/themes/pure/text-editor.theme
  24. 3 3
      src/data/extra/themes/pure/web.css
  25. 2 0
      src/data/extra/web/css/user.css
  26. 27 10
      src/widgets/mainwindow.cpp
  27. 9 6
      src/widgets/mainwindow.h
  28. 4 1
      src/widgets/notebookselector.cpp
  29. 2 0
      src/widgets/notebookselector.h
  30. 49 32
      src/widgets/toolbarhelper.cpp
  31. 2 0
      src/widgets/toolbarhelper.h
  32. 16 1
      src/widgets/viewwindow.cpp
  33. 6 1
      src/widgets/viewwindow.h

+ 2 - 0
src/core/buffer/buffer.cpp

@@ -320,6 +320,8 @@ void Buffer::autoSave()
     case EditorConfig::AutoSavePolicy::AutoSave:
     case EditorConfig::AutoSavePolicy::AutoSave:
         if (save(false) != OperationCode::Success) {
         if (save(false) != OperationCode::Success) {
             qWarning() << "AutoSave failed to save buffer, retry later";
             qWarning() << "AutoSave failed to save buffer, retry later";
+        } else {
+            emit autoSaved();
         }
         }
         break;
         break;
 
 

+ 3 - 0
src/core/buffer/buffer.h

@@ -188,6 +188,9 @@ namespace vnotex
 
 
         void attachmentChanged();
         void attachmentChanged();
 
 
+        // This buffer is AutoSavePolicy::AutoSave.
+        void autoSaved();
+
     protected:
     protected:
         virtual ViewWindow *createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent) = 0;
         virtual ViewWindow *createViewWindowInternal(const QSharedPointer<FileOpenParameters> &p_paras, QWidget *p_parent) = 0;
 
 

+ 38 - 3
src/core/configmgr.cpp

@@ -75,7 +75,10 @@ ConfigMgr::ConfigMgr(bool p_isUnitTest, QObject *p_parent)
     } else {
     } else {
         locateConfigFolder();
         locateConfigFolder();
 
 
-        checkAppConfig();
+        bool needUpdate = checkAppConfig();
+        if (needUpdate) {
+            checkUserConfig();
+        }
     }
     }
 
 
     m_config->init();
     m_config->init();
@@ -134,7 +137,7 @@ void ConfigMgr::locateConfigFolder()
     qInfo() << "user config folder" << m_userConfigFolderPath;
     qInfo() << "user config folder" << m_userConfigFolderPath;
 }
 }
 
 
-void ConfigMgr::checkAppConfig()
+bool ConfigMgr::checkAppConfig()
 {
 {
     bool needUpdate = false;
     bool needUpdate = false;
     QDir appConfigDir(m_appConfigFolderPath);
     QDir appConfigDir(m_appConfigFolderPath);
@@ -212,7 +215,7 @@ void ConfigMgr::checkAppConfig()
         FileUtils::copyFile(getConfigFilePath(Source::Default), mainConfigFilePath);
         FileUtils::copyFile(getConfigFilePath(Source::Default), mainConfigFilePath);
         FileUtils::copyDir(extraDataRoot + QStringLiteral("/web"),
         FileUtils::copyDir(extraDataRoot + QStringLiteral("/web"),
                            appConfigDir.filePath(QStringLiteral("web")));
                            appConfigDir.filePath(QStringLiteral("web")));
-        return;
+        return needUpdate;
     }
     }
 #else
 #else
     Q_ASSERT(needUpdate);
     Q_ASSERT(needUpdate);
@@ -251,6 +254,27 @@ void ConfigMgr::checkAppConfig()
     // Main config file.
     // Main config file.
     FileUtils::copyFile(getConfigFilePath(Source::Default), appConfigDir.filePath(c_configFileName));
     FileUtils::copyFile(getConfigFilePath(Source::Default), appConfigDir.filePath(c_configFileName));
 
 
+    return needUpdate;
+}
+
+void ConfigMgr::checkUserConfig()
+{
+    // Mainly check if the user config and session config is read-only.
+    const auto userFile = getConfigFilePath(Source::User);
+    if (QFileInfo::exists(userFile)) {
+        if (!(QFile::permissions(userFile) & QFile::WriteUser)) {
+            qDebug() << "make user config file writable" << userFile;
+            QFile::setPermissions(userFile, QFile::WriteUser);
+        }
+    }
+
+    const auto sessionFile = getConfigFilePath(Source::Session);
+    if (QFileInfo::exists(sessionFile)) {
+        if (!(QFile::permissions(sessionFile) & QFile::WriteUser)) {
+            qDebug() << "make session config file writable" << sessionFile;
+            QFile::setPermissions(sessionFile, QFile::WriteUser);
+        }
+    }
 }
 }
 
 
 QString ConfigMgr::getConfigFilePath(Source p_src) const
 QString ConfigMgr::getConfigFilePath(Source p_src) const
@@ -419,6 +443,17 @@ QString ConfigMgr::getUserSnippetFolder() const
     return folderPath;
     return folderPath;
 }
 }
 
 
+QString ConfigMgr::getUserMarkdownUserStyleFile() const
+{
+    auto folderPath = PathUtils::concatenateFilePath(m_userConfigFolderPath, QStringLiteral("web/css"));
+    auto filePath = PathUtils::concatenateFilePath(folderPath, QStringLiteral("user.css"));
+    if (!QFileInfo::exists(filePath)) {
+        QDir().mkpath(folderPath);
+        FileUtils::writeFile(filePath, QByteArray());
+    }
+    return filePath;
+}
+
 QString ConfigMgr::getUserOrAppFile(const QString &p_filePath) const
 QString ConfigMgr::getUserOrAppFile(const QString &p_filePath) const
 {
 {
     QFileInfo fi(p_filePath);
     QFileInfo fi(p_filePath);

+ 7 - 1
src/core/configmgr.h

@@ -93,6 +93,9 @@ namespace vnotex
 
 
         QString getUserSnippetFolder() const;
         QString getUserSnippetFolder() const;
 
 
+        // web/css/user.css.
+        QString getUserMarkdownUserStyleFile() const;
+
         // If @p_filePath is absolute, just return it.
         // If @p_filePath is absolute, just return it.
         // Otherwise, first try to find it in user folder, then in app folder.
         // Otherwise, first try to find it in user folder, then in app folder.
         QString getUserOrAppFile(const QString &p_filePath) const;
         QString getUserOrAppFile(const QString &p_filePath) const;
@@ -135,7 +138,10 @@ namespace vnotex
 
 
         // Check if app config exists and is updated.
         // Check if app config exists and is updated.
         // Update it if in need.
         // Update it if in need.
-        void checkAppConfig();
+        // Return true if there is update.
+        bool checkAppConfig();
+
+        void checkUserConfig();
 
 
         static QString getDefaultConfigFilePath();
         static QString getDefaultConfigFilePath();
 
 

+ 10 - 0
src/core/fileopenparameters.h

@@ -1,6 +1,8 @@
 #ifndef FILEOPENPARAMETERS_H
 #ifndef FILEOPENPARAMETERS_H
 #define FILEOPENPARAMETERS_H
 #define FILEOPENPARAMETERS_H
 
 
+#include <functional>
+
 #include <QSharedPointer>
 #include <QSharedPointer>
 
 
 #include "global.h"
 #include "global.h"
@@ -12,6 +14,12 @@ namespace vnotex
 
 
     struct FileOpenParameters
     struct FileOpenParameters
     {
     {
+        enum Hook
+        {
+            PostSave,
+            MaxHook
+        };
+
         ViewWindowMode m_mode = ViewWindowMode::Read;
         ViewWindowMode m_mode = ViewWindowMode::Read;
 
 
         // Force to enter m_mode.
         // Force to enter m_mode.
@@ -41,6 +49,8 @@ namespace vnotex
 
 
         // Whether should save this file into session.
         // Whether should save this file into session.
         bool m_sessionEnabled = true;
         bool m_sessionEnabled = true;
+
+        std::function<void()> m_hooks[Hook::MaxHook];
     };
     };
 }
 }
 
 

+ 2 - 2
src/core/htmltemplatehelper.cpp

@@ -176,9 +176,9 @@ const QString &HtmlTemplateHelper::getMarkdownViewerTemplate()
     return s_markdownViewerTemplate.m_template;
     return s_markdownViewerTemplate.m_template;
 }
 }
 
 
-void HtmlTemplateHelper::updateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config)
+void HtmlTemplateHelper::updateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config, bool p_force)
 {
 {
-    if (p_config.revision() == s_markdownViewerTemplate.m_revision) {
+    if (!p_force && p_config.revision() == s_markdownViewerTemplate.m_revision) {
         return;
         return;
     }
     }
 
 

+ 1 - 1
src/core/htmltemplatehelper.h

@@ -85,7 +85,7 @@ namespace vnotex
         HtmlTemplateHelper() = delete;
         HtmlTemplateHelper() = delete;
 
 
         static const QString &getMarkdownViewerTemplate();
         static const QString &getMarkdownViewerTemplate();
-        static void updateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config);
+        static void updateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config, bool p_force = false);
 
 
         static QString generateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config, const Paras &p_paras);
         static QString generateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config, const Paras &p_paras);
 
 

+ 1 - 1
src/core/mainconfig.cpp

@@ -118,5 +118,5 @@ QString MainConfig::getVersion(const QJsonObject &p_jobj)
 void MainConfig::doVersionSpecificOverride()
 void MainConfig::doVersionSpecificOverride()
 {
 {
     // In a new version, we may want to change one value by force.
     // In a new version, we may want to change one value by force.
-    m_editorConfig->getMarkdownEditorConfig().m_spellCheckEnabled = false;
+    m_coreConfig->m_docksTabBarIconSize = 26;
 }
 }

+ 2 - 1
src/data/core/vnotex.json

@@ -62,7 +62,7 @@
             "OpenLastClosedFile" : "Ctrl+Shift+T"
             "OpenLastClosedFile" : "Ctrl+Shift+T"
         },
         },
         "toolbar_icon_size" : 16,
         "toolbar_icon_size" : 16,
-        "docks_tabbar_icon_size" : 20,
+        "docks_tabbar_icon_size" : 26,
         "note_management" : {
         "note_management" : {
             "external_node" : {
             "external_node" : {
                 "//comment" : "Wildcard patterns of files and folders to exclude as external files",
                 "//comment" : "Wildcard patterns of files and folders to exclude as external files",
@@ -152,6 +152,7 @@
                         "name" : "global_styles",
                         "name" : "global_styles",
                         "enabled" : true,
                         "enabled" : true,
                         "styles" : [
                         "styles" : [
+                            "web/css/user.css",
                             "web/css/globalstyles.css"
                             "web/css/globalstyles.css"
                         ]
                         ]
                     },
                     },

+ 1 - 0
src/data/extra/extra.qrc

@@ -16,6 +16,7 @@
         <file>docs/zh_CN/features_tips.txt</file>
         <file>docs/zh_CN/features_tips.txt</file>
         <file>web/markdown-viewer-template.html</file>
         <file>web/markdown-viewer-template.html</file>
         <file>web/markdown-export-template.html</file>
         <file>web/markdown-export-template.html</file>
+        <file>web/css/user.css</file>
         <file>web/css/globalstyles.css</file>
         <file>web/css/globalstyles.css</file>
         <file>web/css/markdownit.css</file>
         <file>web/css/markdownit.css</file>
         <file>web/css/imageviewer.css</file>
         <file>web/css/imageviewer.css</file>

+ 0 - 1
src/data/extra/themes/moonlight/highlight.css

@@ -10,7 +10,6 @@ code[class*="language-"],
 pre[class*="language-"] {
 pre[class*="language-"] {
     color: #ccc;
     color: #ccc;
     background: none;
     background: none;
-    font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
     font-size: 1em;
     font-size: 1em;
     text-align: left;
     text-align: left;
     white-space: pre;
     white-space: pre;

+ 3 - 1
src/data/extra/themes/moonlight/interface.qss

@@ -434,8 +434,10 @@ vnotex--DragDropAreaIndicator QLabel {
 }
 }
 
 
 vnotex--MainWindow QLabel#MainWindowTipsLabel {
 vnotex--MainWindow QLabel#MainWindowTipsLabel {
-    font-size: 18pt;
+    font-size: 20pt;
     font-weight: bold;
     font-weight: bold;
+    color: @widgets#mainwindow#tips_label#fg;
+    background-color: @widgets#mainwindow#tips_label#bg;
 }
 }
 
 
 /* QLineEdit */
 /* QLineEdit */

+ 4 - 0
src/data/extra/themes/moonlight/palette.json

@@ -642,6 +642,10 @@
                         "fg" : "@base#icon#selected#fg"
                         "fg" : "@base#icon#selected#fg"
                     }
                     }
                 }
                 }
+            },
+            "tips_label" : {
+                "fg" : "@base#master#fg",
+                "bg" : "@base#master#bg"
             }
             }
         }
         }
     }
     }

+ 1 - 1
src/data/extra/themes/moonlight/text-editor.theme

@@ -67,7 +67,7 @@
     "markdown-editor-styles" : {
     "markdown-editor-styles" : {
         "Text" : {
         "Text" : {
             "//comment" : "Support a list of fonts separated by ,",
             "//comment" : "Support a list of fonts separated by ,",
-            "font-family" : "冬青黑体, YaHei Consolas Hybrid, Microsoft YaHei, 微软雅黑, Microsoft YaHei UI, WenQuanYi Micro Hei, 文泉驿雅黑, Dengxian, 等线体, STXihei, 华文细黑, Liberation Sans, Droid Sans, NSimSun, 新宋体, SimSun, 宋体, Verdana, Helvetica, sans-serif, Tahoma, Arial, Geneva, Georgia, Times New Roman",
+            "font-family" : "YaHei Consolas Hybrid, 冬青黑体, Microsoft YaHei, 微软雅黑, Microsoft YaHei UI, WenQuanYi Micro Hei, 文泉驿雅黑, Dengxian, 等线体, STXihei, 华文细黑, Liberation Sans, Droid Sans, NSimSun, 新宋体, SimSun, 宋体, Verdana, Helvetica, Tahoma, Arial, Geneva, Georgia, Times New Roman",
             "font-size" : 12,
             "font-size" : 12,
             "text-color" : "#ccd1d8",
             "text-color" : "#ccd1d8",
             "background-color" : "#333842",
             "background-color" : "#333842",

+ 3 - 3
src/data/extra/themes/moonlight/web.css

@@ -1,6 +1,6 @@
 body {
 body {
     margin: 0 auto;
     margin: 0 auto;
-    font-family: -apple-system, "Noto Sans", "Helvetica Neue", "Segoe UI", Helvetica, sans-serif, Tahoma, Arial, Geneva, Georgia, Palatino, "Times New Roman", "冬青黑体", "YaHei Consolas Hybrid", "Microsoft YaHei", "微软雅黑", "Microsoft YaHei UI", "WenQuanYi Micro Hei", "文泉驿雅黑", Dengxian, "等线体", STXihei, "华文细黑", "Liberation Sans", "Droid Sans", NSimSun, "新宋体", SimSun, "宋体", "Apple Color Emoji", "Segoe UI Emoji";
+    font-family: "YaHei Consolas Hybrid", "Noto Sans", "Helvetica Neue", "Segoe UI", Helvetica, Tahoma, Arial, Geneva, Georgia, Palatino, "Times New Roman", "冬青黑体", "Microsoft YaHei", "微软雅黑", "Microsoft YaHei UI", "WenQuanYi Micro Hei", "文泉驿雅黑", Dengxian, "等线体", STXihei, "华文细黑", "Liberation Sans", "Droid Sans", NSimSun, "新宋体", SimSun, "宋体", "Apple Color Emoji", "Segoe UI Emoji";
     color: #ccd1d8;
     color: #ccd1d8;
     line-height: 1.5;
     line-height: 1.5;
     padding: 16px;
     padding: 16px;
@@ -95,7 +95,7 @@ pre {
 }
 }
 
 
 code {
 code {
-    font-family: "YaHei Consolas Hybrid", Consolas, Monaco, Monospace, Courier;
+    font-family: "YaHei Consolas Hybrid", Consolas, Monaco, "Andale Mono", Monospace, "Courier New";
     color: #98c379;
     color: #98c379;
     word-break: break-word;
     word-break: break-word;
 }
 }
@@ -107,7 +107,7 @@ pre code {
     color: #98c379;
     color: #98c379;
     background-color: #2d323b;
     background-color: #2d323b;
     line-height: 1.5;
     line-height: 1.5;
-    font-family: "YaHei Consolas Hybrid", Consolas, Monaco, Monospace, Courier;
+    font-family: "YaHei Consolas Hybrid", Consolas, Monaco, "Andale Mono", Monospace, "Courier New";
     white-space: pre;
     white-space: pre;
     -moz-tab-size: 4;
     -moz-tab-size: 4;
     -o-tab-size: 4;
     -o-tab-size: 4;

+ 0 - 1
src/data/extra/themes/native/highlight.css

@@ -11,7 +11,6 @@ pre[class*="language-"] {
     color: black;
     color: black;
     background: none;
     background: none;
     text-shadow: 0 1px white;
     text-shadow: 0 1px white;
-    font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
     font-size: 1em;
     font-size: 1em;
     text-align: left;
     text-align: left;
     white-space: pre;
     white-space: pre;

+ 1 - 1
src/data/extra/themes/native/interface.qss

@@ -120,7 +120,7 @@ vnotex--DragDropAreaIndicator QLabel {
 }
 }
 
 
 vnotex--MainWindow QLabel#MainWindowTipsLabel {
 vnotex--MainWindow QLabel#MainWindowTipsLabel {
-    font-size: 18pt;
+    font-size: 20pt;
     font-weight: bold;
     font-weight: bold;
 }
 }
 
 

+ 1 - 1
src/data/extra/themes/native/text-editor.theme

@@ -63,7 +63,7 @@
     "markdown-editor-styles" : {
     "markdown-editor-styles" : {
         "Text" : {
         "Text" : {
             "//comment" : "Support a list of fonts separated by ,",
             "//comment" : "Support a list of fonts separated by ,",
-            "font-family" : "冬青黑体, YaHei Consolas Hybrid, Microsoft YaHei, 微软雅黑, Microsoft YaHei UI, WenQuanYi Micro Hei, 文泉驿雅黑, Dengxian, 等线体, STXihei, 华文细黑, Liberation Sans, Droid Sans, NSimSun, 新宋体, SimSun, 宋体, Verdana, Helvetica, sans-serif, Tahoma, Arial, Geneva, Georgia, Times New Roman",
+            "font-family" : "YaHei Consolas Hybrid, 冬青黑体, Microsoft YaHei, 微软雅黑, Microsoft YaHei UI, WenQuanYi Micro Hei, 文泉驿雅黑, Dengxian, 等线体, STXihei, 华文细黑, Liberation Sans, Droid Sans, NSimSun, 新宋体, SimSun, 宋体, Verdana, Helvetica, Tahoma, Arial, Geneva, Georgia, Times New Roman",
             "font-size" : 12
             "font-size" : 12
         }
         }
     },
     },

+ 3 - 3
src/data/extra/themes/native/web.css

@@ -1,6 +1,6 @@
 body {
 body {
     margin: 0 auto;
     margin: 0 auto;
-    font-family: -apple-system, "Noto Sans", "Helvetica Neue", "Segoe UI", Helvetica, sans-serif, Tahoma, Arial, Geneva, Georgia, Palatino, "Times New Roman", "冬青黑体", "YaHei Consolas Hybrid", "Microsoft YaHei", "微软雅黑", "Microsoft YaHei UI", "WenQuanYi Micro Hei", "文泉驿雅黑", Dengxian, "等线体", STXihei, "华文细黑", "Liberation Sans", "Droid Sans", NSimSun, "新宋体", SimSun, "宋体", "Apple Color Emoji", "Segoe UI Emoji";
+    font-family: "YaHei Consolas Hybrid", "Noto Sans", "Helvetica Neue", "Segoe UI", Helvetica, Tahoma, Arial, Geneva, Georgia, Palatino, "Times New Roman", "冬青黑体", "Microsoft YaHei", "微软雅黑", "Microsoft YaHei UI", "WenQuanYi Micro Hei", "文泉驿雅黑", Dengxian, "等线体", STXihei, "华文细黑", "Liberation Sans", "Droid Sans", NSimSun, "新宋体", SimSun, "宋体", "Apple Color Emoji", "Segoe UI Emoji";
     color: #222222;
     color: #222222;
     line-height: 1.5;
     line-height: 1.5;
     padding: 16px;
     padding: 16px;
@@ -90,7 +90,7 @@ pre {
 }
 }
 
 
 code {
 code {
-    font-family: "YaHei Consolas Hybrid", Consolas, Monaco, Monospace, Courier;
+    font-family: "YaHei Consolas Hybrid", Consolas, Monaco, "Andale Mono", Monospace, "Courier New";
     color: #8E24AA;
     color: #8E24AA;
     word-break: break-word;
     word-break: break-word;
 }
 }
@@ -102,7 +102,7 @@ pre code {
     color: #222222;
     color: #222222;
     background-color: #E0E0E0;
     background-color: #E0E0E0;
     line-height: 1.5;
     line-height: 1.5;
-    font-family: "YaHei Consolas Hybrid", Consolas, Monaco, Monospace, Courier;
+    font-family: "YaHei Consolas Hybrid", Consolas, Monaco, "Andale Mono", Monospace, "Courier New";
     white-space: pre;
     white-space: pre;
     -moz-tab-size: 4;
     -moz-tab-size: 4;
     -o-tab-size: 4;
     -o-tab-size: 4;

+ 0 - 1
src/data/extra/themes/pure/highlight.css

@@ -10,7 +10,6 @@ code[class*="language-"],
 pre[class*="language-"] {
 pre[class*="language-"] {
     color: black;
     color: black;
     background: none;
     background: none;
-    font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
     font-size: 1em;
     font-size: 1em;
     text-align: left;
     text-align: left;
     white-space: pre;
     white-space: pre;

+ 3 - 1
src/data/extra/themes/pure/interface.qss

@@ -434,8 +434,10 @@ vnotex--DragDropAreaIndicator QLabel {
 }
 }
 
 
 vnotex--MainWindow QLabel#MainWindowTipsLabel {
 vnotex--MainWindow QLabel#MainWindowTipsLabel {
-    font-size: 18pt;
+    font-size: 20pt;
     font-weight: bold;
     font-weight: bold;
+    color: @widgets#mainwindow#tips_label#fg;
+    background-color: @widgets#mainwindow#tips_label#bg;
 }
 }
 
 
 /* QLineEdit */
 /* QLineEdit */

+ 4 - 0
src/data/extra/themes/pure/palette.json

@@ -638,6 +638,10 @@
                         "fg" : "@base#icon#selected#fg"
                         "fg" : "@base#icon#selected#fg"
                     }
                     }
                 }
                 }
+            },
+            "tips_label" : {
+                "fg" : "@base#master#fg",
+                "bg" : "@base#master#bg"
             }
             }
         }
         }
     }
     }

+ 1 - 1
src/data/extra/themes/pure/text-editor.theme

@@ -67,7 +67,7 @@
     "markdown-editor-styles" : {
     "markdown-editor-styles" : {
         "Text" : {
         "Text" : {
             "//comment" : "Support a list of fonts separated by ,",
             "//comment" : "Support a list of fonts separated by ,",
-            "font-family" : "冬青黑体, YaHei Consolas Hybrid, Microsoft YaHei, 微软雅黑, Microsoft YaHei UI, WenQuanYi Micro Hei, 文泉驿雅黑, Dengxian, 等线体, STXihei, 华文细黑, Liberation Sans, Droid Sans, NSimSun, 新宋体, SimSun, 宋体, Verdana, Helvetica, sans-serif, Tahoma, Arial, Geneva, Georgia, Times New Roman",
+            "font-family" : "YaHei Consolas Hybrid, 冬青黑体, Microsoft YaHei, 微软雅黑, Microsoft YaHei UI, WenQuanYi Micro Hei, 文泉驿雅黑, Dengxian, 等线体, STXihei, 华文细黑, Liberation Sans, Droid Sans, NSimSun, 新宋体, SimSun, 宋体, Verdana, Helvetica, Tahoma, Arial, Geneva, Georgia, Times New Roman",
             "font-size" : 12,
             "font-size" : 12,
             "text-color" : "#222222",
             "text-color" : "#222222",
             "background-color" : "#f5f5f5",
             "background-color" : "#f5f5f5",

+ 3 - 3
src/data/extra/themes/pure/web.css

@@ -1,6 +1,6 @@
 body {
 body {
     margin: 0 auto;
     margin: 0 auto;
-    font-family: -apple-system, "Noto Sans", "Helvetica Neue", "Segoe UI", Helvetica, sans-serif, Tahoma, Arial, Geneva, Georgia, Palatino, "Times New Roman", "冬青黑体", "YaHei Consolas Hybrid", "Microsoft YaHei", "微软雅黑", "Microsoft YaHei UI", "WenQuanYi Micro Hei", "文泉驿雅黑", Dengxian, "等线体", STXihei, "华文细黑", "Liberation Sans", "Droid Sans", NSimSun, "新宋体", SimSun, "宋体", "Apple Color Emoji", "Segoe UI Emoji";
+    font-family: "YaHei Consolas Hybrid", "Noto Sans", "Helvetica Neue", "Segoe UI", Helvetica, Tahoma, Arial, Geneva, Georgia, Palatino, "Times New Roman", "冬青黑体", "Microsoft YaHei", "微软雅黑", "Microsoft YaHei UI", "WenQuanYi Micro Hei", "文泉驿雅黑", Dengxian, "等线体", STXihei, "华文细黑", "Liberation Sans", "Droid Sans", NSimSun, "新宋体", SimSun, "宋体", "Apple Color Emoji", "Segoe UI Emoji";
     color: #222222;
     color: #222222;
     line-height: 1.5;
     line-height: 1.5;
     padding: 16px;
     padding: 16px;
@@ -90,7 +90,7 @@ pre {
 }
 }
 
 
 code {
 code {
-    font-family: "YaHei Consolas Hybrid", Consolas, Monaco, Monospace, Courier;
+    font-family: "YaHei Consolas Hybrid", Consolas, Monaco, "Andale Mono", Monospace, "Courier New";
     color: #8e24aa;
     color: #8e24aa;
     word-break: break-word;
     word-break: break-word;
 }
 }
@@ -102,7 +102,7 @@ pre code {
     color: #222222;
     color: #222222;
     background-color: #e0e0e0;
     background-color: #e0e0e0;
     line-height: 1.5;
     line-height: 1.5;
-    font-family: "YaHei Consolas Hybrid", Consolas, Monaco, Monospace, Courier;
+    font-family: "YaHei Consolas Hybrid", Consolas, Monaco, "Andale Mono", Monospace, "Courier New";
     white-space: pre;
     white-space: pre;
     -moz-tab-size: 4;
     -moz-tab-size: 4;
     -o-tab-size: 4;
     -o-tab-size: 4;

+ 2 - 0
src/data/extra/web/css/user.css

@@ -0,0 +1,2 @@
+/* Styles here will be placed in the header of the HTML template as global embedded styles. */
+/* This file is to hold customized styles provided by user. */

+ 27 - 10
src/widgets/mainwindow.cpp

@@ -377,6 +377,11 @@ void MainWindow::closeEvent(QCloseEvent *p_event)
     }
     }
 
 
     if (isVisible()) {
     if (isVisible()) {
+        // Avoid geometry corruption caused by fullscreen or minimized window.
+        const auto state = windowState();
+        if (state & (Qt::WindowMinimized | Qt::WindowFullScreen)) {
+            showNormal();
+        }
         saveStateAndGeometry();
         saveStateAndGeometry();
     }
     }
 
 
@@ -626,6 +631,15 @@ void MainWindow::updateDockWidgetTabBar()
 
 
 void MainWindow::exportNotes()
 void MainWindow::exportNotes()
 {
 {
+    if (m_exportDialog) {
+        MessageBoxHelper::notify(MessageBoxHelper::Information,
+                                 tr("There is one export dialog running. Please close it first."),
+                                 this);
+        m_exportDialog->activateWindow();
+        m_exportDialog->show();
+        return;
+    }
+
     auto currentNotebook = m_notebookExplorer->currentNotebook().data();
     auto currentNotebook = m_notebookExplorer->currentNotebook().data();
     auto viewWindow = m_viewArea->getCurrentViewWindow();
     auto viewWindow = m_viewArea->getCurrentViewWindow();
     auto folderNode = m_notebookExplorer->currentExploredFolderNode();
     auto folderNode = m_notebookExplorer->currentExploredFolderNode();
@@ -636,17 +650,20 @@ void MainWindow::exportNotes()
     if (noteNode && !noteNode->hasContent()) {
     if (noteNode && !noteNode->hasContent()) {
         noteNode = nullptr;
         noteNode = nullptr;
     }
     }
-    auto dialog = new ExportDialog(currentNotebook,
-                                   folderNode,
-                                   noteNode,
-                                   viewWindow ? viewWindow->getBuffer() : nullptr,
-                                   nullptr);
-    connect(dialog, &QDialog::finished,
-            this, [this, dialog]() {
-                dialog->deleteLater();
+
+    m_exportDialog = new ExportDialog(currentNotebook,
+                                      folderNode,
+                                      noteNode,
+                                      viewWindow ? viewWindow->getBuffer() : nullptr,
+                                      nullptr);
+    connect(m_exportDialog, &QDialog::finished,
+            this, [this]() {
+                m_exportDialog->deleteLater();
+                m_exportDialog = nullptr;
             });
             });
+
     // Let it be able to run at background.
     // Let it be able to run at background.
-    dialog->show();
+    m_exportDialog->show();
 }
 }
 
 
 void MainWindow::showTips(const QString &p_message, int p_timeoutMilliseconds)
 void MainWindow::showTips(const QString &p_message, int p_timeoutMilliseconds)
@@ -675,7 +692,7 @@ void MainWindow::setTipsAreaVisible(bool p_visible)
         int labelW = m_tipsLabel->width();
         int labelW = m_tipsLabel->width();
         int labelH = m_tipsLabel->height();
         int labelH = m_tipsLabel->height();
         int x = (width() - labelW) / 2;
         int x = (width() - labelW) / 2;
-        int y = (height() - labelH) / 2;
+        int y = (height() - labelH) / 3;
         if (x < 0) {
         if (x < 0) {
             x = 0;
             x = 0;
         }
         }

+ 9 - 6
src/widgets/mainwindow.h

@@ -26,6 +26,7 @@ namespace vnotex
     class SearchPanel;
     class SearchPanel;
     class SnippetPanel;
     class SnippetPanel;
     class HistoryPanel;
     class HistoryPanel;
+    class ExportDialog;
 
 
     enum { RESTART_EXIT_CODE = 1000 };
     enum { RESTART_EXIT_CODE = 1000 };
 
 
@@ -169,6 +170,14 @@ namespace vnotex
 
 
         HistoryPanel *m_historyPanel = nullptr;
         HistoryPanel *m_historyPanel = nullptr;
 
 
+        ExportDialog *m_exportDialog = nullptr;
+
+        QSystemTrayIcon *m_trayIcon = nullptr;
+
+        QLabel *m_tipsLabel = nullptr;
+
+        QTimer *m_tipsTimer = nullptr;
+
         bool m_layoutReset = false;
         bool m_layoutReset = false;
 
 
         // -1: do not request to quit;
         // -1: do not request to quit;
@@ -177,12 +186,6 @@ namespace vnotex
 
 
         Qt::WindowStates m_windowOldState = Qt::WindowMinimized;
         Qt::WindowStates m_windowOldState = Qt::WindowMinimized;
 
 
-        QSystemTrayIcon *m_trayIcon = nullptr;
-
-        QLabel *m_tipsLabel = nullptr;
-
-        QTimer *m_tipsTimer = nullptr;
-
         QStringList m_visibleDocksBeforeExpand;
         QStringList m_visibleDocksBeforeExpand;
     };
     };
 } // ns vnotex
 } // ns vnotex

+ 4 - 1
src/widgets/notebookselector.cpp

@@ -35,6 +35,8 @@ void NotebookSelector::setNotebooks(const QVector<QSharedPointer<Notebook>> &p_n
     }
     }
 
 
     updateGeometry();
     updateGeometry();
+
+    m_notebooksInitialized = true;
 }
 }
 
 
 void NotebookSelector::reloadNotebook(const Notebook *p_notebook)
 void NotebookSelector::reloadNotebook(const Notebook *p_notebook)
@@ -179,7 +181,8 @@ void NotebookSelector::clearNavigation()
 
 
 void NotebookSelector::mousePressEvent(QMouseEvent *p_event)
 void NotebookSelector::mousePressEvent(QMouseEvent *p_event)
 {
 {
-    if (count() == 0) {
+    // Only when notebooks are loaded and there is no notebook, we could prompt for new notebook.
+    if (m_notebooksInitialized && count() == 0) {
         emit newNotebookRequested();
         emit newNotebookRequested();
         return;
         return;
     }
     }

+ 2 - 0
src/widgets/notebookselector.h

@@ -53,6 +53,8 @@ namespace vnotex
 
 
         int findNotebook(ID p_id) const;
         int findNotebook(ID p_id) const;
 
 
+        bool m_notebooksInitialized = false;
+
         QVector<QModelIndex> m_navigationIndexes;
         QVector<QModelIndex> m_navigationIndexes;
     };
     };
 } // ns vnotex
 } // ns vnotex

+ 49 - 32
src/widgets/toolbarhelper.cpp

@@ -22,7 +22,10 @@
 #include <core/configmgr.h>
 #include <core/configmgr.h>
 #include <core/coreconfig.h>
 #include <core/coreconfig.h>
 #include <core/sessionconfig.h>
 #include <core/sessionconfig.h>
+#include <core/editorconfig.h>
+#include <core/markdowneditorconfig.h>
 #include <core/fileopenparameters.h>
 #include <core/fileopenparameters.h>
+#include <core/htmltemplatehelper.h>
 #include "propertydefs.h"
 #include "propertydefs.h"
 #include "dialogs/settings/settingsdialog.h"
 #include "dialogs/settings/settingsdialog.h"
 #include "dialogs/updater.h"
 #include "dialogs/updater.h"
@@ -390,38 +393,7 @@ QToolBar *ToolBarHelper::setupSettingsToolBar(MainWindow *p_win, QToolBar *p_too
 
 
         menu->addSeparator();
         menu->addSeparator();
 
 
-        menu->addAction(MainWindow::tr("Open User Configuration Folder"),
-                        menu,
-                        []() {
-                            auto folderPath = ConfigMgr::getInst().getUserFolder();
-                            WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(folderPath));
-                        });
-
-        menu->addAction(MainWindow::tr("Open Default Configuration Folder"),
-                        menu,
-                        []() {
-                            auto folderPath = ConfigMgr::getInst().getAppFolder();
-                            WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(folderPath));
-                        });
-
-        menu->addSeparator();
-
-        menu->addAction(MainWindow::tr("Edit User Configuration"),
-                        menu,
-                        []() {
-                            auto file = ConfigMgr::getInst().getConfigFilePath(ConfigMgr::Source::User);
-                            auto paras = QSharedPointer<FileOpenParameters>::create();
-                            emit VNoteX::getInst().openFileRequested(file, paras);
-                        });
-
-        menu->addAction(MainWindow::tr("Open Default Configuration"),
-                        menu,
-                        []() {
-                            auto file = ConfigMgr::getInst().getConfigFilePath(ConfigMgr::Source::App);
-                            auto paras = QSharedPointer<FileOpenParameters>::create();
-                            paras->m_readOnly = true;
-                            emit VNoteX::getInst().openFileRequested(file, paras);
-                        });
+        setupConfigurationMenu(menu);
 
 
         menu->addSeparator();
         menu->addSeparator();
 
 
@@ -546,6 +518,51 @@ QToolBar *ToolBarHelper::setupSettingsToolBar(MainWindow *p_win, QToolBar *p_too
     return tb;
     return tb;
 }
 }
 
 
+void ToolBarHelper::setupConfigurationMenu(QMenu *p_menu)
+{
+    auto menu = p_menu->addMenu(MainWindow::tr("Configuration"));
+
+    menu->addAction(MainWindow::tr("Edit User Configuration File"),
+                    menu,
+                    []() {
+                        auto file = ConfigMgr::getInst().getConfigFilePath(ConfigMgr::Source::User);
+                        auto paras = QSharedPointer<FileOpenParameters>::create();
+                        paras->m_sessionEnabled = false;
+                        emit VNoteX::getInst().openFileRequested(file, paras);
+                    });
+
+    menu->addAction(MainWindow::tr("Open User Configuration Folder"),
+                    menu,
+                    []() {
+                        auto folderPath = ConfigMgr::getInst().getUserFolder();
+                        WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(folderPath));
+                    });
+
+    menu->addAction(MainWindow::tr("Open Default Configuration Folder"),
+                    menu,
+                    []() {
+                        auto folderPath = ConfigMgr::getInst().getAppFolder();
+                        WidgetUtils::openUrlByDesktop(QUrl::fromLocalFile(folderPath));
+                    });
+
+    menu->addSeparator();
+
+    auto act = menu->addAction(MainWindow::tr("Edit Markdown User Styles"),
+                               menu,
+                               []() {
+                                   const auto file = ConfigMgr::getInst().getUserMarkdownUserStyleFile();
+                                   auto paras = QSharedPointer<FileOpenParameters>::create();
+                                   paras->m_sessionEnabled = false;
+                                   paras->m_hooks[FileOpenParameters::PostSave] = []() {
+                                       qDebug() << "post save";
+                                       const auto &markdownConfig = ConfigMgr::getInst().getEditorConfig().getMarkdownEditorConfig();
+                                       HtmlTemplateHelper::updateMarkdownViewerTemplate(markdownConfig, true);
+                                   };
+                                   emit VNoteX::getInst().openFileRequested(file, paras);
+                               });
+    act->setStatusTip(MainWindow::tr("Edit the user styles of Markdown editor read mode"));
+}
+
 static const QString c_fgPalette = QStringLiteral("widgets#toolbar#icon#fg");
 static const QString c_fgPalette = QStringLiteral("widgets#toolbar#icon#fg");
 static const QString c_disabledPalette = QStringLiteral("widgets#toolbar#icon#disabled#fg");
 static const QString c_disabledPalette = QStringLiteral("widgets#toolbar#icon#disabled#fg");
 static const QString c_dangerousPalette = QStringLiteral("widgets#toolbar#icon#danger#fg");
 static const QString c_dangerousPalette = QStringLiteral("widgets#toolbar#icon#danger#fg");

+ 2 - 0
src/widgets/toolbarhelper.h

@@ -36,6 +36,8 @@ namespace vnotex
         static QToolBar *setupSettingsToolBar(MainWindow *p_win, QToolBar *p_toolBar);
         static QToolBar *setupSettingsToolBar(MainWindow *p_win, QToolBar *p_toolBar);
 
 
         static void updateQuickAccessMenu(QMenu *p_menu);
         static void updateQuickAccessMenu(QMenu *p_menu);
+
+        static void setupConfigurationMenu(QMenu *p_menu);
     };
     };
 } // ns vnotex
 } // ns vnotex
 
 

+ 16 - 1
src/widgets/viewwindow.cpp

@@ -16,7 +16,6 @@
 #include <QWidgetAction>
 #include <QWidgetAction>
 #include <QActionGroup>
 #include <QActionGroup>
 
 
-#include <core/fileopenparameters.h>
 #include "toolbarhelper.h"
 #include "toolbarhelper.h"
 #include "vnotex.h"
 #include "vnotex.h"
 #include <utils/iconutils.h>
 #include <utils/iconutils.h>
@@ -140,10 +139,17 @@ void ViewWindow::handleBufferChanged(const QSharedPointer<FileOpenParameters> &p
 
 
         connect(buffer, &Buffer::attachmentChanged,
         connect(buffer, &Buffer::attachmentChanged,
                 this, &ViewWindow::attachmentChanged);
                 this, &ViewWindow::attachmentChanged);
+
+        connect(buffer, &Buffer::autoSaved,
+                this, [this]() {
+                    executeHook(FileOpenParameters::PostSave);
+                });
     }
     }
 
 
     m_sessionEnabled = p_paras->m_sessionEnabled;
     m_sessionEnabled = p_paras->m_sessionEnabled;
 
 
+    m_openParas = p_paras;
+
     handleBufferChangedInternal(p_paras);
     handleBufferChangedInternal(p_paras);
 
 
     emit bufferChanged();
     emit bufferChanged();
@@ -887,9 +893,18 @@ bool ViewWindow::save(bool p_force)
         return false;
         return false;
     }
     }
 
 
+    executeHook(FileOpenParameters::PostSave);
+
     return true;
     return true;
 }
 }
 
 
+void ViewWindow::executeHook(FileOpenParameters::Hook p_hook) const
+{
+    if (m_openParas && m_openParas->m_hooks[p_hook]) {
+        m_openParas->m_hooks[p_hook]();
+    }
+}
+
 void ViewWindow::updateEditReadDiscardActionState(EditReadDiscardAction *p_act)
 void ViewWindow::updateEditReadDiscardActionState(EditReadDiscardAction *p_act)
 {
 {
     switch (getMode()) {
     switch (getMode()) {

+ 6 - 1
src/widgets/viewwindow.h

@@ -7,6 +7,7 @@
 
 
 #include <buffer/buffer.h>
 #include <buffer/buffer.h>
 #include <core/global.h>
 #include <core/global.h>
+#include <core/fileopenparameters.h>
 
 
 #include "viewwindowtoolbarhelper.h"
 #include "viewwindowtoolbarhelper.h"
 #include "viewwindowsession.h"
 #include "viewwindowsession.h"
@@ -20,7 +21,6 @@ class QActionGroup;
 namespace vnotex
 namespace vnotex
 {
 {
     class ViewSplit;
     class ViewSplit;
-    struct FileOpenParameters;
     class DragDropAreaIndicator;
     class DragDropAreaIndicator;
     class DragDropAreaIndicatorInterface;
     class DragDropAreaIndicatorInterface;
     class OutlineProvider;
     class OutlineProvider;
@@ -262,6 +262,8 @@ namespace vnotex
         // Managed by QObject.
         // Managed by QObject.
         FindAndReplaceWidget *m_findAndReplace = nullptr;
         FindAndReplaceWidget *m_findAndReplace = nullptr;
 
 
+        QSharedPointer<FileOpenParameters> m_openParas;
+
     private:
     private:
         struct FindInfo
         struct FindInfo
         {
         {
@@ -314,6 +316,8 @@ namespace vnotex
 
 
         void updateImageHostMenu();
         void updateImageHostMenu();
 
 
+        void executeHook(FileOpenParameters::Hook p_hook) const;
+
         static ViewWindow::TypeAction toolBarActionToTypeAction(ViewWindowToolBarHelper::Action p_action);
         static ViewWindow::TypeAction toolBarActionToTypeAction(ViewWindowToolBarHelper::Action p_action);
 
 
         Buffer *m_buffer = nullptr;
         Buffer *m_buffer = nullptr;
@@ -324,6 +328,7 @@ namespace vnotex
         // Last find info.
         // Last find info.
         FindInfo m_findInfo;
         FindInfo m_findInfo;
 
 
+        // Whether this ViewWindow should be recored to session.
         bool m_sessionEnabled = true;
         bool m_sessionEnabled = true;
 
 
         // Null if this window has not been added to any split.
         // Null if this window has not been added to any split.