support section number in edit mode

This commit is contained in:
Le Tan 2020-12-24 20:18:37 +08:00
parent 1f7acf2bc5
commit 4636771081
26 changed files with 535 additions and 75 deletions

@ -1 +1 @@
Subproject commit 12a74cb32438b28f002dea51c484133d3058a882
Subproject commit 47347580cbcd460ebbbc8358e37b409530edaa2f

View File

@ -72,6 +72,13 @@ namespace vnotex
IncrementalSearch = 0x10U
};
Q_DECLARE_FLAGS(FindOptions, FindOption);
enum OverrideState
{
NoOverride = 0,
ForceEnable = 1,
ForceDisable = 2
};
} // ns vnotex
Q_DECLARE_OPERATORS_FOR_FLAGS(vnotex::FindOptions);

View File

@ -155,7 +155,7 @@ void HtmlTemplateHelper::updateMarkdownViewerTemplate(const MarkdownEditorConfig
WebGlobalOptions opts;
opts.m_webPlantUml = p_config.getWebPlantUml();
opts.m_webGraphviz = p_config.getWebGraphviz();
opts.m_sectionNumberEnabled = p_config.getSectionNumberEnabled();
opts.m_sectionNumberEnabled = p_config.getSectionNumberMode() == MarkdownEditorConfig::SectionNumberMode::Read;
opts.m_constrainImageWidthEnabled = p_config.getConstrainImageWidthEnabled();
opts.m_protectFromXss = p_config.getProtectFromXss();
opts.m_htmlTagEnabled = p_config.getHtmlTagEnabled();

View File

@ -10,6 +10,7 @@ using namespace vnotex;
#define READSTR(key) readString(appObj, userObj, (key))
#define READBOOL(key) readBool(appObj, userObj, (key))
#define READREAL(key) readReal(appObj, userObj, (key))
#define READINT(key) readInt(appObj, userObj, (key))
MarkdownEditorConfig::MarkdownEditorConfig(ConfigMgr *p_mgr,
IConfig *p_topConfig,
@ -33,7 +34,10 @@ void MarkdownEditorConfig::init(const QJsonObject &p_app, const QJsonObject &p_u
m_prependDotInRelativeLink = READBOOL(QStringLiteral("prepend_dot_in_relative_link"));
m_confirmBeforeClearObsoleteImages = READBOOL(QStringLiteral("confirm_before_clear_obsolete_images"));
m_insertFileNameAsTitle = READBOOL(QStringLiteral("insert_file_name_as_title"));
m_sectionNumberEnabled = READBOOL(QStringLiteral("section_number"));
m_sectionNumberMode = stringToSectionNumberMode(READSTR(QStringLiteral("section_number")));
m_sectionNumberBaseLevel = READINT(QStringLiteral("section_number_base_level"));
m_constrainImageWidthEnabled = READBOOL(QStringLiteral("constrain_image_width"));
m_constrainInPlacePreviewWidthEnabled = READBOOL(QStringLiteral("constrain_inplace_preview_width"));
m_zoomFactorInReadMode = READREAL(QStringLiteral("zoom_factor_in_read_mode"));
@ -53,7 +57,10 @@ QJsonObject MarkdownEditorConfig::toJson() const
obj[QStringLiteral("prepend_dot_in_relative_link")] = m_prependDotInRelativeLink;
obj[QStringLiteral("confirm_before_clear_obsolete_images")] = m_confirmBeforeClearObsoleteImages;
obj[QStringLiteral("insert_file_name_as_title")] = m_insertFileNameAsTitle;
obj[QStringLiteral("section_number")] = m_sectionNumberEnabled;
obj[QStringLiteral("section_number")] = sectionNumberModeToString(m_sectionNumberMode);
obj[QStringLiteral("section_number_base_level")] = m_sectionNumberBaseLevel;
obj[QStringLiteral("constrain_image_width")] = m_constrainImageWidthEnabled;
obj[QStringLiteral("constrain_inplace_preview_width")] = m_constrainInPlacePreviewWidthEnabled;
obj[QStringLiteral("zoom_factor_in_read_mode")] = m_zoomFactorInReadMode;
@ -147,16 +154,6 @@ void MarkdownEditorConfig::setInsertFileNameAsTitle(bool p_enabled)
updateConfig(m_insertFileNameAsTitle, p_enabled, this);
}
bool MarkdownEditorConfig::getSectionNumberEnabled() const
{
return m_sectionNumberEnabled;
}
void MarkdownEditorConfig::setSectionNumberEnabled(bool p_enabled)
{
updateConfig(m_sectionNumberEnabled, p_enabled, this);
}
bool MarkdownEditorConfig::getConstrainImageWidthEnabled() const
{
return m_constrainImageWidthEnabled;
@ -231,3 +228,49 @@ void MarkdownEditorConfig::setLinkifyEnabled(bool p_enabled)
{
updateConfig(m_linkifyEnabled, p_enabled, this);
}
QString MarkdownEditorConfig::sectionNumberModeToString(SectionNumberMode p_mode) const
{
switch (p_mode) {
case SectionNumberMode::None:
return QStringLiteral("none");
case SectionNumberMode::Edit:
return QStringLiteral("edit");
default:
return QStringLiteral("read");
}
}
MarkdownEditorConfig::SectionNumberMode MarkdownEditorConfig::stringToSectionNumberMode(const QString &p_str) const
{
auto mode = p_str.toLower();
if (mode == QStringLiteral("none")) {
return SectionNumberMode::None;
} else if (mode == QStringLiteral("edit")) {
return SectionNumberMode::Edit;
} else {
return SectionNumberMode::Read;
}
}
MarkdownEditorConfig::SectionNumberMode MarkdownEditorConfig::getSectionNumberMode() const
{
return m_sectionNumberMode;
}
void MarkdownEditorConfig::setSectionNumberMode(SectionNumberMode p_mode)
{
updateConfig(m_sectionNumberMode, p_mode, this);
}
int MarkdownEditorConfig::getSectionNumberBaseLevel() const
{
return m_sectionNumberBaseLevel;
}
void MarkdownEditorConfig::setSectionNumberBaseLevel(int p_level)
{
updateConfig(m_sectionNumberBaseLevel, p_level, this);
}

View File

@ -15,6 +15,13 @@ namespace vnotex
class MarkdownEditorConfig : public IConfig
{
public:
enum SectionNumberMode
{
None,
Read,
Edit
};
MarkdownEditorConfig(ConfigMgr *p_mgr,
IConfig *p_topConfig,
const QSharedPointer<TextEditorConfig> &p_textEditorConfig);
@ -45,8 +52,11 @@ namespace vnotex
bool getInsertFileNameAsTitle() const;
void setInsertFileNameAsTitle(bool p_enabled);
bool getSectionNumberEnabled() const;
void setSectionNumberEnabled(bool p_enabled);
SectionNumberMode getSectionNumberMode() const;
void setSectionNumberMode(SectionNumberMode p_mode);
int getSectionNumberBaseLevel() const;
void setSectionNumberBaseLevel(int p_level);
bool getConstrainImageWidthEnabled() const;
void setConstrainImageWidthEnabled(bool p_enabled);
@ -72,6 +82,9 @@ namespace vnotex
void setLinkifyEnabled(bool p_enabled);
private:
QString sectionNumberModeToString(SectionNumberMode p_mode) const;
SectionNumberMode stringToSectionNumberMode(const QString &p_str) const;
QSharedPointer<TextEditorConfig> m_textEditorConfig;
ViewerResource m_viewerResource;
@ -91,7 +104,10 @@ namespace vnotex
bool m_insertFileNameAsTitle = true;
// Whether enable section numbering.
bool m_sectionNumberEnabled = true;
SectionNumberMode m_sectionNumberMode = SectionNumberMode::Read;
// 1 based.
int m_sectionNumberBaseLevel = 2;
// Whether enable image width constraint.
bool m_constrainImageWidthEnabled = true;

View File

@ -72,6 +72,7 @@
<file>icons/stay_on_top.svg</file>
<file>icons/outline_editor.svg</file>
<file>icons/find_replace_editor.svg</file>
<file>icons/section_number_editor.svg</file>
<file>logo/vnote.svg</file>
<file>logo/vnote.png</file>
<file>logo/256x256/vnote.png</file>

View File

@ -0,0 +1,7 @@
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
<g>
<title>Layer 2</title>
<text stroke="#000000" transform="matrix(11.892995768998892,0,0,12.532065948318316,-399.2455647895517,-874.5950374851516) " xml:space="preserve" text-anchor="middle" font-family="Sans-serif" font-size="24" id="svg_1" y="98.48198" x="55.1146" stroke-width="0" fill="#000000">1.2.</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 502 B

View File

@ -226,8 +226,10 @@
"confirm_before_clear_obsolete_images" : true,
"//comment" : "Whether insert the file name as title on new file",
"insert_file_name_as_title" : true,
"//comment" : "Whether enable auto section numbering",
"section_number" : true,
"//comment" : "none/read/edit",
"section_number" : "read",
"//comment" : "base level to start section numbering, valid only in edit mode",
"section_number_base_level" : 2,
"//comment" : "Whether enable image width constraint",
"constrain_image_width" : true,
"//comment" : "Whether enable in-place preview width constraint",

View File

@ -5,6 +5,9 @@
#include <QCheckBox>
#include <QGroupBox>
#include <QDoubleSpinBox>
#include <QComboBox>
#include <QSpinBox>
#include <QHBoxLayout>
#include <widgets/widgetsfactory.h>
#include <core/editorconfig.h>
@ -25,6 +28,9 @@ void MarkdownEditorPage::setupUI()
{
auto mainLayout = new QVBoxLayout(this);
auto generalBox = setupGeneralGroup();
mainLayout->addWidget(generalBox);
auto readBox = setupReadGroup();
mainLayout->addWidget(readBox);
@ -38,7 +44,13 @@ void MarkdownEditorPage::loadInternal()
m_insertFileNameAsTitleCheckBox->setChecked(markdownConfig.getInsertFileNameAsTitle());
m_sectionNumberCheckBox->setChecked(markdownConfig.getSectionNumberEnabled());
{
int idx = m_sectionNumberComboBox->findData(static_cast<int>(markdownConfig.getSectionNumberMode()));
Q_ASSERT(idx != -1);
m_sectionNumberComboBox->setCurrentIndex(idx);
m_sectionNumberBaseLevelSpinBox->setValue(markdownConfig.getSectionNumberBaseLevel());
}
m_constrainImageWidthCheckBox->setChecked(markdownConfig.getConstrainImageWidthEnabled());
@ -61,7 +73,14 @@ void MarkdownEditorPage::saveInternal()
markdownConfig.setInsertFileNameAsTitle(m_insertFileNameAsTitleCheckBox->isChecked());
markdownConfig.setSectionNumberEnabled(m_sectionNumberCheckBox->isChecked());
{
auto mode = m_sectionNumberComboBox->currentData().toInt();
markdownConfig.setSectionNumberMode(static_cast<MarkdownEditorConfig::SectionNumberMode>(mode));
if (m_sectionNumberBaseLevelSpinBox->isEnabled()) {
markdownConfig.setSectionNumberBaseLevel(m_sectionNumberBaseLevelSpinBox->value());
}
}
markdownConfig.setConstrainImageWidthEnabled(m_constrainImageWidthCheckBox->isChecked());
@ -90,16 +109,6 @@ QGroupBox *MarkdownEditorPage::setupReadGroup()
auto box = new QGroupBox(tr("Read"), this);
auto layout = new QFormLayout(box);
{
const QString label(tr("Section number"));
m_sectionNumberCheckBox = WidgetsFactory::createCheckBox(label, box);
m_sectionNumberCheckBox->setToolTip(tr("Display section number of headings in read mode"));
layout->addRow(m_sectionNumberCheckBox);
addSearchItem(label, m_sectionNumberCheckBox->toolTip(), m_sectionNumberCheckBox);
connect(m_sectionNumberCheckBox, &QCheckBox::stateChanged,
this, &MarkdownEditorPage::pageIsChanged);
}
{
const QString label(tr("Constrain image width"));
m_constrainImageWidthCheckBox = WidgetsFactory::createCheckBox(label, box);
@ -194,3 +203,43 @@ QGroupBox *MarkdownEditorPage::setupEditGroup()
return box;
}
QGroupBox *MarkdownEditorPage::setupGeneralGroup()
{
auto box = new QGroupBox(tr("General"), this);
auto layout = new QFormLayout(box);
{
auto sectionLayout = new QHBoxLayout();
m_sectionNumberComboBox = WidgetsFactory::createComboBox(this);
m_sectionNumberComboBox->setToolTip(tr("Section number mode"));
m_sectionNumberComboBox->addItem(tr("None"), (int)MarkdownEditorConfig::SectionNumberMode::None);
m_sectionNumberComboBox->addItem(tr("Read"), (int)MarkdownEditorConfig::SectionNumberMode::Read);
m_sectionNumberComboBox->addItem(tr("Edit"), (int)MarkdownEditorConfig::SectionNumberMode::Edit);
sectionLayout->addWidget(m_sectionNumberComboBox);
connect(m_sectionNumberComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &MarkdownEditorPage::pageIsChanged);
m_sectionNumberBaseLevelSpinBox = WidgetsFactory::createSpinBox(this);
m_sectionNumberBaseLevelSpinBox->setToolTip(tr("Base level to start section numbering in edit mode"));
m_sectionNumberBaseLevelSpinBox->setRange(1, 6);
m_sectionNumberBaseLevelSpinBox->setSingleStep(1);
sectionLayout->addWidget(m_sectionNumberBaseLevelSpinBox);
connect(m_sectionNumberBaseLevelSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
this, &MarkdownEditorPage::pageIsChanged);
connect(m_sectionNumberComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, [this](int p_index) {
m_sectionNumberBaseLevelSpinBox->setEnabled(p_index == MarkdownEditorConfig::SectionNumberMode::Edit);
});
const QString label(tr("Section number:"));
layout->addRow(label, sectionLayout);
addSearchItem(label, m_sectionNumberComboBox->toolTip(), m_sectionNumberComboBox);
}
return box;
}

View File

@ -6,6 +6,8 @@
class QCheckBox;
class QGroupBox;
class QDoubleSpinBox;
class QSpinBox;
class QComboBox;
namespace vnotex
{
@ -25,14 +27,14 @@ namespace vnotex
private:
void setupUI();
QGroupBox *setupGeneralGroup();
QGroupBox *setupReadGroup();
QGroupBox *setupEditGroup();
QCheckBox *m_insertFileNameAsTitleCheckBox = nullptr;
QCheckBox *m_sectionNumberCheckBox = nullptr;
QCheckBox *m_constrainImageWidthCheckBox = nullptr;
QCheckBox *m_constrainInPlacePreviewWidthCheckBox = nullptr;
@ -46,6 +48,10 @@ namespace vnotex
QCheckBox *m_linkifyCheckBox = nullptr;
QDoubleSpinBox *m_zoomFactorSpinBox = nullptr;
QComboBox *m_sectionNumberComboBox = nullptr;
QSpinBox *m_sectionNumberBaseLevelSpinBox = nullptr;
};
}

View File

@ -13,6 +13,7 @@
#include "texteditorpage.h"
#include "markdowneditorpage.h"
#include "appearancepage.h"
#include "themepage.h"
using namespace vnotex;
@ -72,45 +73,36 @@ void SettingsDialog::setupPages()
// General.
{
auto page = new GeneralPage(this);
m_pageLayout->addWidget(page);
auto item = new QTreeWidgetItem(m_pageExplorer);
setupPage(item, page);
addPage(page);
}
// Appearance.
{
auto page = new AppearancePage(this);
m_pageLayout->addWidget(page);
auto item = addPage(page);
auto item = new QTreeWidgetItem(m_pageExplorer);
setupPage(item, page);
// Theme.
{
auto subPage = new ThemePage(this);
addSubPage(subPage, item);
}
}
// Editor.
{
auto page = new EditorPage(this);
m_pageLayout->addWidget(page);
auto item = new QTreeWidgetItem(m_pageExplorer);
setupPage(item, page);
auto item = addPage(page);
// Text Editor.
{
auto subPage = new TextEditorPage(this);
m_pageLayout->addWidget(subPage);
auto subItem = new QTreeWidgetItem(item);
setupPage(subItem, subPage);
addSubPage(subPage, item);
}
// Markdown Editor.
{
auto subPage = new MarkdownEditorPage(this);
m_pageLayout->addWidget(subPage);
auto subItem = new QTreeWidgetItem(item);
setupPage(subItem, subPage);
addSubPage(subPage, item);
}
}
@ -191,3 +183,21 @@ void SettingsDialog::forEachPage(const std::function<void(SettingsPage *)> &p_fu
p_func(page);
}
}
QTreeWidgetItem *SettingsDialog::addPage(SettingsPage *p_page)
{
m_pageLayout->addWidget(p_page);
auto item = new QTreeWidgetItem(m_pageExplorer);
setupPage(item, p_page);
return item;
}
QTreeWidgetItem *SettingsDialog::addSubPage(SettingsPage *p_page, QTreeWidgetItem *p_parentItem)
{
m_pageLayout->addWidget(p_page);
auto subItem = new QTreeWidgetItem(p_parentItem);
setupPage(subItem, p_page);
return subItem;
}

View File

@ -44,6 +44,10 @@ namespace vnotex
void forEachPage(const std::function<void(SettingsPage *)> &p_func);
QTreeWidgetItem *addPage(SettingsPage *p_page);
QTreeWidgetItem *addSubPage(SettingsPage *p_page, QTreeWidgetItem *p_parentItem);
QLineEdit *m_searchEdit = nullptr;
QTreeWidget *m_pageExplorer = nullptr;

View File

@ -0,0 +1,32 @@
#include "themepage.h"
#include <QComboBox>
#include <QVBoxLayout>
#include <widgets/widgetsfactory.h>
using namespace vnotex;
ThemePage::ThemePage(QWidget *p_parent)
: SettingsPage(p_parent)
{
setupUI();
}
void ThemePage::setupUI()
{
auto mainLayout = new QVBoxLayout(this);
}
void ThemePage::loadInternal()
{
}
void ThemePage::saveInternal()
{
}
QString ThemePage::title() const
{
return tr("Theme");
}

View File

@ -0,0 +1,26 @@
#ifndef THEMEPAGE_H
#define THEMEPAGE_H
#include "settingspage.h"
namespace vnotex
{
class ThemePage : public SettingsPage
{
Q_OBJECT
public:
explicit ThemePage(QWidget *p_parent = nullptr);
QString title() const Q_DECL_OVERRIDE;
protected:
void loadInternal() Q_DECL_OVERRIDE;
void saveInternal() Q_DECL_OVERRIDE;
private:
void setupUI();
};
}
#endif // THEMEPAGE_H

View File

@ -37,6 +37,7 @@
#include <utils/textutils.h>
#include <core/exception.h>
#include <core/markdowneditorconfig.h>
#include <core/texteditorconfig.h>
#include <core/configmgr.h>
#include <core/editorconfig.h>
@ -48,9 +49,13 @@ using namespace vnotex;
// We set the property of the clipboard to mark that we are requesting a rich paste.
static const char *c_clipboardPropertyMark = "RichPaste";
MarkdownEditor::Heading::Heading(const QString &p_name, int p_level, int p_blockNumber)
MarkdownEditor::Heading::Heading(const QString &p_name,
int p_level,
const QString &p_sectionNumber,
int p_blockNumber)
: m_name(p_name),
m_level(p_level),
m_sectionNumber(p_sectionNumber),
m_blockNumber(p_blockNumber)
{
}
@ -71,17 +76,25 @@ MarkdownEditor::MarkdownEditor(const MarkdownEditorConfig &p_config,
this, &MarkdownEditor::handleContextMenuEvent);
connect(getHighlighter(), &vte::PegMarkdownHighlighter::headersUpdated,
this, [this](const QVector<vte::peg::ElementRegion> &p_headerRegions) {
// TODO: insert heading sequence.
updateHeadings(p_headerRegions);
});
this, &MarkdownEditor::updateHeadings);
m_headingTimer = new QTimer(this);
m_headingTimer->setInterval(500);
m_headingTimer->setSingleShot(true);
connect(m_headingTimer, &QTimer::timeout,
this, &MarkdownEditor::currentHeadingChanged);
connect(m_textEdit, &vte::VTextEdit::cursorLineChanged,
m_headingTimer, QOverload<>::of(&QTimer::start));
m_sectionNumberTimer = new QTimer(this);
m_sectionNumberTimer->setInterval(1000);
m_sectionNumberTimer->setSingleShot(true);
connect(m_sectionNumberTimer, &QTimer::timeout,
this, [this]() {
updateSectionNumber(m_headings);
});
updateFromConfig(false);
}
MarkdownEditor::~MarkdownEditor()
@ -719,13 +732,29 @@ int MarkdownEditor::getCurrentHeadingIndex() const
void MarkdownEditor::updateHeadings(const QVector<vte::peg::ElementRegion> &p_headerRegions)
{
auto doc = document();
bool needUpdateSectionNumber = false;
if (isReadOnly()) {
m_sectionNumberEnabled = false;
} else {
needUpdateSectionNumber = m_config.getSectionNumberMode() == MarkdownEditorConfig::SectionNumberMode::Edit;
if (m_overriddenSectionNumber != OverrideState::NoOverride) {
needUpdateSectionNumber = m_overriddenSectionNumber == OverrideState::ForceEnable;
}
if (needUpdateSectionNumber) {
m_sectionNumberEnabled = true;
} else if (m_sectionNumberEnabled) {
// On -> Off. We still need to do the clean up.
needUpdateSectionNumber = true;
m_sectionNumberEnabled = false;
}
}
QVector<Heading> headings;
headings.reserve(p_headerRegions.size());
// Assume that each block contains only one line.
// Only support # syntax for now.
auto doc = document();
QRegExp headerReg(vte::MarkdownUtils::c_headerRegExp);
for (auto const &reg : p_headerRegions) {
auto block = doc->findBlock(reg.m_startPos);
@ -738,14 +767,22 @@ void MarkdownEditor::updateHeadings(const QVector<vte::peg::ElementRegion> &p_he
}
if (headerReg.exactMatch(block.text())) {
int level = headerReg.cap(1).length();
Heading heading(headerReg.cap(2).trimmed(), level, block.blockNumber());
const int level = headerReg.cap(1).length();
Heading heading(headerReg.cap(2).trimmed(),
level,
headerReg.cap(3),
block.blockNumber());
headings.append(heading);
}
}
OutlineProvider::makePerfectHeadings(headings, m_headings);
if (needUpdateSectionNumber) {
// Use a timer to kick off the update to let user have time to undo.
m_sectionNumberTimer->start();
}
emit headingsChanged();
emit currentHeadingChanged();
@ -1045,3 +1082,121 @@ void MarkdownEditor::fetchImagesToLocalAndReplace(QString &p_text)
proDlg.setValue(regs.size());
}
static void increaseSectionNumber(QVector<int> &p_sectionNumber, int p_level, int p_baseLevel)
{
Q_ASSERT(p_level >= 1 && p_level < p_sectionNumber.size());
if (p_level < p_baseLevel) {
p_sectionNumber.fill(0);
return;
}
++p_sectionNumber[p_level];
for (int i = p_level + 1; i < p_sectionNumber.size(); ++i) {
p_sectionNumber[i] = 0;
}
}
static QString joinSectionNumberStr(const QVector<int> &p_sectionNumber)
{
QString res;
for (auto sec : p_sectionNumber) {
if (sec != 0) {
res = res + QString::number(sec) + '.';
} else if (res.isEmpty()) {
continue;
} else {
break;
}
}
return res;
}
static bool updateHeadingSectionNumber(QTextCursor &p_cursor,
const QTextBlock &p_block,
QRegExp &p_headingReg,
QRegExp &p_prefixReg,
const QString &p_sectionNumber)
{
if (!p_block.isValid()) {
return false;
}
QString text = p_block.text();
bool matched = p_headingReg.exactMatch(text);
Q_ASSERT(matched);
matched = p_prefixReg.exactMatch(text);
Q_ASSERT(matched);
int start = p_headingReg.cap(1).length() + 1;
int end = p_prefixReg.cap(1).length();
Q_ASSERT(start <= end);
p_cursor.setPosition(p_block.position() + start);
if (start != end) {
p_cursor.setPosition(p_block.position() + end, QTextCursor::KeepAnchor);
}
if (p_sectionNumber.isEmpty()) {
p_cursor.removeSelectedText();
} else {
p_cursor.insertText(p_sectionNumber + ' ');
}
return true;
}
bool MarkdownEditor::updateSectionNumber(const QVector<Heading> &p_headings)
{
QVector<int> sectionNumber(7, 0);
int baseLevel = m_config.getSectionNumberBaseLevel();
if (baseLevel < 1 || baseLevel > 6) {
baseLevel = 1;
}
bool changed = false;
auto doc = document();
QRegExp headerReg(vte::MarkdownUtils::c_headerRegExp);
QRegExp prefixReg(vte::MarkdownUtils::c_headerPrefixRegExp);
QTextCursor cursor(doc);
cursor.beginEditBlock();
for (const auto &heading : p_headings) {
increaseSectionNumber(sectionNumber, heading.m_level, baseLevel);
auto sectionStr = m_sectionNumberEnabled ? joinSectionNumberStr(sectionNumber) : QString();
if (heading.m_blockNumber > -1 && sectionStr != heading.m_sectionNumber) {
if (updateHeadingSectionNumber(cursor,
doc->findBlockByNumber(heading.m_blockNumber),
headerReg,
prefixReg,
sectionStr)) {
changed = true;
}
}
}
cursor.endEditBlock();
return changed;
}
void MarkdownEditor::overrideSectionNumber(OverrideState p_state)
{
if (m_overriddenSectionNumber == p_state) {
return;
}
m_overriddenSectionNumber = p_state;
getHighlighter()->updateHighlight();
}
void MarkdownEditor::updateFromConfig(bool p_initialized)
{
if (m_config.getTextEditorConfig().getZoomDelta() != 0) {
zoom(m_config.getTextEditorConfig().getZoomDelta());
}
if (p_initialized) {
getHighlighter()->updateHighlight();
}
}

View File

@ -31,12 +31,18 @@ namespace vnotex
{
Heading() = default;
Heading(const QString &p_name, int p_level, int p_blockNumber = -1);
Heading(const QString &p_name,
int p_level,
const QString &p_sectionNumber = QString(),
int p_blockNumber = -1);
QString m_name;
int m_level = -1;
// 1.2. .
QString m_sectionNumber;
int m_blockNumber = -1;
};
@ -84,6 +90,10 @@ namespace vnotex
void scrollToHeading(int p_idx);
void overrideSectionNumber(OverrideState p_state);
void updateFromConfig(bool p_initialized = true);
public slots:
void handleHtmlToMarkdownData(quint64 p_id, TimeStamp p_timeStamp, const QString &p_text);
@ -148,6 +158,8 @@ namespace vnotex
QString getRelativeLink(const QString &p_path);
// Update section number.
// Update headings outline.
void updateHeadings(const QVector<vte::peg::ElementRegion> &p_headerRegions);
int getHeadingIndexByBlockNumber(int p_blockNumber) const;
@ -156,6 +168,9 @@ namespace vnotex
void fetchImagesToLocalAndReplace(QString &p_text);
// Return true if there is change.
bool updateSectionNumber(const QVector<Heading> &p_headings);
static QString generateImageFileNameToInsertAs(const QString &p_title, const QString &p_suffix);
const MarkdownEditorConfig &m_config;
@ -168,6 +183,13 @@ namespace vnotex
TimeStamp m_timeStamp = 0;
QTimer *m_headingTimer = nullptr;
QTimer *m_sectionNumberTimer = nullptr;
// Used to detect the config change and do a clean up.
bool m_sectionNumberEnabled = true;
OverrideState m_overriddenSectionNumber = OverrideState::NoOverride;
};
}

