support outline in edit mode

Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
Le Tan 2016-11-10 21:59:37 +08:00
parent 8a214831e3
commit 1a77056f4a
8 changed files with 149 additions and 6 deletions

View File

@ -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();
} }

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}; };

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;