editor: add line number and editor-line-number config

This commit is contained in:
Le Tan 2017-07-07 16:46:41 +08:00
parent 7fe89d816b
commit 4374f2d8f1
9 changed files with 352 additions and 4 deletions

View File

@ -15,6 +15,8 @@ font-family: Hiragino Sans GB, 冬青黑体, Microsoft YaHei, 微软雅黑, Micr
# [VNote] Style for trailing space
trailing-space: ffebee
font-size: 12
line-number-background: bdbdbd
line-number-foreground: 424242
editor-selection
foreground: eeeeee

View File

@ -53,6 +53,10 @@ enable_vim_mode=false
; Enable smart input method in Vim mode (disable IM in non-Insert modes)
enable_smart_im_in_vim_mode=true
; Display an area besides the editor area to show line number
; 0 - None, 1 - Absolute, 2 - Relative
editor_line_number=0
[session]
tools_dock_checked=true

View File

@ -150,6 +150,9 @@ void VConfigManager::initialize()
m_enableSmartImInVimMode = getConfigFromSettings("global",
"enable_smart_im_in_vim_mode").toBool();
m_editorLineNumber = getConfigFromSettings("global",
"editor_line_number").toInt();
}
void VConfigManager::readPredefinedColorsFromSettings()
@ -336,6 +339,8 @@ void VConfigManager::updateMarkdownEditStyle()
static const QString defaultVimVisualBg = "#90CAF9";
static const QString defaultVimReplaceBg = "#F8BBD0";
static const QString defaultTrailingSpaceBackground = "#FFEBEE";
static const QString defaultLineNumberBg = "#BDBDBD";
static const QString defaultLineNumberFg = "#424242";
// Read style file .mdhl
QString file(getEditorStyleUrl());
@ -392,11 +397,23 @@ void VConfigManager::updateMarkdownEditStyle()
}
m_editorTrailingSpaceBg = defaultTrailingSpaceBackground;
m_editorLineNumberBg = defaultLineNumberBg;
m_editorLineNumberFg = defaultLineNumberFg;
auto editorIt = styles.find("editor");
if (editorIt != styles.end()) {
auto trailingIt = editorIt->find("trailing-space");
if (trailingIt != editorIt->end()) {
m_editorTrailingSpaceBg = "#" + *trailingIt;
auto it = editorIt->find("trailing-space");
if (it != editorIt->end()) {
m_editorTrailingSpaceBg = "#" + *it;
}
it = editorIt->find("line-number-background");
if (it != editorIt->end()) {
m_editorLineNumberBg = "#" + *it;
}
it = editorIt->find("line-number-foreground");
if (it != editorIt->end()) {
m_editorLineNumberFg = "#" + *it;
}
}
}

View File

@ -199,6 +199,12 @@ public:
bool getEnableSmartImInVimMode() const;
void setEnableSmartImInVimMode(bool p_enabled);
int getEditorLineNumber() const;
void setEditorLineNumber(int p_mode);
const QString &getEditorLineNumberBg() const;
const QString &getEditorLineNumberFg() const;
// Get the folder the ini file exists.
QString getConfigFolder() const;
@ -364,6 +370,15 @@ private:
// Enable smart input method in Vim mode.
bool m_enableSmartImInVimMode;
// Editor line number mode.
int m_editorLineNumber;
// The background color of the line number area.
QString m_editorLineNumberBg;
// The foreground color of the line number area.
QString m_editorLineNumberFg;
// The name of the config file in each directory, obsolete.
// Use c_dirConfigFile instead.
static const QString c_obsoleteDirConfigFile;
@ -964,4 +979,30 @@ inline void VConfigManager::setEnableSmartImInVimMode(bool p_enabled)
m_enableSmartImInVimMode);
}
inline int VConfigManager::getEditorLineNumber() const
{
return m_editorLineNumber;
}
inline void VConfigManager::setEditorLineNumber(int p_mode)
{
if (m_editorLineNumber == p_mode) {
return;
}
m_editorLineNumber = p_mode;
setConfigToSettings("global", "editor_line_number",
m_editorLineNumber);
}
inline const QString &VConfigManager::getEditorLineNumberBg() const
{
return m_editorLineNumberBg;
}
inline const QString &VConfigManager::getEditorLineNumberFg() const
{
return m_editorLineNumberFg;
}
#endif // VCONFIGMANAGER_H

View File

