support custom code block style of highlightjs

Add two config:
- template_code_block_css
- template_code_block_css_url
This commit is contained in:
Le Tan 2017-09-30 19:01:21 +08:00
parent 3724eb35dd
commit 06fc4d5831
13 changed files with 308 additions and 29 deletions

View File

@ -41,6 +41,7 @@ var updateHtml = function(html) {
}
}
addClassToCodeBlock();
renderCodeBlockLineNumber();
// If you add new logics after handling MathJax, please pay attention to

View File

@ -97,6 +97,7 @@ var updateText = function(text) {
insertImageCaption();
renderMermaid('lang-mermaid');
renderFlowchart('lang-flowchart');
addClassToCodeBlock();
renderCodeBlockLineNumber();
// If you add new logics after handling MathJax, please pay attention to

View File

@ -22,7 +22,7 @@
</style>
<link rel="stylesheet" type="text/css" href="CSS_PLACE_HOLDER">
<link rel="stylesheet" type="text/css" href="qrc:/utils/highlightjs/styles/vnote.css">
<link rel="stylesheet" type="text/css" href="HIGHLIGHTJS_CSS_PLACE_HOLDER">
<script src="qrc:/resources/qwebchannel.js"></script>
<script src="qrc:/utils/highlightjs/highlight.pack.js"></script>
<!-- EXTRA_PLACE_HOLDER -->

View File

@ -833,3 +833,14 @@ var renderCodeBlockLineNumber = function() {
}
}
};
var addClassToCodeBlock = function() {
var hljsClass = 'hljs';
var codes = document.getElementsByTagName('code');
for (var i = 0; i < codes.length; ++i) {
var code = codes[i];
if (code.parentElement.tagName.toLowerCase() == 'pre') {
code.classList.add(hljsClass);
}
}
};

View File

