mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 05:49:53 +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);
|
||||
}
|
||||
}
|
||||
setCurrentBlockState(0);
|
||||
setCurrentBlockState(HighlightBlockState::BlockNormal);
|
||||
highlightCodeBlock(text);
|
||||
}
|
||||
|
||||
@ -146,7 +146,7 @@ void HGMarkdownHighlighter::highlightCodeBlock(const QString &text)
|
||||
{
|
||||
int nextIndex = 0;
|
||||
int startIndex = 0;
|
||||
if (previousBlockState() != 1) {
|
||||
if (previousBlockState() != HighlightBlockState::BlockCodeBlock) {
|
||||
startIndex = codeBlockStartExp.indexIn(text);
|
||||
if (startIndex >= 0) {
|
||||
nextIndex = startIndex + codeBlockStartExp.matchedLength();
|
||||
@ -159,7 +159,7 @@ void HGMarkdownHighlighter::highlightCodeBlock(const QString &text)
|
||||
int endIndex = codeBlockEndExp.indexIn(text, nextIndex);
|
||||
int codeBlockLength;
|
||||
if (endIndex == -1) {
|
||||
setCurrentBlockState(1);
|
||||
setCurrentBlockState(HighlightBlockState::BlockCodeBlock);
|
||||
codeBlockLength = text.length() - startIndex;
|
||||
} else {
|
||||
codeBlockLength = endIndex - startIndex + codeBlockEndExp.matchedLength();
|
||||
@ -230,4 +230,5 @@ void HGMarkdownHighlighter::timerTimeout()
|
||||
{
|
||||
parse();
|
||||
rehighlight();
|
||||
emit highlightCompleted();
|
||||
}
|
||||
|
@ -28,6 +28,12 @@ struct HighlightingStyle
|
||||
QTextCharFormat format;
|
||||
};
|
||||
|
||||
enum HighlightBlockState
|
||||
{
|
||||
BlockNormal = 0,
|
||||
BlockCodeBlock = 1,
|
||||
};
|
||||
|
||||
// One continuous region for a certain markdown highlight style
|
||||
// within a QTextBlock.
|
||||
// Pay attention to the change of HighlightingStyles[]
|
||||
@ -50,6 +56,9 @@ public:
|
||||
~HGMarkdownHighlighter();
|
||||
void setStyles(const QVector<HighlightingStyle> &styles);
|
||||
|
||||
signals:
|
||||
void highlightCompleted();
|
||||
|
||||
protected:
|
||||
void highlightBlock(const QString &text) Q_DECL_OVERRIDE;
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
#include <QtWidgets>
|
||||
#include <QVector>
|
||||
#include "vedit.h"
|
||||
#include "vnote.h"
|
||||
#include "vconfigmanager.h"
|
||||
#include "hgmarkdownhighlighter.h"
|
||||
#include "vmdeditoperations.h"
|
||||
#include "vtoc.h"
|
||||
|
||||
extern VConfigManager vconfig;
|
||||
|
||||
@ -14,6 +16,8 @@ VEdit::VEdit(VNoteFile *noteFile, QWidget *parent)
|
||||
setAcceptRichText(false);
|
||||
mdHighlighter = new HGMarkdownHighlighter(vconfig.getMdHighlightingStyles(),
|
||||
500, document());
|
||||
connect(mdHighlighter, &HGMarkdownHighlighter::highlightCompleted,
|
||||
this, &VEdit::generateEditOutline);
|
||||
editOps = new VMdEditOperations(this, noteFile);
|
||||
} else {
|
||||
setAutoFormatting(QTextEdit::AutoBulletList);
|
||||
@ -22,6 +26,8 @@ VEdit::VEdit(VNoteFile *noteFile, QWidget *parent)
|
||||
|
||||
updateTabSettings();
|
||||
updateFontAndPalette();
|
||||
connect(this, &VEdit::cursorPositionChanged,
|
||||
this, &VEdit::updateCurHeader);
|
||||
}
|
||||
|
||||
VEdit::~VEdit()
|
||||
@ -164,3 +170,50 @@ void VEdit::insertFromMimeData(const QMimeData *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 "vconstants.h"
|
||||
#include "vnotefile.h"
|
||||
#include "vtoc.h"
|
||||
|
||||
class HGMarkdownHighlighter;
|
||||
class VEditOperations;
|
||||
@ -24,12 +25,21 @@ public:
|
||||
inline bool isModified() const;
|
||||
|
||||
void reloadFile();
|
||||
void scrollToLine(int lineNumber);
|
||||
|
||||
signals:
|
||||
void headersChanged(const QVector<VHeader> &headers);
|
||||
void curHeaderChanged(int lineNumber);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
|
||||
bool canInsertFromMimeData(const QMimeData *source) const Q_DECL_OVERRIDE;
|
||||
void insertFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void generateEditOutline();
|
||||
void updateCurHeader();
|
||||
|
||||
private:
|
||||
void updateTabSettings();
|
||||
void updateFontAndPalette();
|
||||
@ -39,6 +49,7 @@ private:
|
||||
VNoteFile *noteFile;
|
||||
HGMarkdownHighlighter *mdHighlighter;
|
||||
VEditOperations *editOps;
|
||||
QVector<VHeader> headers;
|
||||
};
|
||||
|
||||
|
||||
|
@ -49,6 +49,10 @@ VEditTab::~VEditTab()
|
||||
void VEditTab::setupUI()
|
||||
{
|
||||
textEditor = new VEdit(noteFile);
|
||||
connect(textEditor, &VEdit::headersChanged,
|
||||
this, &VEditTab::updateTocFromHeaders);
|
||||
connect(textEditor, SIGNAL(curHeaderChanged(int)),
|
||||
this, SLOT(updateCurHeader(int)));
|
||||
addWidget(textEditor);
|
||||
|
||||
switch (noteFile->docType) {
|
||||
@ -220,8 +224,8 @@ void VEditTab::setupMarkdownPreview()
|
||||
channel->registerObject(QStringLiteral("content"), &document);
|
||||
connect(&document, &VDocument::tocChanged,
|
||||
this, &VEditTab::updateTocFromHtml);
|
||||
connect(&document, &VDocument::headerChanged,
|
||||
this, &VEditTab::updateCurHeader);
|
||||
connect(&document, SIGNAL(headerChanged(const QString&)),
|
||||
this, SLOT(updateCurHeader(const QString &)));
|
||||
page->setWebChannel(channel);
|
||||
|
||||
if (mdConverterType == MarkdownConverterType::Marked) {
|
||||
@ -273,6 +277,16 @@ void VEditTab::updateTocFromHtml(const QString &tocHtml)
|
||||
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)
|
||||
{
|
||||
Q_ASSERT(xml.isStartElement() && xml.name() == "ul");
|
||||
@ -349,7 +363,7 @@ void VEditTab::scrollToAnchor(const VAnchor &anchor)
|
||||
{
|
||||
if (isEditMode) {
|
||||
if (anchor.lineNumber > -1) {
|
||||
|
||||
textEditor->scrollToLine(anchor.lineNumber);
|
||||
}
|
||||
} else {
|
||||
if (!anchor.anchor.isEmpty()) {
|
||||
@ -369,3 +383,15 @@ void VEditTab::updateCurHeader(const QString &anchor)
|
||||
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 updateTocFromHtml(const QString &tocHtml);
|
||||
void updateCurHeader(const QString &anchor);
|
||||
void updateCurHeader(int lineNumber);
|
||||
void updateTocFromHeaders(const QVector<VHeader> &headers);
|
||||
|
||||
private:
|
||||
bool isMarkdown(const QString &name);
|
||||
|
@ -21,6 +21,7 @@ void VOutline::updateOutline(const VToc &toc)
|
||||
outline = toc;
|
||||
updateTreeFromOutline(outline);
|
||||
expandTree();
|
||||
updateCurHeader(curHeader);
|
||||
}
|
||||
|
||||
void VOutline::updateTreeFromOutline(const VToc &toc)
|
||||
@ -90,6 +91,7 @@ void VOutline::updateCurHeader(const VAnchor &anchor)
|
||||
selectAnchor(anchor.anchor);
|
||||
} else {
|
||||
// Select by lineNumber
|
||||
selectLineNumber(anchor.lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,3 +131,40 @@ bool VOutline::selectAnchorOne(QTreeWidgetItem *item, const QString &anchor)
|
||||
}
|
||||
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 selectAnchor(const QString &anchor);
|
||||
bool selectAnchorOne(QTreeWidgetItem *item, const QString &anchor);
|
||||
void selectLineNumber(int lineNumber);
|
||||
bool selectLineNumberOne(QTreeWidgetItem *item, int lineNumber);
|
||||
|
||||
VToc outline;
|
||||
VAnchor curHeader;
|
||||
|
Loading…
x
Reference in New Issue
Block a user