@ -84,6 +84,16 @@ VEdit::VEdit(VFile *p_file, QWidget *p_parent)
connect(this, &VEdit::selectionChanged,
this, &VEdit::highlightSelectedWord);
m_lineNumberArea = new LineNumberArea(this);
connect(document(), &QTextDocument::blockCountChanged,
this, &VEdit::updateLineNumberAreaMargin);
connect(this, &QTextEdit::textChanged,
this, &VEdit::updateLineNumberArea);
connect(verticalScrollBar(), &QScrollBar::valueChanged,
this, &VEdit::updateLineNumberArea);
updateLineNumberAreaMargin();
}
VEdit::~VEdit()
@ -833,3 +843,164 @@ void VEdit::decorateText(TextDecoration p_decoration)
m_editOps->decorateText(p_decoration);
}
}
void VEdit::updateLineNumberAreaMargin()
{
int width = 0;
if (vconfig.getEditorLineNumber()) {
width = m_lineNumberArea->calculateWidth();
}
setViewportMargins(width, 0, 0, 0);
}
void VEdit::updateLineNumberArea()
{
if (vconfig.getEditorLineNumber()) {
if (!m_lineNumberArea->isVisible()) {
updateLineNumberAreaMargin();
m_lineNumberArea->show();
}
m_lineNumberArea->update();
} else if (m_lineNumberArea->isVisible()) {
updateLineNumberAreaMargin();
m_lineNumberArea->hide();
}
}
void VEdit::resizeEvent(QResizeEvent *p_event)
{
QTextEdit::resizeEvent(p_event);
if (vconfig.getEditorLineNumber()) {
QRect rect = contentsRect();
m_lineNumberArea->setGeometry(QRect(rect.left(),
rect.top(),
m_lineNumberArea->calculateWidth(),
rect.height()));
}
}
void VEdit::lineNumberAreaPaintEvent(QPaintEvent *p_event)
{
if (!vconfig.getEditorLineNumber()) {
updateLineNumberAreaMargin();
m_lineNumberArea->hide();
return;
}
QPainter painter(m_lineNumberArea);
painter.fillRect(p_event->rect(), vconfig.getEditorLineNumberBg());
QTextDocument *doc = document();
QAbstractTextDocumentLayout *layout = doc->documentLayout();
QTextBlock block = firstVisibleBlock();
int blockNumber = block.blockNumber();
int offsetY = contentOffsetY();
QRectF rect = layout->blockBoundingRect(block);
int top = offsetY + (int)rect.y();
int bottom = top + (int)rect.height();
int eventTop = p_event->rect().top();
int eventBtm = p_event->rect().bottom();
const int digitHeight = m_lineNumberArea->getDigitHeight();
const int curBlockNumber = textCursor().block().blockNumber();
const bool relative = vconfig.getEditorLineNumber() == 2;
const QString &fg = vconfig.getEditorLineNumberFg();
while (block.isValid() && top <= eventBtm) {
if (block.isVisible() && bottom >= eventTop) {
QString number = QString::number(relative ? blockNumber - curBlockNumber
: blockNumber + 1);
painter.setPen(fg);
painter.drawText(0, top + 2,
m_lineNumberArea->width(),
digitHeight, Qt::AlignRight, number);
}
block = block.next();
top = bottom;
bottom = top + (int)layout->blockBoundingRect(block).height();
++blockNumber;
}
}
int VEdit::contentOffsetY()
{
int offsety = 0;
QScrollBar *sb = verticalScrollBar();
offsety = sb->value();
return -offsety;
}
QTextBlock VEdit::firstVisibleBlock()
{
QTextDocument *doc = document();
QAbstractTextDocumentLayout *layout = doc->documentLayout();
int offsetY = contentOffsetY();
// Binary search.
int idx = -1;
int start = 0, end = doc->blockCount() - 1;
while (start <= end) {
int mid = start + (end - start) / 2;
QTextBlock block = doc->findBlockByNumber(mid);
if (!block.isValid()) {
break;
}
int y = offsetY + (int)layout->blockBoundingRect(block).y();
if (y == 0) {
return block;
} else if (y < 0) {
start = mid + 1;
} else {
if (idx == -1 || mid < idx) {
idx = mid;
}
end = mid - 1;
}
}
if (idx != -1) {
return doc->findBlockByNumber(idx);
}
// Linear search.
qDebug() << "fall back to linear search for first visible block";
QTextBlock block = doc->begin();
while (block.isValid()) {
int y = offsetY + (int)layout->blockBoundingRect(block).y();
if (y >= 0) {
return block;
}
block = block.next();
}
Q_ASSERT(false);
return doc->begin();
}
int LineNumberArea::calculateWidth() const
{
int bc = m_document->blockCount();
if (m_blockCount == bc) {
return m_width;
}
const_cast<LineNumberArea *>(this)->m_blockCount = bc;
int digits = 1;
int max = qMax(1, m_blockCount);
while (max >= 10) {
max /= 10;
++digits;
}
int width = 2 + m_digitWidth * digits;
const_cast<LineNumberArea *>(this)->m_width = width;
return m_width;
}

View File

