diff --git a/src/resources/vnote.ini b/src/resources/vnote.ini index c0b4c8b0..04f6386c 100644 --- a/src/resources/vnote.ini +++ b/src/resources/vnote.ini @@ -299,6 +299,17 @@ enable_tab_highlight=false ; Download images in parsed HTML text parse_paste_local_image=true +; Enable extra buffer at the bottom of the editor to avoid placing +; cursor at the bottom +enable_extra_buffer=true + +; Auto scroll cursor line when it is at the bottom part of the content +; Need enable_extra_buffer to be enabled +; 0 - disabled +; 1 - end of doc +; 2 - always +auto_scroll_cursor_line=1 + [export] ; Path of the wkhtmltopdf tool wkhtmltopdf=wkhtmltopdf diff --git a/src/utils/veditutils.cpp b/src/utils/veditutils.cpp index 8e6dd703..4b42978d 100644 --- a/src/utils/veditutils.cpp +++ b/src/utils/veditutils.cpp @@ -406,7 +406,8 @@ int VEditUtils::selectedBlockCount(const QTextCursor &p_cursor) void VEditUtils::scrollBlockInPage(QTextEdit *p_edit, int p_blockNum, - int p_dest) + int p_dest, + int p_margin) { QTextDocument *doc = p_edit->document(); QTextCursor cursor = p_edit->textCursor(); @@ -416,9 +417,9 @@ void VEditUtils::scrollBlockInPage(QTextEdit *p_edit, QTextBlock block = doc->findBlockByNumber(p_blockNum); - int pib = cursor.positionInBlock(); if (cursor.block().blockNumber() != p_blockNum) { // Move the cursor to the block. + int pib = cursor.positionInBlock(); if (pib >= block.length()) { pib = block.length() - 1; } @@ -427,15 +428,20 @@ void VEditUtils::scrollBlockInPage(QTextEdit *p_edit, p_edit->setTextCursor(cursor); } - // Scroll to let current cursor locate in proper position. - p_edit->ensureCursorVisible(); QScrollBar *vsbar = p_edit->verticalScrollBar(); - if (!vsbar || !vsbar->isVisible()) { // No vertical scrollbar. No need to scroll. return; } + int sstep = vsbar->singleStep(); + if (p_margin < sstep) { + p_margin = sstep; + } + + // Scroll to let current cursor locate in proper position. + p_edit->ensureCursorVisible(); + QRect rect = p_edit->cursorRect(); int height = p_edit->rect().height(); QScrollBar *sbar = p_edit->horizontalScrollBar(); @@ -443,13 +449,15 @@ void VEditUtils::scrollBlockInPage(QTextEdit *p_edit, height -= sbar->height(); } + bool moved = false; switch (p_dest) { case 0: { // Top. - while (rect.y() > 0 && vsbar->value() < vsbar->maximum()) { - vsbar->setValue(vsbar->value() + vsbar->singleStep()); + while (rect.y() > p_margin && vsbar->value() < vsbar->maximum()) { + vsbar->setValue(vsbar->value() + sstep); rect = p_edit->cursorRect(); + moved = true; } break; @@ -459,15 +467,19 @@ void VEditUtils::scrollBlockInPage(QTextEdit *p_edit, { // Center. height = qMax(height / 2, 1); - if (rect.y() > height) { - while (rect.y() > height && vsbar->value() < vsbar->maximum()) { - vsbar->setValue(vsbar->value() + vsbar->singleStep()); + int upBound = height + p_margin; + int lowBound = height - p_margin; + if (rect.y() > upBound) { + while (rect.y() > upBound && vsbar->value() < vsbar->maximum()) { + vsbar->setValue(vsbar->value() + sstep); rect = p_edit->cursorRect(); + moved = true; } - } else if (rect.y() < height) { - while (rect.y() < height && vsbar->value() > vsbar->minimum()) { - vsbar->setValue(vsbar->value() - vsbar->singleStep()); + } else if (rect.y() < lowBound) { + while (rect.y() < lowBound && vsbar->value() > vsbar->minimum()) { + vsbar->setValue(vsbar->value() - sstep); rect = p_edit->cursorRect(); + moved = true; } } @@ -475,24 +487,31 @@ void VEditUtils::scrollBlockInPage(QTextEdit *p_edit, } case 2: + { // Bottom. - while (rect.y() < height && vsbar->value() > vsbar->minimum()) { - vsbar->setValue(vsbar->value() - vsbar->singleStep()); + int lowBound = height - p_margin; + while (rect.y() < lowBound && vsbar->value() > vsbar->minimum()) { + vsbar->setValue(vsbar->value() - sstep); rect = p_edit->cursorRect(); + moved = true; } break; + } default: break; } - p_edit->ensureCursorVisible(); + if (moved) { + p_edit->ensureCursorVisible(); + } } void VEditUtils::scrollBlockInPage(QPlainTextEdit *p_edit, int p_blockNum, - int p_dest) + int p_dest, + int p_margin) { QTextDocument *doc = p_edit->document(); QTextCursor cursor = p_edit->textCursor(); @@ -502,9 +521,9 @@ void VEditUtils::scrollBlockInPage(QPlainTextEdit *p_edit, QTextBlock block = doc->findBlockByNumber(p_blockNum); - int pib = cursor.positionInBlock(); if (cursor.block().blockNumber() != p_blockNum) { // Move the cursor to the block. + int pib = cursor.positionInBlock(); if (pib >= block.length()) { pib = block.length() - 1; } @@ -513,15 +532,20 @@ void VEditUtils::scrollBlockInPage(QPlainTextEdit *p_edit, p_edit->setTextCursor(cursor); } - // Scroll to let current cursor locate in proper position. - p_edit->ensureCursorVisible(); QScrollBar *vsbar = p_edit->verticalScrollBar(); - if (!vsbar || !vsbar->isVisible()) { // No vertical scrollbar. No need to scroll. return; } + int sstep = vsbar->singleStep(); + if (p_margin < sstep) { + p_margin = sstep; + } + + // Scroll to let current cursor locate in proper position. + p_edit->ensureCursorVisible(); + QRect rect = p_edit->cursorRect(); int height = p_edit->rect().height(); QScrollBar *sbar = p_edit->horizontalScrollBar(); @@ -529,12 +553,13 @@ void VEditUtils::scrollBlockInPage(QPlainTextEdit *p_edit, height -= sbar->height(); } + bool moved = false; switch (p_dest) { case 0: { // Top. - while (rect.y() > 0 && vsbar->value() < vsbar->maximum()) { - vsbar->setValue(vsbar->value() + vsbar->singleStep()); + while (rect.y() > p_margin && vsbar->value() < vsbar->maximum()) { + vsbar->setValue(vsbar->value() + sstep); rect = p_edit->cursorRect(); } @@ -545,15 +570,19 @@ void VEditUtils::scrollBlockInPage(QPlainTextEdit *p_edit, { // Center. height = qMax(height / 2, 1); - if (rect.y() > height) { - while (rect.y() > height && vsbar->value() < vsbar->maximum()) { - vsbar->setValue(vsbar->value() + vsbar->singleStep()); + int upBound = height + p_margin; + int lowBound = height - p_margin; + if (rect.y() > upBound) { + while (rect.y() > upBound && vsbar->value() < vsbar->maximum()) { + vsbar->setValue(vsbar->value() + sstep); rect = p_edit->cursorRect(); + moved = true; } - } else if (rect.y() < height) { - while (rect.y() < height && vsbar->value() > vsbar->minimum()) { - vsbar->setValue(vsbar->value() - vsbar->singleStep()); + } else if (rect.y() < lowBound) { + while (rect.y() < lowBound && vsbar->value() > vsbar->minimum()) { + vsbar->setValue(vsbar->value() - sstep); rect = p_edit->cursorRect(); + moved = true; } } @@ -561,19 +590,25 @@ void VEditUtils::scrollBlockInPage(QPlainTextEdit *p_edit, } case 2: + { // Bottom. - while (rect.y() < height && vsbar->value() > vsbar->minimum()) { - vsbar->setValue(vsbar->value() - vsbar->singleStep()); + int lowBound = height - p_margin; + while (rect.y() < lowBound && vsbar->value() > vsbar->minimum()) { + vsbar->setValue(vsbar->value() - sstep); rect = p_edit->cursorRect(); + moved = true; } break; + } default: break; } - p_edit->ensureCursorVisible(); + if (moved) { + p_edit->ensureCursorVisible(); + } } bool VEditUtils::isBlockQuoteBlock(const QTextBlock &p_block) diff --git a/src/utils/veditutils.h b/src/utils/veditutils.h index bad723d1..0825ca35 100644 --- a/src/utils/veditutils.h +++ b/src/utils/veditutils.h @@ -129,7 +129,8 @@ public: // Will set the cursor to the block. static void scrollBlockInPage(QTextEdit *p_edit, int p_blockNum, - int p_dest); + int p_dest, + int p_margin = 0); // Scroll block @p_blockNum into the visual window. // @p_dest is the position of the window: 0 for top, 1 for center, 2 for bottom. @@ -137,7 +138,8 @@ public: // Will set the cursor to the block. static void scrollBlockInPage(QPlainTextEdit *p_edit, int p_blockNum, - int p_dest); + int p_dest, + int p_margin = 0); // Check if @p_block is a auto list block. // @p_seq will be the seq number of the ordered list, or -1. diff --git a/src/vconfigmanager.cpp b/src/vconfigmanager.cpp index b2c8c50a..60c03805 100644 --- a/src/vconfigmanager.cpp +++ b/src/vconfigmanager.cpp @@ -361,6 +361,10 @@ void VConfigManager::initEditorConfigs() "enable_tab_highlight").toBool(); m_parsePasteLocalImage = getConfigFromSettings("editor", "parse_paste_local_image").toBool(); + + m_enableExtraBuffer = getConfigFromSettings("editor", "enable_extra_buffer").toBool(); + + m_autoScrollCursorLine = getConfigFromSettings("editor", "auto_scroll_cursor_line").toInt(); } void VConfigManager::initSettings() diff --git a/src/vconfigmanager.h b/src/vconfigmanager.h index 158b8173..942ea98d 100644 --- a/src/vconfigmanager.h +++ b/src/vconfigmanager.h @@ -67,6 +67,13 @@ enum SmartLivePreview WebToEditor = 0x2 }; +enum +{ + AutoScrollDisabled = 0, + AutoScrollEndOfDoc = 1, + AutoScrollAlways = 2 +}; + class VConfigManager : public QObject { public: @@ -587,6 +594,11 @@ public: const QColor &getBaseBackground() const; void setBaseBackground(const QColor &p_bg); + bool getEnableExtraBuffer() const; + + int getAutoScrollCursorLine() const; + void setAutoScrollCursorLine(int p_mode); + private: // Look up a config from user and default settings. QVariant getConfigFromSettings(const QString §ion, const QString &key) const; @@ -1050,6 +1062,16 @@ private: // Base background of MainWindow. QColor m_baseBackground; + // Whether enable extra buffer at the bottom of editor. + bool m_enableExtraBuffer; + + // Whether auto scroll cursor line to the middle of the window when + // cursor is at the bottom part of the content. + // 0 - disalbed + // 1 - end of doc + // 2 - always + int m_autoScrollCursorLine; + // The name of the config file in each directory. static const QString c_dirConfigFile; @@ -2730,4 +2752,33 @@ inline void VConfigManager::setBaseBackground(const QColor &p_bg) { m_baseBackground = p_bg; } + +inline bool VConfigManager::getEnableExtraBuffer() const +{ + return m_enableExtraBuffer; +} + +inline int VConfigManager::getAutoScrollCursorLine() const +{ + if (m_enableExtraBuffer) { + return m_autoScrollCursorLine; + } else { + return AutoScrollDisabled; + } +} + +inline void VConfigManager::setAutoScrollCursorLine(int p_mode) +{ + m_autoScrollCursorLine = p_mode; + if (p_mode == AutoScrollDisabled) { + setConfigToSettings("editor", "auto_scroll_cursor_line", m_autoScrollCursorLine); + } else { + if (!m_enableExtraBuffer) { + m_enableExtraBuffer = true; + setConfigToSettings("editor", "enable_extra_buffer", m_enableExtraBuffer); + } + + setConfigToSettings("editor", "auto_scroll_cursor_line", m_autoScrollCursorLine); + } +} #endif // VCONFIGMANAGER_H diff --git a/src/veditor.cpp b/src/veditor.cpp index 963ce9bd..4c237ca6 100644 --- a/src/veditor.cpp +++ b/src/veditor.cpp @@ -1705,3 +1705,39 @@ void VEditor::pastePlainText() m_editOps->insertText(text); } + +void VEditor::scrollCursorLineIfNecessary() +{ + bool moved = false; + QTextCursor cursor = textCursorW(); + int mode = g_config->getAutoScrollCursorLine(); + switch (mode) { + case AutoScrollEndOfDoc: + V_FALLTHROUGH; + case AutoScrollAlways: + { + const int bc = m_document->blockCount(); + int bn = cursor.blockNumber(); + int margin = -1; + if (mode == AutoScrollAlways) { + margin = m_editor->rect().height() / 8; + } else if (bn == bc - 1) { + margin = m_editor->rect().height() / 4; + } + + if (margin > -1) { + moved = true; + scrollBlockInPage(bn, 1, margin); + } + + break; + } + + default: + break; + } + + if (!moved) { + makeBlockVisible(cursor.block()); + } +} diff --git a/src/veditor.h b/src/veditor.h index 569f6d0e..6a2dc874 100644 --- a/src/veditor.h +++ b/src/veditor.h @@ -154,7 +154,7 @@ public: // @p_dest is the position of the window: 0 for top, 1 for center, 2 for bottom. // @p_blockNum is based on 0. // Will set the cursor to the block. - virtual void scrollBlockInPage(int p_blockNum, int p_dest) = 0; + virtual void scrollBlockInPage(int p_blockNum, int p_dest, int p_margin = 0) = 0; // Update config according to global configurations. virtual void updateConfig(); @@ -273,6 +273,9 @@ protected: // Paste plain text. void pastePlainText(); + // Scroll cursor line if in need. + void scrollCursorLineIfNecessary(); + QWidget *m_editor; VEditorObject *m_object; diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 313f53af..b36c6a54 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -815,7 +815,7 @@ void VMainWindow::initHelpMenu() QAction *docAct = new QAction(tr("&Documentation"), this); docAct->setToolTip(tr("View VNote's documentation")); connect(docAct, &QAction::triggered, - this, [this]() { + this, []() { QString url("http://vnote.readthedocs.io"); QDesktopServices::openUrl(url); }); @@ -823,7 +823,7 @@ void VMainWindow::initHelpMenu() QAction *donateAct = new QAction(tr("Do&nate"), this); donateAct->setToolTip(tr("Donate to VNote or view the donate list")); connect(donateAct, &QAction::triggered, - this, [this]() { + this, []() { QString url("https://github.com/tamlok/vnote#donate"); QDesktopServices::openUrl(url); }); @@ -839,7 +839,7 @@ void VMainWindow::initHelpMenu() QAction *starAct = new QAction(tr("Star VNote on &Github"), this); starAct->setToolTip(tr("Give a star to VNote on Github project")); connect(starAct, &QAction::triggered, - this, [this]() { + this, []() { QString url("https://github.com/tamlok/vnote"); QDesktopServices::openUrl(url); }); @@ -847,7 +847,7 @@ void VMainWindow::initHelpMenu() QAction *feedbackAct = new QAction(tr("&Feedback"), this); feedbackAct->setToolTip(tr("Open an issue on Github")); connect(feedbackAct, &QAction::triggered, - this, [this]() { + this, []() { QString url("https://github.com/tamlok/vnote/issues"); QDesktopServices::openUrl(url); }); @@ -958,7 +958,7 @@ void VMainWindow::initMarkdownMenu() lineNumberAct->setToolTip(tr("Enable line number in code blocks in read mode")); lineNumberAct->setCheckable(true); connect(lineNumberAct, &QAction::triggered, - this, [this](bool p_checked){ + this, [](bool p_checked){ g_config->setEnableCodeBlockLineNumber(p_checked); }); markdownMenu->addAction(lineNumberAct); @@ -1081,7 +1081,7 @@ void VMainWindow::initFileMenu() QAction *openConfigAct = new QAction(tr("Open Configuration Folder"), this); openConfigAct->setToolTip(tr("Open configuration folder of VNote")); connect(openConfigAct, &QAction::triggered, - this, [this](){ + this, [](){ QUrl url = QUrl::fromLocalFile(g_config->getConfigFolder()); QDesktopServices::openUrl(url); }); @@ -1247,7 +1247,7 @@ void VMainWindow::initEditMenu() tabAct->setToolTip(tr("Highlight all the tabs")); tabAct->setCheckable(true); connect(tabAct, &QAction::triggered, - this, [this](bool p_checked) { + this, [](bool p_checked) { g_config->setEnableTabHighlight(p_checked); }); @@ -1329,6 +1329,8 @@ void VMainWindow::initEditMenu() editMenu->addAction(tabAct); tabAct->setChecked(g_config->getEnableTabHighlight()); + + initAutoScrollCursorLineMenu(editMenu); } void VMainWindow::initDockWindows() @@ -1582,7 +1584,7 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu) htmlAct->setCheckable(true); htmlAct->setChecked(opt.m_html); connect(htmlAct, &QAction::triggered, - this, [this](bool p_checked) { + this, [](bool p_checked) { MarkdownitOption opt = g_config->getMarkdownitOption(); opt.m_html = p_checked; g_config->setMarkdownitOption(opt); @@ -1593,7 +1595,7 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu) breaksAct->setCheckable(true); breaksAct->setChecked(opt.m_breaks); connect(breaksAct, &QAction::triggered, - this, [this](bool p_checked) { + this, [](bool p_checked) { MarkdownitOption opt = g_config->getMarkdownitOption(); opt.m_breaks = p_checked; g_config->setMarkdownitOption(opt); @@ -1604,7 +1606,7 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu) linkifyAct->setCheckable(true); linkifyAct->setChecked(opt.m_linkify); connect(linkifyAct, &QAction::triggered, - this, [this](bool p_checked) { + this, [](bool p_checked) { MarkdownitOption opt = g_config->getMarkdownitOption(); opt.m_linkify = p_checked; g_config->setMarkdownitOption(opt); @@ -1615,7 +1617,7 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu) supAct->setCheckable(true); supAct->setChecked(opt.m_sup); connect(supAct, &QAction::triggered, - this, [this](bool p_checked) { + this, [](bool p_checked) { MarkdownitOption opt = g_config->getMarkdownitOption(); opt.m_sup = p_checked; g_config->setMarkdownitOption(opt); @@ -1626,7 +1628,7 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu) subAct->setCheckable(true); subAct->setChecked(opt.m_sub); connect(subAct, &QAction::triggered, - this, [this](bool p_checked) { + this, [](bool p_checked) { MarkdownitOption opt = g_config->getMarkdownitOption(); opt.m_sub = p_checked; g_config->setMarkdownitOption(opt); @@ -1637,7 +1639,7 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu) metadataAct->setCheckable(true); metadataAct->setChecked(opt.m_metadata); connect(metadataAct, &QAction::triggered, - this, [this](bool p_checked) { + this, [](bool p_checked) { MarkdownitOption opt = g_config->getMarkdownitOption(); opt.m_metadata = p_checked; g_config->setMarkdownitOption(opt); @@ -1648,7 +1650,7 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu) emojiAct->setCheckable(true); emojiAct->setChecked(opt.m_emoji); connect(emojiAct, &QAction::triggered, - this, [this](bool p_checked) { + this, [](bool p_checked) { MarkdownitOption opt = g_config->getMarkdownitOption(); opt.m_emoji = p_checked; g_config->setMarkdownitOption(opt); @@ -1740,14 +1742,10 @@ void VMainWindow::initRenderStyleMenu(QMenu *p_menu) QActionGroup *ag = new QActionGroup(this); connect(ag, &QActionGroup::triggered, - this, [this](QAction *p_action) { - if (!p_action) { - return; - } - + this, [](QAction *p_action) { QString data = p_action->data().toString(); g_config->setCssStyle(data); - vnote->updateTemplate(); + g_vnote->updateTemplate(); }); QList styles = g_config->getCssStyles(); @@ -1793,14 +1791,10 @@ void VMainWindow::initCodeBlockStyleMenu(QMenu *p_menu) QActionGroup *ag = new QActionGroup(this); connect(ag, &QActionGroup::triggered, - this, [this](QAction *p_action) { - if (!p_action) { - return; - } - + this, [](QAction *p_action) { QString data = p_action->data().toString(); g_config->setCodeBlockCssStyle(data); - vnote->updateTemplate(); + g_vnote->updateTemplate(); }); QList styles = g_config->getCodeBlockCssStyles(); @@ -1942,11 +1936,7 @@ void VMainWindow::initEditorStyleMenu(QMenu *p_menu) QActionGroup *ag = new QActionGroup(this); connect(ag, &QActionGroup::triggered, - this, [this](QAction *p_action) { - if (!p_action) { - return; - } - + this, [](QAction *p_action) { QString data = p_action->data().toString(); g_config->setEditorStyle(data); }); @@ -1968,6 +1958,49 @@ void VMainWindow::initEditorStyleMenu(QMenu *p_menu) } } +void VMainWindow::initAutoScrollCursorLineMenu(QMenu *p_menu) +{ + QMenu *subMenu = p_menu->addMenu(tr("Auto Scroll Cursor Line")); + subMenu->setToolTipsVisible(true); + + QActionGroup *ag = new QActionGroup(this); + connect(ag, &QActionGroup::triggered, + this, [](QAction *p_action) { + g_config->setAutoScrollCursorLine(p_action->data().toInt()); + }); + + int mode = g_config->getAutoScrollCursorLine(); + + int data = AutoScrollDisabled; + QAction *act = new QAction(tr("Disabled"), ag); + act->setCheckable(true); + act->setData(data); + subMenu->addAction(act); + if (mode == data) { + act->setChecked(true); + } + + data = AutoScrollEndOfDoc; + act = new QAction(tr("End Of Document"), ag); + act->setToolTip(tr("Scroll cursor line into the center when it locates at the end of document")); + act->setCheckable(true); + act->setData(data); + subMenu->addAction(act); + if (mode == data) { + act->setChecked(true); + } + + data = AutoScrollAlways; + act = new QAction(tr("Always"), ag); + act->setToolTip(tr("Always scroll cursor line into the center")); + act->setCheckable(true); + act->setData(data); + subMenu->addAction(act); + if (mode == data) { + act->setChecked(true); + } +} + void VMainWindow::setRenderBackgroundColor(QAction *action) { if (!action) { @@ -3018,11 +3051,7 @@ void VMainWindow::initThemeMenu(QMenu *p_menu) QActionGroup *ag = new QActionGroup(this); connect(ag, &QActionGroup::triggered, - this, [this](QAction *p_action) { - if (!p_action) { - return; - } - + this, [](QAction *p_action) { QString data = p_action->data().toString(); g_config->setTheme(data); }); diff --git a/src/vmainwindow.h b/src/vmainwindow.h index ef8ae7c1..64e599d4 100644 --- a/src/vmainwindow.h +++ b/src/vmainwindow.h @@ -260,7 +260,9 @@ private: // Init the Line Number submenu in Edit menu. void initEditorLineNumberMenu(QMenu *p_menu); - void initEditorStyleMenu(QMenu *p_emnu); + void initEditorStyleMenu(QMenu *p_menu); + + void initAutoScrollCursorLineMenu(QMenu *p_menu); void updateWindowTitle(const QString &str); diff --git a/src/vmdeditor.cpp b/src/vmdeditor.cpp index 24a55750..e47b1d90 100644 --- a/src/vmdeditor.cpp +++ b/src/vmdeditor.cpp @@ -91,7 +91,7 @@ VMdEditor::VMdEditor(VFile *p_file, // in this case. connect(m_pegHighlighter, &PegMarkdownHighlighter::highlightCompleted, this, [this]() { - makeBlockVisible(textCursor().block()); + scrollCursorLineIfNecessary(); if (m_freshEdit) { m_freshEdit = false; @@ -215,7 +215,7 @@ bool VMdEditor::scrollToBlock(int p_blockNumber) { QTextBlock block = document()->findBlockByNumber(p_blockNumber); if (block.isValid()) { - VEditUtils::scrollBlockInPage(this, block.blockNumber(), 0); + scrollBlockInPage(block.blockNumber(), 0); moveCursor(QTextCursor::EndOfBlock); return true; } @@ -1014,9 +1014,9 @@ bool VMdEditor::jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) return false; } -void VMdEditor::scrollBlockInPage(int p_blockNum, int p_dest) +void VMdEditor::scrollBlockInPage(int p_blockNum, int p_dest, int p_margin) { - VEditUtils::scrollBlockInPage(this, p_blockNum, p_dest); + VEditUtils::scrollBlockInPage(this, p_blockNum, p_dest, p_margin); } void VMdEditor::updateTextEditConfig() @@ -1029,6 +1029,8 @@ void VMdEditor::updateTextEditConfig() setImageLineColor(g_config->getEditorPreviewImageLineFg()); + setEnableExtraBuffer(g_config->getEnableExtraBuffer()); + int lineNumber = g_config->getEditorLineNumber(); if (lineNumber < (int)LineNumberType::None || lineNumber >= (int)LineNumberType::Invalid) { lineNumber = (int)LineNumberType::None; diff --git a/src/vmdeditor.h b/src/vmdeditor.h index df82d232..14719655 100644 --- a/src/vmdeditor.h +++ b/src/vmdeditor.h @@ -58,7 +58,7 @@ public: // Return true if @p_blockNumber is valid to scroll to. bool scrollToHeader(int p_blockNumber); - void scrollBlockInPage(int p_blockNum, int p_dest) Q_DECL_OVERRIDE; + void scrollBlockInPage(int p_blockNum, int p_dest, int p_margin = 0) Q_DECL_OVERRIDE; void updateConfig() Q_DECL_OVERRIDE; diff --git a/src/vtextdocumentlayout.cpp b/src/vtextdocumentlayout.cpp index 91bde4f1..e17ef406 100644 --- a/src/vtextdocumentlayout.cpp +++ b/src/vtextdocumentlayout.cpp @@ -41,7 +41,8 @@ VTextDocumentLayout::VTextDocumentLayout(QTextDocument *p_doc, m_lastCursorBlockWidth(-1), m_highlightCursorLineBlock(false), m_cursorLineBlockBg("#C0C0C0"), - m_cursorLineBlockNumber(-1) + m_cursorLineBlockNumber(-1), + m_extraBufferHeight(0) { } @@ -396,7 +397,7 @@ int VTextDocumentLayout::pageCount() const QSizeF VTextDocumentLayout::documentSize() const { - return QSizeF(m_width, m_height); + return QSizeF(m_width, m_height + m_extraBufferHeight); } QRectF VTextDocumentLayout::frameBoundingRect(QTextFrame *p_frame) const diff --git a/src/vtextdocumentlayout.h b/src/vtextdocumentlayout.h index 639de20d..f84055b9 100644 --- a/src/vtextdocumentlayout.h +++ b/src/vtextdocumentlayout.h @@ -77,6 +77,8 @@ public: // Request update block by block number. void updateBlockByNumber(int p_blockNumber); + void setExtraBufferHeight(int p_height); + signals: // Emit to update current cursor block width if m_cursorBlockMode is enabled. void cursorBlockWidthUpdated(int p_width); @@ -196,7 +198,7 @@ private: // The block number of the block which contains the m_width. int m_maximumWidthBlockNumber; - // Height of all the document (all the blocks). + // Height of all the document (all the blocks, excluding m_extraBufferHeight). qreal m_height; // Set the leading space of a line. @@ -237,6 +239,9 @@ private: // The block containing the cursor. int m_cursorLineBlockNumber; + + // Extra buffer height in document size. + int m_extraBufferHeight; }; inline qreal VTextDocumentLayout::getLineLeading() const @@ -325,4 +330,14 @@ inline void VTextDocumentLayout::updateOffset(const QTextBlock &p_block) updateOffsetBefore(p_block); updateOffsetAfter(p_block); } + +inline void VTextDocumentLayout::setExtraBufferHeight(int p_height) +{ + if (m_extraBufferHeight == p_height) { + return; + } + + m_extraBufferHeight = p_height; + emit documentSizeChanged(documentSize()); +} #endif // VTEXTDOCUMENTLAYOUT_H diff --git a/src/vtextedit.cpp b/src/vtextedit.cpp index 18315280..175f7989 100644 --- a/src/vtextedit.cpp +++ b/src/vtextedit.cpp @@ -52,6 +52,8 @@ void VTextEdit::init() m_highlightCursorLineBlock = false; + m_enableExtraBuffer = false; + m_imageMgr = new VImageResourceManager2(); QTextDocument *doc = document(); @@ -106,13 +108,17 @@ void VTextEdit::resizeEvent(QResizeEvent *p_event) { QTextEdit::resizeEvent(p_event); + QRect rect = contentsRect(); if (m_lineNumberType != LineNumberType::None) { - QRect rect = contentsRect(); m_lineNumberArea->setGeometry(QRect(rect.left(), rect.top(), m_lineNumberArea->calculateWidth(), rect.height())); } + + if (m_enableExtraBuffer) { + getLayout()->setExtraBufferHeight(rect.height() / 2); + } } void VTextEdit::paintLineNumberArea(QPaintEvent *p_event) diff --git a/src/vtextedit.h b/src/vtextedit.h index 9eef9fa8..13d04d76 100644 --- a/src/vtextedit.h +++ b/src/vtextedit.h @@ -78,6 +78,8 @@ public: void setDisplayScaleFactor(qreal p_factor); + void setEnableExtraBuffer(bool p_enable); + protected: void resizeEvent(QResizeEvent *p_event) Q_DECL_OVERRIDE; @@ -112,6 +114,8 @@ private: bool m_highlightCursorLineBlock; int m_defaultCursorWidth; + + bool m_enableExtraBuffer; }; inline void VTextEdit::setLineNumberType(LineNumberType p_type) @@ -131,4 +135,15 @@ inline void VTextEdit::setLineNumberColor(const QColor &p_foreground, m_lineNumberArea->setForegroundColor(p_foreground); m_lineNumberArea->setBackgroundColor(p_background); } + +inline void VTextEdit::setEnableExtraBuffer(bool p_enable) +{ + if (m_enableExtraBuffer == p_enable) { + return; + } + + m_enableExtraBuffer = p_enable; + getLayout()->setExtraBufferHeight(m_enableExtraBuffer ? contentsRect().height() / 2 + : 0); +} #endif // VTEXTEDIT_H