View File

@ -292,6 +292,7 @@ void MainWindow::closeEvent(QCloseEvent *p_event)
isExit = true;
#endif
bool needShowMessage = false;
if(!isExit && toTray == -1){
int ret = MessageBoxHelper::questionYesNo(MessageBoxHelper::Question,
tr("Do you want to minimize %1 to system tray "
@ -301,6 +302,7 @@ void MainWindow::closeEvent(QCloseEvent *p_event)
this);
if (ret == QMessageBox::Yes) {
ConfigMgr::getInst().getSessionConfig().setMinimizeToSystemTray(true);
needShowMessage = true;
} else if (ret == QMessageBox::No) {
ConfigMgr::getInst().getSessionConfig().setMinimizeToSystemTray(false);
isExit = true;
@ -330,6 +332,9 @@ void MainWindow::closeEvent(QCloseEvent *p_event)
} else {
hide();
p_event->ignore();
if (needShowMessage) {
m_trayIcon->showMessage(ConfigMgr::c_appName, tr("%1 is still running here.").arg(ConfigMgr::c_appName));
}
}
}

View File

@ -186,9 +186,7 @@ void MarkdownViewWindow::handleEditorConfigChange()
auto config = createMarkdownEditorConfig(markdownEditorConfig);
m_editor->setConfig(config);
if (markdownEditorConfig.getTextEditorConfig().getZoomDelta() != 0) {
m_editor->zoom(markdownEditorConfig.getTextEditorConfig().getZoomDelta());
}
m_editor->updateFromConfig();
}
updateWebViewerConfig(markdownEditorConfig);
@ -269,6 +267,8 @@ void MarkdownViewWindow::setupToolBar()
toolBar->addSeparator();
addAction(toolBar, ViewWindowToolBarHelper::SectionNumber);
addAction(toolBar, ViewWindowToolBarHelper::TypeHeading);
addAction(toolBar, ViewWindowToolBarHelper::TypeBold);
addAction(toolBar, ViewWindowToolBarHelper::TypeItalic);
@ -306,10 +306,6 @@ void MarkdownViewWindow::setupTextEditor()
this);
m_splitter->insertWidget(0, m_editor);
if (markdownEditorConfig.getTextEditorConfig().getZoomDelta() != 0) {
m_editor->zoom(markdownEditorConfig.getTextEditorConfig().getZoomDelta());
}
TextViewWindowHelper::connectEditor(this);
// Status widget.
@ -683,6 +679,13 @@ void MarkdownViewWindow::handleTypeAction(TypeAction p_action)
}
}
void MarkdownViewWindow::handleSectionNumberOverride(OverrideState p_state)
{
if (m_mode != Mode::Read) {
m_editor->overrideSectionNumber(p_state);
}
}
void MarkdownViewWindow::detachFromBufferInternal()
{
auto buffer = getBuffer();

View File

@ -49,6 +49,8 @@ namespace vnotex
void handleTypeAction(TypeAction p_action) Q_DECL_OVERRIDE;
void handleSectionNumberOverride(OverrideState p_state) Q_DECL_OVERRIDE;
void handleFindTextChanged(const QString &p_text, FindOptions p_options) Q_DECL_OVERRIDE;
void handleFindNext(const QString &p_text, FindOptions p_options) Q_DECL_OVERRIDE;

View File

@ -38,9 +38,7 @@ void TextViewWindow::setupUI()
m_editor = new TextEditor(config, this);
setCentralWidget(m_editor);
if (textEditorConfig.getZoomDelta() != 0) {
m_editor->zoom(textEditorConfig.getZoomDelta());
}
updateEditorFromConfig();
}
TextViewWindowHelper::connectEditor(this);
@ -135,9 +133,7 @@ void TextViewWindow::handleEditorConfigChange()
auto config = createTextEditorConfig(textEditorConfig);
m_editor->setConfig(config);
if (textEditorConfig.getZoomDelta() != 0) {
m_editor->zoom(textEditorConfig.getZoomDelta());
}
updateEditorFromConfig();
}
}
@ -209,3 +205,12 @@ void TextViewWindow::handleFindAndReplaceWidgetClosed()
{
TextViewWindowHelper::handleFindAndReplaceWidgetClosed(this);
}
void TextViewWindow::updateEditorFromConfig()
{
const auto &editorConfig = ConfigMgr::getInst().getEditorConfig();
const auto &textEditorConfig = editorConfig.getTextEditorConfig();
if (textEditorConfig.getZoomDelta() != 0) {
m_editor->zoom(textEditorConfig.getZoomDelta());
}
}

