support synchronization between preview page and outline

Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
Le Tan 2016-11-08 23:21:22 +08:00
parent fe3d16ba3b
commit 849fdf05bd
15 changed files with 165 additions and 3 deletions

View File

@ -26,4 +26,27 @@
content.requestScrollToAnchor.connect(scrollToAnchor); content.requestScrollToAnchor.connect(scrollToAnchor);
} }
); );
window.onscroll = function() {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset;
var eles = document.querySelectorAll("h1, h2, h3, h4, h5, h6");
if (eles.length == 0) {
return;
}
var curIdx = 0;
var biaScrollTop = scrollTop + 20;
for (var i = 0; i < eles.length; ++i) {
if (biaScrollTop >= eles[i].offsetTop) {
curIdx = i;
} else {
break;
}
}
var curHeader = eles[curIdx].getAttribute("id");
if (curHeader != null) {
content.setHeader(curHeader);
}
}
</script> </script>

View File

@ -157,6 +157,29 @@
content.requestScrollToAnchor.connect(scrollToAnchor); content.requestScrollToAnchor.connect(scrollToAnchor);
} }
); );
window.onscroll = function() {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset;
var eles = document.querySelectorAll("h1, h2, h3, h4, h5, h6");
if (eles.length == 0) {
return;
}
var curIdx = 0;
var biaScrollTop = scrollTop + 20;
for (var i = 0; i < eles.length; ++i) {
if (biaScrollTop >= eles[i].offsetTop) {
curIdx = i;
} else {
break;
}
}
var curHeader = eles[curIdx].getAttribute("id");
if (curHeader != null) {
content.setHeader(curHeader);
}
}
</script> </script>
</body> </body>
</html> </html>

View File

@ -43,3 +43,12 @@ void VDocument::scrollToAnchor(const QString &anchor)
{ {
emit requestScrollToAnchor(anchor); emit requestScrollToAnchor(anchor);
} }
void VDocument::setHeader(const QString &anchor)
{
if (anchor == m_header) {
return;
}
m_header = anchor;
emit headerChanged(m_header);
}

View File

@ -21,15 +21,18 @@ public:
public slots: public slots:
// Will be called in the HTML side // Will be called in the HTML side
void setToc(const QString &toc); void setToc(const QString &toc);
void setHeader(const QString &anchor);
signals: signals:
void textChanged(const QString &text); void textChanged(const QString &text);
void tocChanged(const QString &toc); void tocChanged(const QString &toc);
void requestScrollToAnchor(const QString &anchor); void requestScrollToAnchor(const QString &anchor);
void headerChanged(const QString &anchor);
private: private:
QString m_text; QString m_text;
QString m_toc; QString m_toc;
QString m_header;
}; };
#endif // VDOCUMENT_H #endif // VDOCUMENT_H

View File

@ -39,6 +39,8 @@ void VEditArea::insertSplitWindow(int idx)
this, &VEditArea::handleWindowFocused); this, &VEditArea::handleWindowFocused);
connect(win, &VEditWindow::outlineChanged, connect(win, &VEditWindow::outlineChanged,
this, &VEditArea::handleOutlineChanged); this, &VEditArea::handleOutlineChanged);
connect(win, &VEditWindow::curHeaderChanged,
this, &VEditArea::handleCurHeaderChanged);
int nrWin = splitter->count(); int nrWin = splitter->count();
if (nrWin == 1) { if (nrWin == 1) {
@ -290,6 +292,14 @@ void VEditArea::handleOutlineChanged(const VToc &toc)
} }
} }
void VEditArea::handleCurHeaderChanged(const VAnchor &anchor)
{
QObject *winObject = sender();
if (splitter->widget(curWindowIndex) == winObject) {
emit curHeaderChanged(anchor);
}
}
void VEditArea::handleOutlineItemActivated(const VAnchor &anchor) void VEditArea::handleOutlineItemActivated(const VAnchor &anchor)
{ {
// Notice current window // Notice current window

View File

@ -26,6 +26,7 @@ signals:
void curTabStatusChanged(const QString &notebook, const QString &relativePath, void curTabStatusChanged(const QString &notebook, const QString &relativePath,
bool editMode, bool modifiable); bool editMode, bool modifiable);
void outlineChanged(const VToc &toc); void outlineChanged(const VToc &toc);
void curHeaderChanged(const VAnchor &anchor);
protected: protected:
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
@ -47,6 +48,7 @@ private slots:
void handleRemoveSplitRequest(VEditWindow *curWindow); void handleRemoveSplitRequest(VEditWindow *curWindow);
void handleWindowFocused(); void handleWindowFocused();
void handleOutlineChanged(const VToc &toc); void handleOutlineChanged(const VToc &toc);
void handleCurHeaderChanged(const VAnchor &anchor);
private: private:
void setupUI(); void setupUI();

View File

@ -221,6 +221,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,
this, &VEditTab::updateCurHeader);
page->setWebChannel(channel); page->setWebChannel(channel);
if (mdConverterType == MarkdownConverterType::Marked) { if (mdConverterType == MarkdownConverterType::Marked) {
@ -263,7 +265,6 @@ void VEditTab::updateTocFromHtml(const QString &tocHtml)
return; return;
} }
tableOfContent.curHeaderIndex = 0;
tableOfContent.filePath = QDir::cleanPath(QDir(noteFile->basePath).filePath(noteFile->fileName)); tableOfContent.filePath = QDir::cleanPath(QDir(noteFile->basePath).filePath(noteFile->fileName));
tableOfContent.valid = true; tableOfContent.valid = true;
@ -349,3 +350,15 @@ void VEditTab::scrollToAnchor(const VAnchor &anchor)
} }
} }
} }
void VEditTab::updateCurHeader(const QString &anchor)
{
if (curHeader == anchor) {
return;
}
curHeader = anchor;
if (!anchor.isEmpty()) {
emit curHeaderChanged(VAnchor(QDir::cleanPath(QDir(noteFile->basePath).filePath(noteFile->fileName)),
"#" + anchor, -1));
}
}

