mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
support outline in edit mode
Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
parent
8a214831e3
commit
1a77056f4a
@ -77,7 +77,7 @@ void HGMarkdownHighlighter::highlightBlock(const QString &text)
|
|||||||
setFormat(unit.start, unit.length, highlightingStyles[unit.styleIndex].format);
|
setFormat(unit.start, unit.length, highlightingStyles[unit.styleIndex].format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setCurrentBlockState(0);
|
setCurrentBlockState(HighlightBlockState::BlockNormal);
|
||||||
highlightCodeBlock(text);
|
highlightCodeBlock(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ void HGMarkdownHighlighter::highlightCodeBlock(const QString &text)
|
|||||||
{
|
{
|
||||||
int nextIndex = 0;
|
int nextIndex = 0;
|
||||||
int startIndex = 0;
|
int startIndex = 0;
|
||||||
if (previousBlockState() != 1) {
|
if (previousBlockState() != HighlightBlockState::BlockCodeBlock) {
|
||||||
startIndex = codeBlockStartExp.indexIn(text);
|
startIndex = codeBlockStartExp.indexIn(text);
|
||||||
if (startIndex >= 0) {
|
if (startIndex >= 0) {
|
||||||
nextIndex = startIndex + codeBlockStartExp.matchedLength();
|
nextIndex = startIndex + codeBlockStartExp.matchedLength();
|
||||||
@ -159,7 +159,7 @@ void HGMarkdownHighlighter::highlightCodeBlock(const QString &text)
|
|||||||
int endIndex = codeBlockEndExp.indexIn(text, nextIndex);
|
int endIndex = codeBlockEndExp.indexIn(text, nextIndex);
|
||||||
int codeBlockLength;
|
int codeBlockLength;
|
||||||
if (endIndex == -1) {
|
if (endIndex == -1) {
|
||||||
setCurrentBlockState(1);
|
setCurrentBlockState(HighlightBlockState::BlockCodeBlock);
|
||||||
codeBlockLength = text.length() - startIndex;
|
codeBlockLength = text.length() - startIndex;
|
||||||
} else {
|
} else {
|
||||||
codeBlockLength = endIndex - startIndex + codeBlockEndExp.matchedLength();
|
codeBlockLength = endIndex - startIndex + codeBlockEndExp.matchedLength();
|
||||||
@ -230,4 +230,5 @@ void HGMarkdownHighlighter::timerTimeout()
|
|||||||
{
|
{
|
||||||
parse();
|
parse();
|
||||||
rehighlight();
|
rehighlight();
|
||||||
|
emit highlightCompleted();
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,12 @@ struct HighlightingStyle
|
|||||||
QTextCharFormat format;
|
QTextCharFormat format;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum HighlightBlockState
|
||||||
|
{
|
||||||
|
BlockNormal = 0,
|
||||||
|
BlockCodeBlock = 1,
|
||||||
|
};
|
||||||
|
|
||||||
// One continuous region for a certain markdown highlight style
|
// One continuous region for a certain markdown highlight style
|
||||||
// within a QTextBlock.
|
// within a QTextBlock.
|
||||||
// Pay attention to the change of HighlightingStyles[]
|
// Pay attention to the change of HighlightingStyles[]
|
||||||
@ -50,6 +56,9 @@ public:
|
|||||||
~HGMarkdownHighlighter();
|
~HGMarkdownHighlighter();
|
||||||
void setStyles(const QVector<HighlightingStyle> &styles);
|
void setStyles(const QVector<HighlightingStyle> &styles);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void highlightCompleted();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void highlightBlock(const QString &text) Q_DECL_OVERRIDE;
|
void highlightBlock(const QString &text) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
#include <QtWidgets>
|
#include <QtWidgets>
|
||||||
|
#include <QVector>
|
||||||
#include "vedit.h"
|
#include "vedit.h"
|
||||||
#include "vnote.h"
|
#include "vnote.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "hgmarkdownhighlighter.h"
|
#include "hgmarkdownhighlighter.h"
|
||||||
#include "vmdeditoperations.h"
|
#include "vmdeditoperations.h"
|
||||||
|
#include "vtoc.h"
|
||||||
|
|
||||||
extern VConfigManager vconfig;
|
extern VConfigManager vconfig;
|
||||||
|
|
||||||
@ -14,6 +16,8 @@ VEdit::VEdit(VNoteFile *noteFile, QWidget *parent)
|
|||||||
setAcceptRichText(false);
|
setAcceptRichText(false);
|
||||||
mdHighlighter = new HGMarkdownHighlighter(vconfig.getMdHighlightingStyles(),
|
mdHighlighter = new HGMarkdownHighlighter(vconfig.getMdHighlightingStyles(),
|
||||||
500, document());
|
500, document());
|
||||||
|
connect(mdHighlighter, &HGMarkdownHighlighter::highlightCompleted,
|
||||||
|
this, &VEdit::generateEditOutline);
|
||||||
editOps = new VMdEditOperations(this, noteFile);
|
editOps = new VMdEditOperations(this, noteFile);
|
||||||
} else {
|
} else {
|
||||||
setAutoFormatting(QTextEdit::AutoBulletList);
|
setAutoFormatting(QTextEdit::AutoBulletList);
|
||||||
@ -22,6 +26,8 @@ VEdit::VEdit(VNoteFile *noteFile, QWidget *parent)
|
|||||||
|
|
||||||
updateTabSettings();
|
updateTabSettings();
|
||||||
updateFontAndPalette();
|
updateFontAndPalette();
|
||||||
|
connect(this, &VEdit::cursorPositionChanged,
|
||||||
|
this, &VEdit::updateCurHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
VEdit::~VEdit()
|
VEdit::~VEdit()
|
||||||
@ -164,3 +170,50 @@ void VEdit::insertFromMimeData(const QMimeData *source)
|
|||||||
}
|
}
|
||||||
QTextEdit::insertFromMimeData(source);
|
QTextEdit::insertFromMimeData(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VEdit::generateEditOutline()
|
||||||
|
{
|
||||||
|
QTextDocument *doc = document();
|
||||||
|
headers.clear();
|
||||||
|
// Assume that each block contains only one line
|
||||||
|
// Only support # syntax for now
|
||||||
|
QRegExp headerReg("(#{1,6})\\s*(\\S.*)"); // Need to trim the spaces
|
||||||
|
for (QTextBlock block = doc->begin(); block != doc->end(); block = block.next()) {
|
||||||
|
Q_ASSERT(block.lineCount() == 1);
|
||||||
|
if (headerReg.exactMatch(block.text())) {
|
||||||
|
VHeader header(headerReg.cap(1).length(),
|
||||||
|
headerReg.cap(2).trimmed(), "", block.firstLineNumber());
|
||||||
|
headers.append(header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit headersChanged(headers);
|
||||||
|
updateCurHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEdit::scrollToLine(int lineNumber)
|
||||||
|
{
|
||||||
|
Q_ASSERT(lineNumber >= 0);
|
||||||
|
|
||||||
|
// Move the cursor to the end first
|
||||||
|
moveCursor(QTextCursor::End);
|
||||||
|
QTextCursor cursor(document()->findBlockByLineNumber(lineNumber));
|
||||||
|
cursor.movePosition(QTextCursor::EndOfLine);
|
||||||
|
setTextCursor(cursor);
|
||||||
|
|
||||||
|
setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEdit::updateCurHeader()
|
||||||
|
{
|
||||||
|
int curHeader = 0;
|
||||||
|
QTextCursor cursor(this->textCursor());
|
||||||
|
int curLine = cursor.block().firstLineNumber();
|
||||||
|
for (int i = headers.size() - 1; i >= 0; --i) {
|
||||||
|
if (headers[i].lineNumber <= curLine) {
|
||||||
|
curHeader = headers[i].lineNumber;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit curHeaderChanged(curHeader);
|
||||||
|
}
|
||||||
|
11
src/vedit.h
11
src/vedit.h
@ -5,6 +5,7 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include "vconstants.h"
|
#include "vconstants.h"
|
||||||
#include "vnotefile.h"
|
#include "vnotefile.h"
|
||||||
|
#include "vtoc.h"
|
||||||
|
|
||||||
class HGMarkdownHighlighter;
|
class HGMarkdownHighlighter;
|
||||||
class VEditOperations;
|
class VEditOperations;
|
||||||
@ -24,12 +25,21 @@ public:
|
|||||||
inline bool isModified() const;
|
inline bool isModified() const;
|
||||||
|
|
||||||
void reloadFile();
|
void reloadFile();
|
||||||
|
void scrollToLine(int lineNumber);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void headersChanged(const QVector<VHeader> &headers);
|
||||||
|
void curHeaderChanged(int lineNumber);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
|
void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
|
||||||
bool canInsertFromMimeData(const QMimeData *source) const Q_DECL_OVERRIDE;
|
bool canInsertFromMimeData(const QMimeData *source) const Q_DECL_OVERRIDE;
|
||||||
void insertFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
|
void insertFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void generateEditOutline();
|
||||||
|
void updateCurHeader();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateTabSettings();
|
void updateTabSettings();
|
||||||
void updateFontAndPalette();
|
void updateFontAndPalette();
|
||||||
@ -39,6 +49,7 @@ private:
|
|||||||
VNoteFile *noteFile;
|
VNoteFile *noteFile;
|
||||||
HGMarkdownHighlighter *mdHighlighter;
|
HGMarkdownHighlighter *mdHighlighter;
|
||||||
VEditOperations *editOps;
|
VEditOperations *editOps;
|
||||||
|
QVector<VHeader> headers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,6 +49,10 @@ VEditTab::~VEditTab()
|
|||||||
void VEditTab::setupUI()
|
void VEditTab::setupUI()
|
||||||
{
|
{
|
||||||
textEditor = new VEdit(noteFile);
|
textEditor = new VEdit(noteFile);
|
||||||
|
connect(textEditor, &VEdit::headersChanged,
|
||||||
|
this, &VEditTab::updateTocFromHeaders);
|
||||||
|
connect(textEditor, SIGNAL(curHeaderChanged(int)),
|
||||||
|
this, SLOT(updateCurHeader(int)));
|
||||||
addWidget(textEditor);
|
addWidget(textEditor);
|
||||||
|
|
||||||
switch (noteFile->docType) {
|
switch (noteFile->docType) {
|
||||||
@ -220,8 +224,8 @@ void VEditTab::setupMarkdownPreview()
|
|||||||
channel->registerObject(QStringLiteral("content"), &document);
|
channel->registerObject(QStringLiteral("content"), &document);
|
||||||
connect(&document, &VDocument::tocChanged,
|
connect(&document, &VDocument::tocChanged,
|
||||||
this, &VEditTab::updateTocFromHtml);
|
this, &VEditTab::updateTocFromHtml);
|
||||||
connect(&document, &VDocument::headerChanged,
|
connect(&document, SIGNAL(headerChanged(const QString&)),
|
||||||
this, &VEditTab::updateCurHeader);
|
this, SLOT(updateCurHeader(const QString &)));
|
||||||
page->setWebChannel(channel);
|
page->setWebChannel(channel);
|
||||||
|
|
||||||
if (mdConverterType == MarkdownConverterType::Marked) {
|
if (mdConverterType == MarkdownConverterType::Marked) {
|
||||||
@ -273,6 +277,16 @@ void VEditTab::updateTocFromHtml(const QString &tocHtml)
|
|||||||
emit outlineChanged(tableOfContent);
|
emit outlineChanged(tableOfContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VEditTab::updateTocFromHeaders(const QVector<VHeader> &headers)
|
||||||
|
{
|
||||||
|
tableOfContent.type = VHeaderType::LineNumber;
|
||||||
|
tableOfContent.headers = headers;
|
||||||
|
tableOfContent.filePath = QDir::cleanPath(QDir(noteFile->basePath).filePath(noteFile->fileName));
|
||||||
|
tableOfContent.valid = true;
|
||||||
|
|
||||||
|
emit outlineChanged(tableOfContent);
|
||||||
|
}
|
||||||
|
|
||||||
void VEditTab::parseTocUl(QXmlStreamReader &xml, QVector<VHeader> &headers, int level)
|
void VEditTab::parseTocUl(QXmlStreamReader &xml, QVector<VHeader> &headers, int level)
|
||||||
{
|
{
|
||||||
Q_ASSERT(xml.isStartElement() && xml.name() == "ul");
|
Q_ASSERT(xml.isStartElement() && xml.name() == "ul");
|
||||||
@ -349,7 +363,7 @@ void VEditTab::scrollToAnchor(const VAnchor &anchor)
|
|||||||
{
|
{
|
||||||
if (isEditMode) {
|
if (isEditMode) {
|
||||||
if (anchor.lineNumber > -1) {
|
if (anchor.lineNumber > -1) {
|
||||||
|
textEditor->scrollToLine(anchor.lineNumber);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!anchor.anchor.isEmpty()) {
|
if (!anchor.anchor.isEmpty()) {
|
||||||
@ -369,3 +383,15 @@ void VEditTab::updateCurHeader(const QString &anchor)
|
|||||||
emit curHeaderChanged(curHeader);
|
emit curHeaderChanged(curHeader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VEditTab::updateCurHeader(int lineNumber)
|
||||||
|
{
|
||||||
|
if (curHeader.lineNumber == lineNumber) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
curHeader = VAnchor(QDir::cleanPath(QDir(noteFile->basePath).filePath(noteFile->fileName)),
|
||||||
|
"", lineNumber);
|
||||||
|
if (lineNumber > -1) {
|
||||||
|
emit curHeaderChanged(curHeader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -46,6 +46,8 @@ private slots:
|
|||||||
void handleFocusChanged(QWidget *old, QWidget *now);
|
void handleFocusChanged(QWidget *old, QWidget *now);
|
||||||
void updateTocFromHtml(const QString &tocHtml);
|
void updateTocFromHtml(const QString &tocHtml);
|
||||||
void updateCurHeader(const QString &anchor);
|
void updateCurHeader(const QString &anchor);
|
||||||
|
void updateCurHeader(int lineNumber);
|
||||||
|
void updateTocFromHeaders(const QVector<VHeader> &headers);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isMarkdown(const QString &name);
|
bool isMarkdown(const QString &name);
|
||||||
|
@ -21,6 +21,7 @@ void VOutline::updateOutline(const VToc &toc)
|
|||||||
outline = toc;
|
outline = toc;
|
||||||
updateTreeFromOutline(outline);
|
updateTreeFromOutline(outline);
|
||||||
expandTree();
|
expandTree();
|
||||||
|
updateCurHeader(curHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VOutline::updateTreeFromOutline(const VToc &toc)
|
void VOutline::updateTreeFromOutline(const VToc &toc)
|
||||||
@ -90,6 +91,7 @@ void VOutline::updateCurHeader(const VAnchor &anchor)
|
|||||||
selectAnchor(anchor.anchor);
|
selectAnchor(anchor.anchor);
|
||||||
} else {
|
} else {
|
||||||
// Select by lineNumber
|
// Select by lineNumber
|
||||||
|
selectLineNumber(anchor.lineNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,3 +131,40 @@ bool VOutline::selectAnchorOne(QTreeWidgetItem *item, const QString &anchor)
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VOutline::selectLineNumber(int lineNumber)
|
||||||
|
{
|
||||||
|
QList<QTreeWidgetItem *> selected = selectedItems();
|
||||||
|
foreach (QTreeWidgetItem *item, selected) {
|
||||||
|
setItemSelected(item, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int nrTop = topLevelItemCount();
|
||||||
|
for (int i = 0; i < nrTop; ++i) {
|
||||||
|
if (selectLineNumberOne(topLevelItem(i), lineNumber)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VOutline::selectLineNumberOne(QTreeWidgetItem *item, int lineNumber)
|
||||||
|
{
|
||||||
|
if (!item) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject();
|
||||||
|
int itemLineNum = itemJson["line_number"].toInt();
|
||||||
|
if (itemLineNum == lineNumber) {
|
||||||
|
// Select this item
|
||||||
|
setItemSelected(item, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nrChild = item->childCount();
|
||||||
|
for (int i = 0; i < nrChild; ++i) {
|
||||||
|
if (selectLineNumberOne(item->child(i), lineNumber)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -27,6 +27,8 @@ private:
|
|||||||
void expandTree();
|
void expandTree();
|
||||||
void selectAnchor(const QString &anchor);
|
void selectAnchor(const QString &anchor);
|
||||||
bool selectAnchorOne(QTreeWidgetItem *item, const QString &anchor);
|
bool selectAnchorOne(QTreeWidgetItem *item, const QString &anchor);
|
||||||
|
void selectLineNumber(int lineNumber);
|
||||||
|
bool selectLineNumberOne(QTreeWidgetItem *item, int lineNumber);
|
||||||
|
|
||||||
VToc outline;
|
VToc outline;
|
||||||
VAnchor curHeader;
|
VAnchor curHeader;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user