View File

@ -64,6 +64,8 @@ namespace vnotex
// call this function to tell us now the latest buffer revision.
void setBufferRevisionAfterInvalidation(int p_bufferRevision);
void updateEditorFromConfig();
static QSharedPointer<vte::TextEditorConfig> createTextEditorConfig(const TextEditorConfig &p_config);
// Managed by QObject.

View File

@ -408,6 +408,23 @@ QAction *ViewWindow::addAction(QToolBar *p_toolBar, ViewWindowToolBarHelper::Act
});
break;
}
case ViewWindowToolBarHelper::SectionNumber:
{
act = ViewWindowToolBarHelper::addAction(p_toolBar, p_action);
connect(this, &ViewWindow::modeChanged,
this, [this, act]() {
act->setEnabled(inModeCanInsert() && getBuffer() && !getBuffer()->isReadOnly());
});
auto toolBtn = dynamic_cast<QToolButton *>(p_toolBar->widgetForAction(act));
Q_ASSERT(toolBtn);
connect(toolBtn->menu(), &QMenu::triggered,
this, [this](QAction *p_act) {
OverrideState state = static_cast<OverrideState>(p_act->data().toInt());
handleSectionNumberOverride(state);
});
break;
}
}
return act;
@ -586,6 +603,12 @@ void ViewWindow::handleTypeAction(TypeAction p_action)
Q_ASSERT(false);
}
void ViewWindow::handleSectionNumberOverride(OverrideState p_state)
{
Q_UNUSED(p_state);
Q_ASSERT(false);
}
ViewWindow::TypeAction ViewWindow::toolBarActionToTypeAction(ViewWindowToolBarHelper::Action p_action)
{
Q_ASSERT(p_action >= ViewWindowToolBarHelper::Action::TypeBold

View File

@ -137,6 +137,8 @@ namespace vnotex
// Handle all kinds of type action.
virtual void handleTypeAction(TypeAction p_action);
virtual void handleSectionNumberOverride(OverrideState p_state);
virtual void handleFindTextChanged(const QString &p_text, FindOptions p_options);
virtual void handleFindNext(const QString &p_text, FindOptions p_options);

View File

@ -8,6 +8,7 @@
#include <QToolButton>
#include <QMenu>
#include <QDebug>
#include <QActionGroup>
#include "toolbarhelper.h"
#include <utils/iconutils.h>
@ -20,6 +21,7 @@
#include "propertydefs.h"
#include "outlinepopup.h"
#include "viewwindow.h"
#include <core/global.h>
using namespace vnotex;
@ -306,6 +308,39 @@ QAction *ViewWindowToolBarHelper::addAction(QToolBar *p_tb, Action p_action)
break;
}
case Action::SectionNumber:
{
act = p_tb->addAction(ToolBarHelper::generateIcon("section_number_editor.svg"),
ViewWindow::tr("Section Number"));
auto toolBtn = dynamic_cast<QToolButton *>(p_tb->widgetForAction(act));
Q_ASSERT(toolBtn);
toolBtn->setPopupMode(QToolButton::InstantPopup);
toolBtn->setProperty(PropertyDefs::s_toolButtonWithoutMenuIndicator, true);
auto menu = WidgetsFactory::createMenu(p_tb);
auto actGroup = new QActionGroup(menu);
auto act1 = actGroup->addAction(ViewWindow::tr("Follow Configuration"));
act1->setCheckable(true);
act1->setChecked(true);
act1->setData(OverrideState::NoOverride);
menu->addAction(act1);
act1 = actGroup->addAction(ViewWindow::tr("Enabled"));
act1->setCheckable(true);
act1->setData(OverrideState::ForceEnable);
menu->addAction(act1);
act1 = actGroup->addAction(ViewWindow::tr("Disabled"));
act1->setCheckable(true);
act1->setData(OverrideState::ForceDisable);
menu->addAction(act1);
toolBtn->setMenu(menu);
break;
}
default:
Q_ASSERT(false);
break;

View File

@ -39,7 +39,8 @@ namespace vnotex
Attachment,
Outline,
FindAndReplace
FindAndReplace,
SectionNumber
};
static QAction *addAction(QToolBar *p_tb, Action p_action);

View File

@ -20,6 +20,7 @@ SOURCES += \
$$PWD/dialogs/settings/settingspage.cpp \
$$PWD/dialogs/settings/settingsdialog.cpp \
$$PWD/dialogs/settings/texteditorpage.cpp \
$$PWD/dialogs/settings/themepage.cpp \
$$PWD/dragdropareaindicator.cpp \
$$PWD/editors/editormarkdownvieweradapter.cpp \
$$PWD/editors/markdowneditor.cpp \
@ -100,6 +101,7 @@ HEADERS += \
$$PWD/dialogs/settings/settingspage.h \
$$PWD/dialogs/settings/settingsdialog.h \
$$PWD/dialogs/settings/texteditorpage.h \
$$PWD/dialogs/settings/themepage.h \
$$PWD/dragdropareaindicator.h \
$$PWD/editors/editormarkdownvieweradapter.h \
$$PWD/editors/markdowneditor.h \