View File

@ -39,10 +39,12 @@ public:
signals: signals:
void getFocused(); void getFocused();
void outlineChanged(const VToc &toc); void outlineChanged(const VToc &toc);
void curHeaderChanged(const VAnchor &anchor);
private slots: 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);
private: private:
bool isMarkdown(const QString &name); bool isMarkdown(const QString &name);
@ -63,6 +65,7 @@ private:
VDocument document; VDocument document;
MarkdownConverterType mdConverterType; MarkdownConverterType mdConverterType;
VToc tableOfContent; VToc tableOfContent;
QString curHeader;
}; };
inline bool VEditTab::getIsEditMode() const inline bool VEditTab::getIsEditMode() const

View File

@ -167,6 +167,8 @@ int VEditWindow::openFileInTab(const QString &notebook, const QString &relativeP
this, &VEditWindow::getFocused); this, &VEditWindow::getFocused);
connect(editor, &VEditTab::outlineChanged, connect(editor, &VEditTab::outlineChanged,
this, &VEditWindow::handleOutlineChanged); this, &VEditWindow::handleOutlineChanged);
connect(editor, &VEditTab::curHeaderChanged,
this, &VEditWindow::handleCurHeaderChanged);
QJsonObject tabJson; QJsonObject tabJson;
tabJson["notebook"] = notebook; tabJson["notebook"] = notebook;
@ -384,6 +386,20 @@ void VEditWindow::handleOutlineChanged(const VToc &toc)
} }
} }
void VEditWindow::handleCurHeaderChanged(const VAnchor &anchor)
{
// Only propagate it if it is current tab
int idx = currentIndex();
QJsonObject tabJson = tabBar()->tabData(idx).toJsonObject();
Q_ASSERT(!tabJson.isEmpty());
QString path = vnote->getNotebookPath(tabJson["notebook"].toString());
path = QDir::cleanPath(QDir(path).filePath(tabJson["relative_path"].toString()));
if (anchor.filePath == path) {
emit curHeaderChanged(anchor);
}
}
void VEditWindow::scrollCurTab(const VAnchor &anchor) void VEditWindow::scrollCurTab(const VAnchor &anchor)
{ {
int idx = currentIndex(); int idx = currentIndex();

View File

@ -50,6 +50,7 @@ signals:
// This widget or its children get the focus // This widget or its children get the focus
void getFocused(); void getFocused();
void outlineChanged(const VToc &toc); void outlineChanged(const VToc &toc);
void curHeaderChanged(const VAnchor &anchor);
private slots: private slots:
bool handleTabCloseRequest(int index); bool handleTabCloseRequest(int index);
@ -59,6 +60,7 @@ private slots:
void contextMenuRequested(QPoint pos); void contextMenuRequested(QPoint pos);
void tabListJump(QAction *action); void tabListJump(QAction *action);
void handleOutlineChanged(const VToc &toc); void handleOutlineChanged(const VToc &toc);
void handleCurHeaderChanged(const VAnchor &anchor);
private: private:
void setupCornerWidget(); void setupCornerWidget();

View File

@ -347,6 +347,8 @@ void VMainWindow::initDockWindows()
outline, &VOutline::updateOutline); outline, &VOutline::updateOutline);
connect(outline, &VOutline::outlineItemActivated, connect(outline, &VOutline::outlineItemActivated,
editArea, &VEditArea::handleOutlineItemActivated); editArea, &VEditArea::handleOutlineItemActivated);
connect(editArea, &VEditArea::curHeaderChanged,
outline, &VOutline::updateCurHeader);
toolBox->addItem(outline, QIcon(":/resources/icons/outline.svg"), tr("Outline")); toolBox->addItem(outline, QIcon(":/resources/icons/outline.svg"), tr("Outline"));
dock->setWidget(toolBox); dock->setWidget(toolBox);
addDockWidget(Qt::RightDockWidgetArea, dock); addDockWidget(Qt::RightDockWidgetArea, dock);