@ -49,6 +49,7 @@ var updateText = function(text) {
insertImageCaption();
renderMermaid('lang-mermaid');
renderFlowchart('lang-flowchart');
addClassToCodeBlock();
renderCodeBlockLineNumber();
// If you add new logics after handling MathJax, please pay attention to

View File

@ -76,6 +76,7 @@ var updateText = function(text) {
highlightCodeBlocks(document, VEnableMermaid, VEnableFlowchart);
renderMermaid('language-mermaid');
renderFlowchart('language-flowchart');
addClassToCodeBlock();
renderCodeBlockLineNumber();
// If you add new logics after handling MathJax, please pay attention to

View File

@ -77,7 +77,6 @@ p, ul, ol {
}
pre {
padding: 0px 24px;
background-color: #f8f8f8;
border-radius: 3px;
border: 1px solid #cccccc;
@ -92,12 +91,12 @@ code {
}
pre code {
margin: 0;
padding: 0;
border: none;
background: transparent;
overflow: auto;
color: #363636;
margin: 0;
padding: 0;
border: none;
background: transparent;
overflow: auto;
color: #363636;
}
aside {

View File

@ -1,6 +1,16 @@
[global]
welcome_page_path=:/resources/welcome.html
; CSS style for Markdown template
template_css=default
; Code block CSS style for Markdown template
template_code_block_css=vnote
; Code block CSS style file URL for Markdown template
; If not empty, VNote will ignore template_code_block_css
template_code_block_css_url=
editor_style=default
current_notebook=0
tab_stop_width=4

View File

@ -18,7 +18,9 @@ const QString VConfigManager::c_obsoleteDirConfigFile = QString(".vnote.json");
const QString VConfigManager::c_dirConfigFile = QString("_vnote.json");
const QString VConfigManager::defaultConfigFilePath = QString(":/resources/vnote.ini");
const QString VConfigManager::c_styleConfigFolder = QString("styles");
const QString VConfigManager::c_codeBlockStyleConfigFolder = QString("codeblock_styles");
const QString VConfigManager::c_defaultCssFile = QString(":/resources/styles/default.css");
const QString VConfigManager::c_defaultCodeBlockCssFile = QString(":/utils/highlightjs/styles/vnote.css");
const QString VConfigManager::c_defaultMdhlFile = QString(":/resources/styles/default.mdhl");
const QString VConfigManager::c_solarizedDarkMdhlFile = QString(":/resources/styles/solarized-dark.mdhl");
const QString VConfigManager::c_solarizedLightMdhlFile = QString(":/resources/styles/solarized-light.mdhl");
@ -55,6 +57,7 @@ void VConfigManager::initialize()
// Override the default css styles on start up.
outputDefaultCssStyle();
outputDefaultCodeBlockCssStyle();
outputDefaultEditorStyle();
m_defaultEditPalette = QTextEdit().palette();
@ -63,6 +66,8 @@ void VConfigManager::initialize()
welcomePagePath = getConfigFromSettings("global", "welcome_page_path").toString();
m_templateCss = getConfigFromSettings("global", "template_css").toString();
m_templateCodeBlockCss = getConfigFromSettings("global", "template_code_block_css").toString();
m_templateCodeBlockCssUrl = getConfigFromSettings("global", "template_code_block_css_url").toString();
curNotebookIndex = getConfigFromSettings("global", "current_notebook").toInt();
markdownExtensions = hoedown_extensions(HOEDOWN_EXT_TABLES | HOEDOWN_EXT_FENCED_CODE |
@ -606,6 +611,11 @@ QString VConfigManager::getStyleConfigFolder() const
return getConfigFolder() + QDir::separator() + c_styleConfigFolder;
}
QString VConfigManager::getCodeBlockStyleConfigFolder() const
{
return getStyleConfigFolder() + QDir::separator() + c_codeBlockStyleConfigFolder;
}
QVector<QString> VConfigManager::getCssStyles() const
{
QVector<QString> res;
@ -627,6 +637,27 @@ QVector<QString> VConfigManager::getCssStyles() const
return res;
}
QVector<QString> VConfigManager::getCodeBlockCssStyles() const
{
QVector<QString> res;
QDir dir(getCodeBlockStyleConfigFolder());
if (!dir.exists()) {
// Output pre-defined CSS styles to this folder.
outputDefaultCodeBlockCssStyle();
}
// Get all the .css files in the folder.
dir.setFilter(QDir::Files | QDir::NoSymLinks);
dir.setNameFilters(QStringList("*.css"));
QStringList files = dir.entryList();
res.reserve(files.size());
for (auto const &item : files) {
res.push_back(item.left(item.size() - 4));
}
return res;
}
QVector<QString> VConfigManager::getEditorStyles() const
{
QVector<QString> res;
@ -651,15 +682,46 @@ QVector<QString> VConfigManager::getEditorStyles() const
bool VConfigManager::outputDefaultCssStyle() const
{
// Make sure the styles folder exists.
QDir dir(getConfigFolder());
if (!dir.exists(c_styleConfigFolder)) {
if (!dir.mkdir(c_styleConfigFolder)) {
QString folderPath = getStyleConfigFolder();
QDir dir(folderPath);
if (!dir.exists()) {
if (!dir.mkpath(folderPath)) {
return false;
}
}
QString srcPath = c_defaultCssFile;
QString destPath = getStyleConfigFolder() + QDir::separator() + QFileInfo(srcPath).fileName();
QString destPath = folderPath + QDir::separator() + QFileInfo(srcPath).fileName();
if (QFileInfo::exists(destPath)) {
QString bakPath = destPath + ".bak";
// We only keep one bak file.
if (!QFileInfo::exists(bakPath)) {
QFile::rename(destPath, bakPath);
} else {
// Just delete the default style.
QFile file(destPath);
file.setPermissions(QFile::ReadUser | QFile::WriteUser);
file.remove();
}
}
return VUtils::copyFile(srcPath, destPath, false);
}
bool VConfigManager::outputDefaultCodeBlockCssStyle() const
{
// Make sure the styles folder exists.
QString folderPath = getCodeBlockStyleConfigFolder();
QDir dir(folderPath);
if (!dir.exists()) {
if (!dir.mkpath(folderPath)) {
return false;
}
}
QString srcPath = c_defaultCodeBlockCssFile;
QString destPath = folderPath + QDir::separator() + QFileInfo(srcPath).fileName();
if (QFileInfo::exists(destPath)) {
QString bakPath = destPath + ".bak";
@ -753,6 +815,36 @@ QString VConfigManager::getTemplateCssUrl()
return cssPath;
}
// The URL will be used in the Web page.
QString VConfigManager::getTemplateCodeBlockCssUrl()
{
if (!m_templateCodeBlockCssUrl.isEmpty()) {
return m_templateCodeBlockCssUrl;
}
QString cssPath = getCodeBlockStyleConfigFolder() +
QDir::separator() +
m_templateCodeBlockCss + ".css";
QUrl cssUrl = QUrl::fromLocalFile(cssPath);
cssPath = cssUrl.toString();
if (!QFile::exists(cssUrl.toLocalFile())) {
// Specified css not exists.
if (m_templateCss == "vnote") {
bool ret = outputDefaultCodeBlockCssStyle();
if (!ret) {
// Use embedded file.
cssPath = "qrc" + c_defaultCodeBlockCssFile;
}
} else {
setTemplateCodeBlockCss("vnote");
return getTemplateCodeBlockCssUrl();
}
}
qDebug() << "use template code block css:" << cssPath;
return cssPath;
}
QString VConfigManager::getEditorStyleUrl()
{
QString mdhlPath = getStyleConfigFolder() + QDir::separator() + m_editorStyle + ".mdhl";
@ -775,20 +867,6 @@ QString VConfigManager::getEditorStyleUrl()
}
const QString &VConfigManager::getTemplateCss() const
{
return m_templateCss;
}
void VConfigManager::setTemplateCss(const QString &p_css)
{
if (m_templateCss == p_css) {
return;
}
m_templateCss = p_css;
setConfigToSettings("global", "template_css", m_templateCss);
}
const QString &VConfigManager::getEditorStyle() const
{
return m_editorStyle;

View File

@ -87,11 +87,16 @@ public:
QString getTemplateCssUrl();
QString getTemplateCodeBlockCssUrl();
QString getEditorStyleUrl();
const QString &getTemplateCss() const;
void setTemplateCss(const QString &p_css);
const QString &getTemplateCodeBlockCss() const;
void setTemplateCodeBlockCss(const QString &p_css);
const QString &getEditorStyle() const;
void setEditorStyle(const QString &p_style);
@ -283,6 +288,9 @@ public:
bool getDoubleClickCloseTab() const;
// Whether user specify template_code_block_css_url directly.
bool getUserSpecifyTemplateCodeBlockCssUrl() const;
// Return the configured key sequence of @p_operation.
// Return empty if there is no corresponding config.
QString getShortcutKeySequence(const QString &p_operation) const;
@ -299,6 +307,12 @@ public:
// Read all available css files in c_styleConfigFolder.
QVector<QString> getCssStyles() const;
// Get the folder c_codeBlockStyleConfigFolder in the config folder.
QString getCodeBlockStyleConfigFolder() const;
// Read all available css files in c_codeBlockStyleConfigFolder.
QVector<QString> getCodeBlockCssStyles() const;
// Read all available mdhl files in c_styleConfigFolder.
QVector<QString> getEditorStyles() const;
@ -326,7 +340,11 @@ private:
// This is for the change of org name.
void migrateIniFile();
// Output pre-defined CSS styles to style folder.
bool outputDefaultCssStyle() const;
bool outputDefaultCodeBlockCssStyle() const;
bool outputDefaultEditorStyle() const;
// See if the old c_obsoleteDirConfigFile exists. If so, rename it to
@ -363,7 +381,17 @@ private:
QHash<QString, QTextCharFormat> m_codeBlockStyles;
QString welcomePagePath;
// CSS style for Markdown template.
QString m_templateCss;
// Code block CSS style for Markdown template.
QString m_templateCodeBlockCss;
// Code block CSS style file URL for Markdown template.
// If not empty, VNote will ignore m_templateCodeBlockCss.
QString m_templateCodeBlockCssUrl;
QString m_editorStyle;
int curNotebookIndex;
@ -585,10 +613,19 @@ private:
QSettings *userSettings;
// Qsettings for @defaultConfigFileName
QSettings *defaultSettings;
// The folder name of style files.
static const QString c_styleConfigFolder;
// The folder name of code block style files.
static const QString c_codeBlockStyleConfigFolder;
// Default CSS file in resource system.
static const QString c_defaultCssFile;
// Default code block CSS file in resource system.
static const QString c_defaultCodeBlockCssFile;
// MDHL files for editor styles.
static const QString c_defaultMdhlFile;
static const QString c_solarizedDarkMdhlFile;
@ -1498,4 +1535,39 @@ inline bool VConfigManager::getDoubleClickCloseTab() const
return m_doubleClickCloseTab;
}
inline const QString &VConfigManager::getTemplateCss() const
{
return m_templateCss;
}
inline void VConfigManager::setTemplateCss(const QString &p_css)
{
if (m_templateCss == p_css) {
return;
}
m_templateCss = p_css;
setConfigToSettings("global", "template_css", m_templateCss);
}
inline const QString &VConfigManager::getTemplateCodeBlockCss() const
{
return m_templateCodeBlockCss;
}
inline void VConfigManager::setTemplateCodeBlockCss(const QString &p_css)
{
if (m_templateCodeBlockCss == p_css) {
return;
}
m_templateCodeBlockCss = p_css;
setConfigToSettings("global", "template_code_block_css", m_templateCodeBlockCss);
}
inline bool VConfigManager::getUserSpecifyTemplateCodeBlockCssUrl() const
{
return !m_templateCodeBlockCssUrl.isEmpty();
}
#endif // VCONFIGMANAGER_H

View File

@ -579,6 +579,8 @@ void VMainWindow::initMarkdownMenu()
initRenderBackgroundMenu(markdownMenu);
initCodeBlockStyleMenu(markdownMenu);
QAction *constrainImageAct = new QAction(tr("Constrain The Width of Images"), this);
constrainImageAct->setToolTip(tr("Constrain the width of images to the window in read mode (re-open current tabs to make it work)"));
constrainImageAct->setCheckable(true);
@ -1319,6 +1321,7 @@ void VMainWindow::updateRenderStyleMenu()
}
// Update the menu actions with styles.
QString curStyle = g_config->getTemplateCss();
QVector<QString> styles = g_config->getCssStyles();
for (auto const &style : styles) {
QAction *act = new QAction(style, m_renderStyleActs);
@ -1329,7 +1332,7 @@ void VMainWindow::updateRenderStyleMenu()
// Add it to the menu.
menu->addAction(act);
if (g_config->getTemplateCss() == style) {
if (curStyle == style) {
act->setChecked(true);
}
}
@ -1347,14 +1350,73 @@ void VMainWindow::initRenderStyleMenu(QMenu *p_menu)
this, &VMainWindow::setRenderStyle);
QAction *addAct = newAction(QIcon(":/resources/icons/add_style.svg"),
tr("&Add Style"), m_renderStyleActs);
addAct->setToolTip(tr("Open the folder to add your custom CSS style files"));
tr("&Add Style"),
m_renderStyleActs);
addAct->setToolTip(tr("Open the folder to add your custom CSS style files "
"for Markdown rendering"));
addAct->setCheckable(true);
addAct->setData("AddStyle");
styleMenu->addAction(addAct);
}
void VMainWindow::updateCodeBlockStyleMenu()
{
QMenu *menu = dynamic_cast<QMenu *>(sender());
V_ASSERT(menu);
QList<QAction *> actions = menu->actions();
// Remove all other actions except the first one.
for (int i = 1; i < actions.size(); ++i) {
menu->removeAction(actions[i]);
m_codeBlockStyleActs->removeAction(actions[i]);
delete actions[i];
}
// Update the menu actions with styles.
QString curStyle = g_config->getTemplateCodeBlockCss();
QVector<QString> styles = g_config->getCodeBlockCssStyles();
for (auto const &style : styles) {
QAction *act = new QAction(style, m_codeBlockStyleActs);
act->setToolTip(tr("Set as the code block CSS style for Markdown rendering"));
act->setCheckable(true);
act->setData(style);
// Add it to the menu.
menu->addAction(act);
if (curStyle == style) {
act->setChecked(true);
}
}
}
void VMainWindow::initCodeBlockStyleMenu(QMenu *p_menu)
{
QMenu *styleMenu = p_menu->addMenu(tr("Code Block Style"));
styleMenu->setToolTipsVisible(true);
connect(styleMenu, &QMenu::aboutToShow,
this, &VMainWindow::updateCodeBlockStyleMenu);
m_codeBlockStyleActs = new QActionGroup(this);
connect(m_codeBlockStyleActs, &QActionGroup::triggered,
this, &VMainWindow::setCodeBlockStyle);
QAction *addAct = newAction(QIcon(":/resources/icons/add_style.svg"),
tr("&Add Style"),
m_codeBlockStyleActs);
addAct->setToolTip(tr("Open the folder to add your custom CSS style files "
"for Markdown code block rendering"));
addAct->setCheckable(true);
addAct->setData("AddStyle");
styleMenu->addAction(addAct);
if (g_config->getUserSpecifyTemplateCodeBlockCssUrl()) {
styleMenu->setEnabled(false);
}
}
void VMainWindow::initEditorBackgroundMenu(QMenu *menu)
{
QMenu *backgroundColorMenu = menu->addMenu(tr("&Background Color"));
@ -1537,6 +1599,23 @@ void VMainWindow::setEditorStyle(QAction *p_action)
}
}
void VMainWindow::setCodeBlockStyle(QAction *p_action)
{
if (!p_action) {
return;
}
QString data = p_action->data().toString();
if (data == "AddStyle") {
// Add custom style.
QUrl url = QUrl::fromLocalFile(g_config->getCodeBlockStyleConfigFolder());
QDesktopServices::openUrl(url);
} else {
g_config->setTemplateCodeBlockCss(data);
vnote->updateTemplate();
}
}
void VMainWindow::updateActionStateFromTabStatusChange(const VFile *p_file,
bool p_editMode)
{

View File

@ -83,10 +83,22 @@ private slots:
void setTabStopWidth(QAction *action);
void setEditorBackgroundColor(QAction *action);
void setRenderBackgroundColor(QAction *action);
void setRenderStyle(QAction *p_action);
void setEditorStyle(QAction *p_action);
// Set code block render style.
void setCodeBlockStyle(QAction *p_action);
// Update the render styles menu according to existing files.
void updateRenderStyleMenu();
void updateEditorStyleMenu();
// Update the code block styles menu according to existing files.
void updateCodeBlockStyleMenu();
void changeHighlightCursorLine(bool p_checked);
void changeHighlightSelectedWord(bool p_checked);
void changeHighlightSearchedWord(bool p_checked);
@ -161,7 +173,11 @@ private:
void initAvatar();
void initPredefinedColorPixmaps();
void initRenderBackgroundMenu(QMenu *menu);
void initRenderStyleMenu(QMenu *p_menu);
void initCodeBlockStyleMenu(QMenu *p_menu);
void initConverterMenu(QMenu *p_menu);
void initMarkdownitOptionMenu(QMenu *p_menu);
void initEditorBackgroundMenu(QMenu *menu);
@ -245,9 +261,14 @@ private:
QAction *m_autoIndentAct;
// Act group for render styles.
QActionGroup *m_renderStyleActs;
QActionGroup *m_editorStyleActs;
// Act group for code block render styles.
QActionGroup *m_codeBlockStyleActs;
QShortcut *m_closeNoteShortcut;
// Menus

View File

@ -180,6 +180,7 @@ void VNote::updateTemplate()
}
}
}
QString cssStyle;
if (!rgb.isEmpty()) {
cssStyle += "body { background-color: #" + rgb + " !important; }\n";
@ -192,8 +193,12 @@ void VNote::updateTemplate()
const QString styleHolder("<!-- BACKGROUND_PLACE_HOLDER -->");
const QString cssHolder("CSS_PLACE_HOLDER");
const QString codeBlockCssHolder("HIGHLIGHTJS_CSS_PLACE_HOLDER");
s_markdownTemplate = VUtils::readFileFromDisk(c_markdownTemplatePath);
// Must replace the code block holder first.
s_markdownTemplate.replace(codeBlockCssHolder, g_config->getTemplateCodeBlockCssUrl());
s_markdownTemplate.replace(cssHolder, g_config->getTemplateCssUrl());
s_markdownTemplatePDF = s_markdownTemplate;