Editor: support auto scrolling cursor line into center

This commit is contained in:
Le Tan 2018-10-30 20:47:41 +08:00
parent f3ff862cfb
commit aeb2263be3
15 changed files with 293 additions and 81 deletions

View File

@ -299,6 +299,17 @@ enable_tab_highlight=false
; Download images in parsed HTML text ; Download images in parsed HTML text
parse_paste_local_image=true 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] [export]
; Path of the wkhtmltopdf tool ; Path of the wkhtmltopdf tool
wkhtmltopdf=wkhtmltopdf wkhtmltopdf=wkhtmltopdf

View File

@ -406,7 +406,8 @@ int VEditUtils::selectedBlockCount(const QTextCursor &p_cursor)
void VEditUtils::scrollBlockInPage(QTextEdit *p_edit, void VEditUtils::scrollBlockInPage(QTextEdit *p_edit,
int p_blockNum, int p_blockNum,
int p_dest) int p_dest,
int p_margin)
{ {
QTextDocument *doc = p_edit->document(); QTextDocument *doc = p_edit->document();
QTextCursor cursor = p_edit->textCursor(); QTextCursor cursor = p_edit->textCursor();
@ -416,9 +417,9 @@ void VEditUtils::scrollBlockInPage(QTextEdit *p_edit,
QTextBlock block = doc->findBlockByNumber(p_blockNum); QTextBlock block = doc->findBlockByNumber(p_blockNum);
int pib = cursor.positionInBlock();
if (cursor.block().blockNumber() != p_blockNum) { if (cursor.block().blockNumber() != p_blockNum) {
// Move the cursor to the block. // Move the cursor to the block.
int pib = cursor.positionInBlock();
if (pib >= block.length()) { if (pib >= block.length()) {
pib = block.length() - 1; pib = block.length() - 1;
} }
@ -427,15 +428,20 @@ void VEditUtils::scrollBlockInPage(QTextEdit *p_edit,
p_edit->setTextCursor(cursor); p_edit->setTextCursor(cursor);
} }
// Scroll to let current cursor locate in proper position.
p_edit->ensureCursorVisible();
QScrollBar *vsbar = p_edit->verticalScrollBar(); QScrollBar *vsbar = p_edit->verticalScrollBar();
if (!vsbar || !vsbar->isVisible()) { if (!vsbar || !vsbar->isVisible()) {
// No vertical scrollbar. No need to scroll. // No vertical scrollbar. No need to scroll.
return; 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(); QRect rect = p_edit->cursorRect();
int height = p_edit->rect().height(); int height = p_edit->rect().height();
QScrollBar *sbar = p_edit->horizontalScrollBar(); QScrollBar *sbar = p_edit->horizontalScrollBar();
@ -443,13 +449,15 @@ void VEditUtils::scrollBlockInPage(QTextEdit *p_edit,
height -= sbar->height(); height -= sbar->height();
} }
bool moved = false;
switch (p_dest) { switch (p_dest) {
case 0: case 0:
{ {
// Top. // Top.
while (rect.y() > 0 && vsbar->value() < vsbar->maximum()) { while (rect.y() > p_margin && vsbar->value() < vsbar->maximum()) {
vsbar->setValue(vsbar->value() + vsbar->singleStep()); vsbar->setValue(vsbar->value() + sstep);
rect = p_edit->cursorRect(); rect = p_edit->cursorRect();
moved = true;
} }
break; break;
@ -459,15 +467,19 @@ void VEditUtils::scrollBlockInPage(QTextEdit *p_edit,
{ {
// Center. // Center.
height = qMax(height / 2, 1); height = qMax(height / 2, 1);
if (rect.y() > height) { int upBound = height + p_margin;
while (rect.y() > height && vsbar->value() < vsbar->maximum()) { int lowBound = height - p_margin;
vsbar->setValue(vsbar->value() + vsbar->singleStep()); if (rect.y() > upBound) {
while (rect.y() > upBound && vsbar->value() < vsbar->maximum()) {
vsbar->setValue(vsbar->value() + sstep);
rect = p_edit->cursorRect(); rect = p_edit->cursorRect();
moved = true;
} }
} else if (rect.y() < height) { } else if (rect.y() < lowBound) {
while (rect.y() < height && vsbar->value() > vsbar->minimum()) { while (rect.y() < lowBound && vsbar->value() > vsbar->minimum()) {
vsbar->setValue(vsbar->value() - vsbar->singleStep()); vsbar->setValue(vsbar->value() - sstep);
rect = p_edit->cursorRect(); rect = p_edit->cursorRect();
moved = true;
} }
} }
@ -475,24 +487,31 @@ void VEditUtils::scrollBlockInPage(QTextEdit *p_edit,
} }
case 2: case 2:
{
// Bottom. // Bottom.
while (rect.y() < height && vsbar->value() > vsbar->minimum()) { int lowBound = height - p_margin;
vsbar->setValue(vsbar->value() - vsbar->singleStep()); while (rect.y() < lowBound && vsbar->value() > vsbar->minimum()) {
vsbar->setValue(vsbar->value() - sstep);
rect = p_edit->cursorRect(); rect = p_edit->cursorRect();
moved = true;
} }
break; break;
}
default: default:
break; break;
} }
if (moved) {
p_edit->ensureCursorVisible(); p_edit->ensureCursorVisible();
} }
}
void VEditUtils::scrollBlockInPage(QPlainTextEdit *p_edit, void VEditUtils::scrollBlockInPage(QPlainTextEdit *p_edit,
int p_blockNum, int p_blockNum,
int p_dest) int p_dest,
int p_margin)
{ {
QTextDocument *doc = p_edit->document(); QTextDocument *doc = p_edit->document();
QTextCursor cursor = p_edit->textCursor(); QTextCursor cursor = p_edit->textCursor();
@ -502,9 +521,9 @@ void VEditUtils::scrollBlockInPage(QPlainTextEdit *p_edit,
QTextBlock block = doc->findBlockByNumber(p_blockNum); QTextBlock block = doc->findBlockByNumber(p_blockNum);
int pib = cursor.positionInBlock();
if (cursor.block().blockNumber() != p_blockNum) { if (cursor.block().blockNumber() != p_blockNum) {
// Move the cursor to the block. // Move the cursor to the block.
int pib = cursor.positionInBlock();
if (pib >= block.length()) { if (pib >= block.length()) {
pib = block.length() - 1; pib = block.length() - 1;
} }
@ -513,15 +532,20 @@ void VEditUtils::scrollBlockInPage(QPlainTextEdit *p_edit,
p_edit->setTextCursor(cursor); p_edit->setTextCursor(cursor);
} }
// Scroll to let current cursor locate in proper position.
p_edit->ensureCursorVisible();
QScrollBar *vsbar = p_edit->verticalScrollBar(); QScrollBar *vsbar = p_edit->verticalScrollBar();
if (!vsbar || !vsbar->isVisible()) { if (!vsbar || !vsbar->isVisible()) {
// No vertical scrollbar. No need to scroll. // No vertical scrollbar. No need to scroll.
return; 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(); QRect rect = p_edit->cursorRect();
int height = p_edit->rect().height(); int height = p_edit->rect().height();
QScrollBar *sbar = p_edit->horizontalScrollBar(); QScrollBar *sbar = p_edit->horizontalScrollBar();
@ -529,12 +553,13 @@ void VEditUtils::scrollBlockInPage(QPlainTextEdit *p_edit,
height -= sbar->height(); height -= sbar->height();
} }
bool moved = false;
switch (p_dest) { switch (p_dest) {
case 0: case 0:
{ {
// Top. // Top.
while (rect.y() > 0 && vsbar->value() < vsbar->maximum()) { while (rect.y() > p_margin && vsbar->value() < vsbar->maximum()) {
vsbar->setValue(vsbar->value() + vsbar->singleStep()); vsbar->setValue(vsbar->value() + sstep);
rect = p_edit->cursorRect(); rect = p_edit->cursorRect();
} }
@ -545,15 +570,19 @@ void VEditUtils::scrollBlockInPage(QPlainTextEdit *p_edit,
{ {
// Center. // Center.
height = qMax(height / 2, 1); height = qMax(height / 2, 1);
if (rect.y() > height) { int upBound = height + p_margin;
while (rect.y() > height && vsbar->value() < vsbar->maximum()) { int lowBound = height - p_margin;
vsbar->setValue(vsbar->value() + vsbar->singleStep()); if (rect.y() > upBound) {
while (rect.y() > upBound && vsbar->value() < vsbar->maximum()) {
vsbar->setValue(vsbar->value() + sstep);
rect = p_edit->cursorRect(); rect = p_edit->cursorRect();
moved = true;
} }
} else if (rect.y() < height) { } else if (rect.y() < lowBound) {
while (rect.y() < height && vsbar->value() > vsbar->minimum()) { while (rect.y() < lowBound && vsbar->value() > vsbar->minimum()) {
vsbar->setValue(vsbar->value() - vsbar->singleStep()); vsbar->setValue(vsbar->value() - sstep);
rect = p_edit->cursorRect(); rect = p_edit->cursorRect();
moved = true;
} }
} }
@ -561,20 +590,26 @@ void VEditUtils::scrollBlockInPage(QPlainTextEdit *p_edit,
} }
case 2: case 2:
{
// Bottom. // Bottom.
while (rect.y() < height && vsbar->value() > vsbar->minimum()) { int lowBound = height - p_margin;
vsbar->setValue(vsbar->value() - vsbar->singleStep()); while (rect.y() < lowBound && vsbar->value() > vsbar->minimum()) {
vsbar->setValue(vsbar->value() - sstep);
rect = p_edit->cursorRect(); rect = p_edit->cursorRect();
moved = true;
} }
break; break;
}
default: default:
break; break;
} }
if (moved) {
p_edit->ensureCursorVisible(); p_edit->ensureCursorVisible();
} }
}
bool VEditUtils::isBlockQuoteBlock(const QTextBlock &p_block) bool VEditUtils::isBlockQuoteBlock(const QTextBlock &p_block)
{ {

View File

@ -129,7 +129,8 @@ public:
// Will set the cursor to the block. // Will set the cursor to the block.
static void scrollBlockInPage(QTextEdit *p_edit, static void scrollBlockInPage(QTextEdit *p_edit,
int p_blockNum, int p_blockNum,
int p_dest); int p_dest,
int p_margin = 0);
// Scroll block @p_blockNum into the visual window. // 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. // @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. // Will set the cursor to the block.
static void scrollBlockInPage(QPlainTextEdit *p_edit, static void scrollBlockInPage(QPlainTextEdit *p_edit,
int p_blockNum, int p_blockNum,
int p_dest); int p_dest,
int p_margin = 0);
// Check if @p_block is a auto list block. // Check if @p_block is a auto list block.
// @p_seq will be the seq number of the ordered list, or -1. // @p_seq will be the seq number of the ordered list, or -1.

View File

@ -361,6 +361,10 @@ void VConfigManager::initEditorConfigs()
"enable_tab_highlight").toBool(); "enable_tab_highlight").toBool();
m_parsePasteLocalImage = getConfigFromSettings("editor", "parse_paste_local_image").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() void VConfigManager::initSettings()

View File

@ -67,6 +67,13 @@ enum SmartLivePreview
WebToEditor = 0x2 WebToEditor = 0x2
}; };
enum
{
AutoScrollDisabled = 0,
AutoScrollEndOfDoc = 1,
AutoScrollAlways = 2
};
class VConfigManager : public QObject class VConfigManager : public QObject
{ {
public: public:
@ -587,6 +594,11 @@ public:
const QColor &getBaseBackground() const; const QColor &getBaseBackground() const;
void setBaseBackground(const QColor &p_bg); void setBaseBackground(const QColor &p_bg);
bool getEnableExtraBuffer() const;
int getAutoScrollCursorLine() const;
void setAutoScrollCursorLine(int p_mode);
private: private:
// Look up a config from user and default settings. // Look up a config from user and default settings.
QVariant getConfigFromSettings(const QString &section, const QString &key) const; QVariant getConfigFromSettings(const QString &section, const QString &key) const;
@ -1050,6 +1062,16 @@ private:
// Base background of MainWindow. // Base background of MainWindow.
QColor m_baseBackground; 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. // The name of the config file in each directory.
static const QString c_dirConfigFile; static const QString c_dirConfigFile;
@ -2730,4 +2752,33 @@ inline void VConfigManager::setBaseBackground(const QColor &p_bg)
{ {
m_baseBackground = 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 #endif // VCONFIGMANAGER_H

View File

@ -1705,3 +1705,39 @@ void VEditor::pastePlainText()
m_editOps->insertText(text); 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());
}
}

View File

@ -154,7 +154,7 @@ public:
// @p_dest is the position of the window: 0 for top, 1 for center, 2 for bottom. // @p_dest is the position of the window: 0 for top, 1 for center, 2 for bottom.
// @p_blockNum is based on 0. // @p_blockNum is based on 0.
// Will set the cursor to the block. // 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. // Update config according to global configurations.
virtual void updateConfig(); virtual void updateConfig();
@ -273,6 +273,9 @@ protected:
// Paste plain text. // Paste plain text.
void pastePlainText(); void pastePlainText();
// Scroll cursor line if in need.
void scrollCursorLineIfNecessary();
QWidget *m_editor; QWidget *m_editor;
VEditorObject *m_object; VEditorObject *m_object;

View File

@ -815,7 +815,7 @@ void VMainWindow::initHelpMenu()
QAction *docAct = new QAction(tr("&Documentation"), this); QAction *docAct = new QAction(tr("&Documentation"), this);
docAct->setToolTip(tr("View VNote's documentation")); docAct->setToolTip(tr("View VNote's documentation"));
connect(docAct, &QAction::triggered, connect(docAct, &QAction::triggered,
this, [this]() { this, []() {
QString url("http://vnote.readthedocs.io"); QString url("http://vnote.readthedocs.io");
QDesktopServices::openUrl(url); QDesktopServices::openUrl(url);
}); });
@ -823,7 +823,7 @@ void VMainWindow::initHelpMenu()
QAction *donateAct = new QAction(tr("Do&nate"), this); QAction *donateAct = new QAction(tr("Do&nate"), this);
donateAct->setToolTip(tr("Donate to VNote or view the donate list")); donateAct->setToolTip(tr("Donate to VNote or view the donate list"));
connect(donateAct, &QAction::triggered, connect(donateAct, &QAction::triggered,
this, [this]() { this, []() {
QString url("https://github.com/tamlok/vnote#donate"); QString url("https://github.com/tamlok/vnote#donate");
QDesktopServices::openUrl(url); QDesktopServices::openUrl(url);
}); });
@ -839,7 +839,7 @@ void VMainWindow::initHelpMenu()
QAction *starAct = new QAction(tr("Star VNote on &Github"), this); QAction *starAct = new QAction(tr("Star VNote on &Github"), this);
starAct->setToolTip(tr("Give a star to VNote on Github project")); starAct->setToolTip(tr("Give a star to VNote on Github project"));
connect(starAct, &QAction::triggered, connect(starAct, &QAction::triggered,
this, [this]() { this, []() {
QString url("https://github.com/tamlok/vnote"); QString url("https://github.com/tamlok/vnote");
QDesktopServices::openUrl(url); QDesktopServices::openUrl(url);
}); });
@ -847,7 +847,7 @@ void VMainWindow::initHelpMenu()
QAction *feedbackAct = new QAction(tr("&Feedback"), this); QAction *feedbackAct = new QAction(tr("&Feedback"), this);
feedbackAct->setToolTip(tr("Open an issue on Github")); feedbackAct->setToolTip(tr("Open an issue on Github"));
connect(feedbackAct, &QAction::triggered, connect(feedbackAct, &QAction::triggered,
this, [this]() { this, []() {
QString url("https://github.com/tamlok/vnote/issues"); QString url("https://github.com/tamlok/vnote/issues");
QDesktopServices::openUrl(url); QDesktopServices::openUrl(url);
}); });
@ -958,7 +958,7 @@ void VMainWindow::initMarkdownMenu()
lineNumberAct->setToolTip(tr("Enable line number in code blocks in read mode")); lineNumberAct->setToolTip(tr("Enable line number in code blocks in read mode"));
lineNumberAct->setCheckable(true); lineNumberAct->setCheckable(true);
connect(lineNumberAct, &QAction::triggered, connect(lineNumberAct, &QAction::triggered,
this, [this](bool p_checked){ this, [](bool p_checked){
g_config->setEnableCodeBlockLineNumber(p_checked); g_config->setEnableCodeBlockLineNumber(p_checked);
}); });
markdownMenu->addAction(lineNumberAct); markdownMenu->addAction(lineNumberAct);
@ -1081,7 +1081,7 @@ void VMainWindow::initFileMenu()
QAction *openConfigAct = new QAction(tr("Open Configuration Folder"), this); QAction *openConfigAct = new QAction(tr("Open Configuration Folder"), this);
openConfigAct->setToolTip(tr("Open configuration folder of VNote")); openConfigAct->setToolTip(tr("Open configuration folder of VNote"));
connect(openConfigAct, &QAction::triggered, connect(openConfigAct, &QAction::triggered,
this, [this](){ this, [](){
QUrl url = QUrl::fromLocalFile(g_config->getConfigFolder()); QUrl url = QUrl::fromLocalFile(g_config->getConfigFolder());
QDesktopServices::openUrl(url); QDesktopServices::openUrl(url);
}); });
@ -1247,7 +1247,7 @@ void VMainWindow::initEditMenu()
tabAct->setToolTip(tr("Highlight all the tabs")); tabAct->setToolTip(tr("Highlight all the tabs"));
tabAct->setCheckable(true); tabAct->setCheckable(true);
connect(tabAct, &QAction::triggered, connect(tabAct, &QAction::triggered,
this, [this](bool p_checked) { this, [](bool p_checked) {
g_config->setEnableTabHighlight(p_checked); g_config->setEnableTabHighlight(p_checked);
}); });
@ -1329,6 +1329,8 @@ void VMainWindow::initEditMenu()
editMenu->addAction(tabAct); editMenu->addAction(tabAct);
tabAct->setChecked(g_config->getEnableTabHighlight()); tabAct->setChecked(g_config->getEnableTabHighlight());
initAutoScrollCursorLineMenu(editMenu);
} }
void VMainWindow::initDockWindows() void VMainWindow::initDockWindows()
@ -1582,7 +1584,7 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu)
htmlAct->setCheckable(true); htmlAct->setCheckable(true);
htmlAct->setChecked(opt.m_html); htmlAct->setChecked(opt.m_html);
connect(htmlAct, &QAction::triggered, connect(htmlAct, &QAction::triggered,
this, [this](bool p_checked) { this, [](bool p_checked) {
MarkdownitOption opt = g_config->getMarkdownitOption(); MarkdownitOption opt = g_config->getMarkdownitOption();
opt.m_html = p_checked; opt.m_html = p_checked;
g_config->setMarkdownitOption(opt); g_config->setMarkdownitOption(opt);
@ -1593,7 +1595,7 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu)
breaksAct->setCheckable(true); breaksAct->setCheckable(true);
breaksAct->setChecked(opt.m_breaks); breaksAct->setChecked(opt.m_breaks);
connect(breaksAct, &QAction::triggered, connect(breaksAct, &QAction::triggered,
this, [this](bool p_checked) { this, [](bool p_checked) {
MarkdownitOption opt = g_config->getMarkdownitOption(); MarkdownitOption opt = g_config->getMarkdownitOption();
opt.m_breaks = p_checked; opt.m_breaks = p_checked;
g_config->setMarkdownitOption(opt); g_config->setMarkdownitOption(opt);
@ -1604,7 +1606,7 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu)
linkifyAct->setCheckable(true); linkifyAct->setCheckable(true);
linkifyAct->setChecked(opt.m_linkify); linkifyAct->setChecked(opt.m_linkify);
connect(linkifyAct, &QAction::triggered, connect(linkifyAct, &QAction::triggered,
this, [this](bool p_checked) { this, [](bool p_checked) {
MarkdownitOption opt = g_config->getMarkdownitOption(); MarkdownitOption opt = g_config->getMarkdownitOption();
opt.m_linkify = p_checked; opt.m_linkify = p_checked;
g_config->setMarkdownitOption(opt); g_config->setMarkdownitOption(opt);
@ -1615,7 +1617,7 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu)
supAct->setCheckable(true); supAct->setCheckable(true);
supAct->setChecked(opt.m_sup); supAct->setChecked(opt.m_sup);
connect(supAct, &QAction::triggered, connect(supAct, &QAction::triggered,
this, [this](bool p_checked) { this, [](bool p_checked) {
MarkdownitOption opt = g_config->getMarkdownitOption(); MarkdownitOption opt = g_config->getMarkdownitOption();
opt.m_sup = p_checked; opt.m_sup = p_checked;
g_config->setMarkdownitOption(opt); g_config->setMarkdownitOption(opt);
@ -1626,7 +1628,7 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu)
subAct->setCheckable(true); subAct->setCheckable(true);
subAct->setChecked(opt.m_sub); subAct->setChecked(opt.m_sub);
connect(subAct, &QAction::triggered, connect(subAct, &QAction::triggered,
this, [this](bool p_checked) { this, [](bool p_checked) {
MarkdownitOption opt = g_config->getMarkdownitOption(); MarkdownitOption opt = g_config->getMarkdownitOption();
opt.m_sub = p_checked; opt.m_sub = p_checked;
g_config->setMarkdownitOption(opt); g_config->setMarkdownitOption(opt);
@ -1637,7 +1639,7 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu)
metadataAct->setCheckable(true); metadataAct->setCheckable(true);
metadataAct->setChecked(opt.m_metadata); metadataAct->setChecked(opt.m_metadata);
connect(metadataAct, &QAction::triggered, connect(metadataAct, &QAction::triggered,
this, [this](bool p_checked) { this, [](bool p_checked) {
MarkdownitOption opt = g_config->getMarkdownitOption(); MarkdownitOption opt = g_config->getMarkdownitOption();
opt.m_metadata = p_checked; opt.m_metadata = p_checked;
g_config->setMarkdownitOption(opt); g_config->setMarkdownitOption(opt);
@ -1648,7 +1650,7 @@ void VMainWindow::initMarkdownitOptionMenu(QMenu *p_menu)
emojiAct->setCheckable(true); emojiAct->setCheckable(true);
emojiAct->setChecked(opt.m_emoji); emojiAct->setChecked(opt.m_emoji);
connect(emojiAct, &QAction::triggered, connect(emojiAct, &QAction::triggered,
this, [this](bool p_checked) { this, [](bool p_checked) {
MarkdownitOption opt = g_config->getMarkdownitOption(); MarkdownitOption opt = g_config->getMarkdownitOption();
opt.m_emoji = p_checked; opt.m_emoji = p_checked;
g_config->setMarkdownitOption(opt); g_config->setMarkdownitOption(opt);
@ -1740,14 +1742,10 @@ void VMainWindow::initRenderStyleMenu(QMenu *p_menu)
QActionGroup *ag = new QActionGroup(this); QActionGroup *ag = new QActionGroup(this);
connect(ag, &QActionGroup::triggered, connect(ag, &QActionGroup::triggered,
this, [this](QAction *p_action) { this, [](QAction *p_action) {
if (!p_action) {
return;
}
QString data = p_action->data().toString(); QString data = p_action->data().toString();
g_config->setCssStyle(data); g_config->setCssStyle(data);
vnote->updateTemplate(); g_vnote->updateTemplate();
}); });
QList<QString> styles = g_config->getCssStyles(); QList<QString> styles = g_config->getCssStyles();
@ -1793,14 +1791,10 @@ void VMainWindow::initCodeBlockStyleMenu(QMenu *p_menu)
QActionGroup *ag = new QActionGroup(this); QActionGroup *ag = new QActionGroup(this);
connect(ag, &QActionGroup::triggered, connect(ag, &QActionGroup::triggered,
this, [this](QAction *p_action) { this, [](QAction *p_action) {
if (!p_action) {
return;
}
QString data = p_action->data().toString(); QString data = p_action->data().toString();
g_config->setCodeBlockCssStyle(data); g_config->setCodeBlockCssStyle(data);
vnote->updateTemplate(); g_vnote->updateTemplate();
}); });
QList<QString> styles = g_config->getCodeBlockCssStyles(); QList<QString> styles = g_config->getCodeBlockCssStyles();
@ -1942,11 +1936,7 @@ void VMainWindow::initEditorStyleMenu(QMenu *p_menu)
QActionGroup *ag = new QActionGroup(this); QActionGroup *ag = new QActionGroup(this);
connect(ag, &QActionGroup::triggered, connect(ag, &QActionGroup::triggered,
this, [this](QAction *p_action) { this, [](QAction *p_action) {
if (!p_action) {
return;
}
QString data = p_action->data().toString(); QString data = p_action->data().toString();
g_config->setEditorStyle(data); 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) void VMainWindow::setRenderBackgroundColor(QAction *action)
{ {
if (!action) { if (!action) {
@ -3018,11 +3051,7 @@ void VMainWindow::initThemeMenu(QMenu *p_menu)
QActionGroup *ag = new QActionGroup(this); QActionGroup *ag = new QActionGroup(this);
connect(ag, &QActionGroup::triggered, connect(ag, &QActionGroup::triggered,
this, [this](QAction *p_action) { this, [](QAction *p_action) {
if (!p_action) {
return;
}
QString data = p_action->data().toString(); QString data = p_action->data().toString();
g_config->setTheme(data); g_config->setTheme(data);
}); });

View File

@ -260,7 +260,9 @@ private:
// Init the Line Number submenu in Edit menu. // Init the Line Number submenu in Edit menu.
void initEditorLineNumberMenu(QMenu *p_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); void updateWindowTitle(const QString &str);

View File

@ -91,7 +91,7 @@ VMdEditor::VMdEditor(VFile *p_file,
// in this case. // in this case.
connect(m_pegHighlighter, &PegMarkdownHighlighter::highlightCompleted, connect(m_pegHighlighter, &PegMarkdownHighlighter::highlightCompleted,
this, [this]() { this, [this]() {
makeBlockVisible(textCursor().block()); scrollCursorLineIfNecessary();
if (m_freshEdit) { if (m_freshEdit) {
m_freshEdit = false; m_freshEdit = false;
@ -215,7 +215,7 @@ bool VMdEditor::scrollToBlock(int p_blockNumber)
{ {
QTextBlock block = document()->findBlockByNumber(p_blockNumber); QTextBlock block = document()->findBlockByNumber(p_blockNumber);
if (block.isValid()) { if (block.isValid()) {
VEditUtils::scrollBlockInPage(this, block.blockNumber(), 0); scrollBlockInPage(block.blockNumber(), 0);
moveCursor(QTextCursor::EndOfBlock); moveCursor(QTextCursor::EndOfBlock);
return true; return true;
} }
@ -1014,9 +1014,9 @@ bool VMdEditor::jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat)
return false; 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() void VMdEditor::updateTextEditConfig()
@ -1029,6 +1029,8 @@ void VMdEditor::updateTextEditConfig()
setImageLineColor(g_config->getEditorPreviewImageLineFg()); setImageLineColor(g_config->getEditorPreviewImageLineFg());
setEnableExtraBuffer(g_config->getEnableExtraBuffer());
int lineNumber = g_config->getEditorLineNumber(); int lineNumber = g_config->getEditorLineNumber();
if (lineNumber < (int)LineNumberType::None || lineNumber >= (int)LineNumberType::Invalid) { if (lineNumber < (int)LineNumberType::None || lineNumber >= (int)LineNumberType::Invalid) {
lineNumber = (int)LineNumberType::None; lineNumber = (int)LineNumberType::None;

View File

@ -58,7 +58,7 @@ public:
// Return true if @p_blockNumber is valid to scroll to. // Return true if @p_blockNumber is valid to scroll to.
bool scrollToHeader(int p_blockNumber); 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; void updateConfig() Q_DECL_OVERRIDE;

View File

@ -41,7 +41,8 @@ VTextDocumentLayout::VTextDocumentLayout(QTextDocument *p_doc,
m_lastCursorBlockWidth(-1), m_lastCursorBlockWidth(-1),
m_highlightCursorLineBlock(false), m_highlightCursorLineBlock(false),
m_cursorLineBlockBg("#C0C0C0"), m_cursorLineBlockBg("#C0C0C0"),
m_cursorLineBlockNumber(-1) m_cursorLineBlockNumber(-1),
m_extraBufferHeight(0)
{ {
} }
@ -396,7 +397,7 @@ int VTextDocumentLayout::pageCount() const
QSizeF VTextDocumentLayout::documentSize() 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 QRectF VTextDocumentLayout::frameBoundingRect(QTextFrame *p_frame) const

View File

@ -77,6 +77,8 @@ public:
// Request update block by block number. // Request update block by block number.
void updateBlockByNumber(int p_blockNumber); void updateBlockByNumber(int p_blockNumber);
void setExtraBufferHeight(int p_height);
signals: signals:
// Emit to update current cursor block width if m_cursorBlockMode is enabled. // Emit to update current cursor block width if m_cursorBlockMode is enabled.
void cursorBlockWidthUpdated(int p_width); void cursorBlockWidthUpdated(int p_width);
@ -196,7 +198,7 @@ private:
// The block number of the block which contains the m_width. // The block number of the block which contains the m_width.
int m_maximumWidthBlockNumber; 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; qreal m_height;
// Set the leading space of a line. // Set the leading space of a line.
@ -237,6 +239,9 @@ private:
// The block containing the cursor. // The block containing the cursor.
int m_cursorLineBlockNumber; int m_cursorLineBlockNumber;
// Extra buffer height in document size.
int m_extraBufferHeight;
}; };
inline qreal VTextDocumentLayout::getLineLeading() const inline qreal VTextDocumentLayout::getLineLeading() const
@ -325,4 +330,14 @@ inline void VTextDocumentLayout::updateOffset(const QTextBlock &p_block)
updateOffsetBefore(p_block); updateOffsetBefore(p_block);
updateOffsetAfter(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 #endif // VTEXTDOCUMENTLAYOUT_H

View File

@ -52,6 +52,8 @@ void VTextEdit::init()
m_highlightCursorLineBlock = false; m_highlightCursorLineBlock = false;
m_enableExtraBuffer = false;
m_imageMgr = new VImageResourceManager2(); m_imageMgr = new VImageResourceManager2();
QTextDocument *doc = document(); QTextDocument *doc = document();
@ -106,13 +108,17 @@ void VTextEdit::resizeEvent(QResizeEvent *p_event)
{ {
QTextEdit::resizeEvent(p_event); QTextEdit::resizeEvent(p_event);
if (m_lineNumberType != LineNumberType::None) {
QRect rect = contentsRect(); QRect rect = contentsRect();
if (m_lineNumberType != LineNumberType::None) {
m_lineNumberArea->setGeometry(QRect(rect.left(), m_lineNumberArea->setGeometry(QRect(rect.left(),
rect.top(), rect.top(),
m_lineNumberArea->calculateWidth(), m_lineNumberArea->calculateWidth(),
rect.height())); rect.height()));
} }
if (m_enableExtraBuffer) {
getLayout()->setExtraBufferHeight(rect.height() / 2);
}
} }
void VTextEdit::paintLineNumberArea(QPaintEvent *p_event) void VTextEdit::paintLineNumberArea(QPaintEvent *p_event)

View File

@ -78,6 +78,8 @@ public:
void setDisplayScaleFactor(qreal p_factor); void setDisplayScaleFactor(qreal p_factor);
void setEnableExtraBuffer(bool p_enable);
protected: protected:
void resizeEvent(QResizeEvent *p_event) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *p_event) Q_DECL_OVERRIDE;
@ -112,6 +114,8 @@ private:
bool m_highlightCursorLineBlock; bool m_highlightCursorLineBlock;
int m_defaultCursorWidth; int m_defaultCursorWidth;
bool m_enableExtraBuffer;
}; };
inline void VTextEdit::setLineNumberType(LineNumberType p_type) 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->setForegroundColor(p_foreground);
m_lineNumberArea->setBackgroundColor(p_background); 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 #endif // VTEXTEDIT_H