View File

@ -10,6 +10,7 @@ VOutline::VOutline(QWidget *parent)
{ {
setColumnCount(1); setColumnCount(1);
setHeaderHidden(true); setHeaderHidden(true);
setSelectionMode(QAbstractItemView::SingleSelection);
connect(this, &VOutline::itemClicked, connect(this, &VOutline::itemClicked,
this, &VOutline::handleItemClicked); this, &VOutline::handleItemClicked);
@ -64,7 +65,11 @@ void VOutline::updateTreeByLevel(const QVector<VHeader> &headers, int &index,
void VOutline::expandTree() void VOutline::expandTree()
{ {
if (topLevelItemCount() == 0) {
return;
}
expandAll(); expandAll();
setItemSelected(topLevelItem(0), true);
} }
@ -77,3 +82,50 @@ void VOutline::handleItemClicked(QTreeWidgetItem *item, int column)
qDebug() << "click anchor" << anchor << lineNumber; qDebug() << "click anchor" << anchor << lineNumber;
emit outlineItemActivated(VAnchor(outline.filePath, anchor, lineNumber)); emit outlineItemActivated(VAnchor(outline.filePath, anchor, lineNumber));
} }
void VOutline::updateCurHeader(const VAnchor &anchor)
{
curHeader = anchor;
if (outline.type == VHeaderType::Anchor) {
selectAnchor(anchor.anchor);
} else {
// Select by lineNumber
}
}
void VOutline::selectAnchor(const QString &anchor)
{
QList<QTreeWidgetItem *> selected = selectedItems();
foreach (QTreeWidgetItem *item, selected) {
setItemSelected(item, false);
}
int nrTop = topLevelItemCount();
for (int i = 0; i < nrTop; ++i) {
if (selectAnchorOne(topLevelItem(i), anchor)) {
return;
}
}
}
bool VOutline::selectAnchorOne(QTreeWidgetItem *item, const QString &anchor)
{
if (!item) {
return false;
}
QJsonObject itemJson = item->data(0, Qt::UserRole).toJsonObject();
QString itemAnchor = itemJson["anchor"].toString();
if (itemAnchor == anchor) {
// Select this item
setItemSelected(item, true);
return true;
}
int nrChild = item->childCount();
for (int i = 0; i < nrChild; ++i) {
if (selectAnchorOne(item->child(i), anchor)) {
return true;
}
}
return false;
}

View File

@ -15,6 +15,7 @@ signals:
public slots: public slots:
void updateOutline(const VToc &toc); void updateOutline(const VToc &toc);
void updateCurHeader(const VAnchor &anchor);
private slots: private slots:
void handleItemClicked(QTreeWidgetItem *item, int column); void handleItemClicked(QTreeWidgetItem *item, int column);
@ -24,8 +25,11 @@ private:
void updateTreeByLevel(const QVector<VHeader> &headers, int &index, QTreeWidgetItem *parent, void updateTreeByLevel(const QVector<VHeader> &headers, int &index, QTreeWidgetItem *parent,
QTreeWidgetItem *last, int level); QTreeWidgetItem *last, int level);
void expandTree(); void expandTree();
void selectAnchor(const QString &anchor);
bool selectAnchorOne(QTreeWidgetItem *item, const QString &anchor);
VToc outline; VToc outline;
VAnchor curHeader;
}; };
#endif // VOUTLINE_H #endif // VOUTLINE_H

View File

@ -1,6 +1,6 @@
#include "vtoc.h" #include "vtoc.h"
VToc::VToc() VToc::VToc()
: curHeaderIndex(0), type(VHeaderType::Anchor), valid(false) : type(VHeaderType::Anchor), valid(false)
{ {
} }

View File

@ -23,6 +23,7 @@ struct VHeader
struct VAnchor struct VAnchor
{ {
VAnchor() : lineNumber(-1) {}
VAnchor(const QString filePath, const QString &anchor, int lineNumber) VAnchor(const QString filePath, const QString &anchor, int lineNumber)
: filePath(filePath), anchor(anchor), lineNumber(lineNumber) {} : filePath(filePath), anchor(anchor), lineNumber(lineNumber) {}
QString filePath; QString filePath;
@ -36,7 +37,6 @@ public:
VToc(); VToc();
QVector<VHeader> headers; QVector<VHeader> headers;
int curHeaderIndex;
int type; int type;
QString filePath; QString filePath;
bool valid; bool valid;