mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
editor: add line number and editor-line-number config
This commit is contained in:
parent
7fe89d816b
commit
4374f2d8f1
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
171
src/vedit.cpp
171
src/vedit.cpp
@ -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;
|
||||
}
|
||||
|
62
src/vedit.h
62
src/vedit.h
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user