@ -7,6 +7,7 @@
#include <QVector>
#include <QList>
#include <QColor>
#include <QRect>
#include <QFontMetrics>
#include "vconstants.h"
#include "vtoc.h"
@ -16,6 +17,10 @@ class VEditOperations;
class QLabel;
class QTimer;
class VVim;
class QPaintEvent;
class QResizeEvent;
class QSize;
class QWidget;
enum class SelectionId {
CurrentLine = 0,
@ -55,6 +60,8 @@ public:
bool m_highlightWholeBlock;
};
class LineNumberArea;
class VEdit : public QTextEdit
{
Q_OBJECT
@ -95,6 +102,9 @@ public:
// Insert decoration markers or decorate selected text.
void decorateText(TextDecoration p_decoration);
// LineNumberArea will call this to request paint itself.
void lineNumberAreaPaintEvent(QPaintEvent *p_event);
signals:
// Request VEditTab to save and exit edit mode.
void saveAndRead();
@ -137,6 +147,11 @@ private slots:
void highlightTrailingSpace();
void handleCursorPositionChanged();
// Update viewport margin to hold the line number area.
void updateLineNumberAreaMargin();
void updateLineNumberArea();
protected:
QPointer<VFile> m_file;
VEditOperations *m_editOps;
@ -151,6 +166,8 @@ protected:
virtual void mouseReleaseEvent(QMouseEvent *p_event) Q_DECL_OVERRIDE;
virtual void mouseMoveEvent(QMouseEvent *p_event) Q_DECL_OVERRIDE;
virtual void resizeEvent(QResizeEvent *p_event) Q_DECL_OVERRIDE;
// Update m_config according to VConfigManager.
void updateConfig();
@ -177,6 +194,8 @@ private:
// Whether enable input method.
bool m_enableInputMethod;
LineNumberArea *m_lineNumberArea;
void showWrapLabel();
// Trigger the timer to request highlight.
@ -196,8 +215,51 @@ private:
void highlightSearchedWord(const QString &p_text, uint p_options);
bool wordInSearchedSelection(const QString &p_text);
// Return the first visible block.
QTextBlock firstVisibleBlock();
// Return the y offset of the content.
int contentOffsetY();
};
class LineNumberArea : public QWidget
{
public:
LineNumberArea(VEdit *p_editor)
: QWidget(p_editor), m_editor(p_editor),
m_document(p_editor->document()),
m_width(0), m_blockCount(-1)
{
m_digitWidth = m_editor->fontMetrics().width(QLatin1Char('9'));
m_digitHeight = m_editor->fontMetrics().height();
}
QSize sizeHint() const Q_DECL_OVERRIDE
{
return QSize(calculateWidth(), 0);
}
int calculateWidth() const;
int getDigitHeight() const
{
return m_digitHeight;
}
protected:
void paintEvent(QPaintEvent *p_event) Q_DECL_OVERRIDE
{
m_editor->lineNumberAreaPaintEvent(p_event);
}
private:
VEdit *m_editor;
const QTextDocument *m_document;
int m_width;
int m_blockCount;
int m_digitWidth;
int m_digitHeight;
};
#endif // VEDIT_H

View File

@ -848,6 +848,8 @@ void VMainWindow::initEditMenu()
initEditorBackgroundMenu(editMenu);
initEditorLineNumberMenu(editMenu);
editMenu->addAction(cursorLineAct);
cursorLineAct->setChecked(vconfig.getHighlightCursorLine());
@ -1130,6 +1132,51 @@ void VMainWindow::initEditorBackgroundMenu(QMenu *menu)
}
}
void VMainWindow::initEditorLineNumberMenu(QMenu *p_menu)
{
QMenu *lineNumMenu = p_menu->addMenu(tr("Line Number"));
lineNumMenu->setToolTipsVisible(true);
QActionGroup *lineNumAct = new QActionGroup(lineNumMenu);
connect(lineNumAct, &QActionGroup::triggered,
this, [this](QAction *p_action){
if (!p_action) {
return;
}
vconfig.setEditorLineNumber(p_action->data().toInt());
});
int lineNumberMode = vconfig.getEditorLineNumber();
QAction *act = lineNumAct->addAction(tr("None"));
act->setToolTip(tr("Do not display line number in edit mode"));
act->setCheckable(true);
act->setData(0);
lineNumMenu->addAction(act);
if (lineNumberMode == 0) {
act->setChecked(true);
}
act = lineNumAct->addAction(tr("Absolute"));
act->setToolTip(tr("Display absolute line number in edit mode"));
act->setCheckable(true);
act->setData(1);
lineNumMenu->addAction(act);
if (lineNumberMode == 1) {
act->setChecked(true);
}
act = lineNumAct->addAction(tr("Relative"));
act->setToolTip(tr("Display line number relative to current cursor line in edit mode"));
act->setCheckable(true);
act->setData(2);
lineNumMenu->addAction(act);
if (lineNumberMode == 2) {
act->setChecked(true);
}
}
void VMainWindow::updateEditorStyleMenu()
{
QMenu *menu = dynamic_cast<QMenu *>(sender());

View File

@ -124,6 +124,10 @@ private:
void initRenderBackgroundMenu(QMenu *menu);
void initRenderStyleMenu(QMenu *p_menu);
void initEditorBackgroundMenu(QMenu *menu);
// Init the Line Number submenu in Edit menu.
void initEditorLineNumberMenu(QMenu *p_menu);
void initEditorStyleMenu(QMenu *p_emnu);
void changeSplitterView(int nrPanel);
void updateWindowTitle(const QString &str);

View File

@ -234,7 +234,7 @@ void VStyleParser::fetchMarkdownEditorStyles(QPalette &palette, QFont &font,
}
// Get custom styles:
// trailing-space.
// trailing-space, line-number-background, line-number-foreground
case pmh_attr_type_other:
{
QString attrName(editorStyles->name);