QScintilla入门指南之边界栏
一、边界栏基础知识
1. 边界栏基础知识
边界栏
就是编辑器侧⾯的侧边栏。使⽤边界栏时,需要记住如下内容:
- 无需创建边界栏对象:不必像通常创建对象那样创建边界栏。它们是⼀直存在的,但是默认情况下不可见。而且,默认情况下,有5个边界栏。但是从QScintilla 2.10开始,可以使用函数
setMargins(int margins)
来改变该值。而函数margins()
则返回该值。 - 边界栏标识符:每个边界栏都有⼀个唯⼀的标识符。计数从0开始。
- 边界栏可见性:默认情况下有5个边界栏,但是默认情况下都是不可见的。如果要使得⼀个或多个可见,只需要将他们的宽度设置为⼤于0即可。
- 边界栏类型:并非所有的边界类型都相同。存在多种边界类型,例如:行号区、符号区等。
如下图所示。该图显示了⼀个具有4个边界栏的编辑器。其中,边界0是行号区,边界1和3是符号区,边界2用于短文本:
1.1 边界栏类型
设置边界栏类型的函数如下:
void QsciScintilla::setMarginType(int margin, MarginType type);
其中参数MarginType
是枚举变量,其常用类型如下:
- NumberMargin:行号栏。记住,当行号增⻓时,边界宽度不会自动调整。因此,如果需要,必须要手动调整其宽度。
- SymbolMargin:符号栏,包括用于折叠的符号。
- SymbolMarginDefaultForegroundColor:符号栏使用默认前景色作为其背景色。
- SymbolMarginDefaultBackgroundColor:符号栏使用默认背景色作为其背景色。
- SymbolMarginColor:符号栏使用
setMarginBackgroundColor()
设置的颜色作为其背景色。 - TextMargin:文本栏。⽂本栏用于显示文本。字体大小、族、颜色都是可选的。
- TextMarginRightJustified:边界栏包含右对齐的样式文本。
1.2 边界宽度
如下函数用来设置指定边界的宽度(以像素为单位):
virtual void QsciScintilla::setMarginWidth(int margin, int width);
如下函数根据字符串字体样式计算所需的边界宽度:
virtual void QsciScintilla::setMarginWidth(int margin, const QString& s);
注意,没有函数用于指定边界的隐藏或者显示。非零值表示边界可见。
1.3 边界颜色
边界栏的前景色通过如下函数修改:
virtual void QsciScintilla::setMarginsForegroundColor(const QColor& col)
注意,没有参数用于指定特定的边距,因此该函数会影响所有边距。
而边界栏的背景色可通过如下函数修改:
virtual void QsciScintilla::setMarginsBackgroundColor(const QColor& col)
1.4 边界类型
1.4.1 行号区
如下语句可以将第n个边界栏设置为行号区:
editor->setMarginType(n, QsciScintilla::NumberMargin);
可以通过以下语句来设置边界文本(包括行号)的颜色:
editor->setMarginsForegroundColor(Qt::red);
1.4.2 符号区
如下四个函数可以将第n个边界设置为符号区:
editor->setMarginType(n, QsciScintilla::SymbolMargin);
editor->setMarginType(n, QsciScintilla::SymbolMarginDefaultBackgroundColor);
editor->setMarginType(n, QsciScintilla::SymbolMarginDefaultForegroundColor));
editor->setMarginType(n, QsciScintilla::SymbolMarginColor);
它们之间的区别与背景颜色有关:
- 第⼀个函数在默认的灰色背景上显示符号区。可以更改该颜色,但是,对函数
setMarginsBackgroundColor(const QColor& col)
的调用会更改所有边界背景色(包括此颜色)。 - 第⼆个函数在默认背景色上显示符号区。因此,此边界不会受到对
setMarginsBackgroundColor(const QColor& col)
的调用的影响。 - 第三个函数在默认的前景色上显示符号区。因此,此边界不会收到对
setMarginsBackgroundColor(const QColor& col)
的调用的影响。 - 第四个函数提供了最大的灵活性,但是仅在QScintilla 2.10中可用。无论是否进行其他设置,都可以选择此符号区的背景色。必须使用函数
setMarginBackgroundColor(int margin, const QColor &col)
。
(1)创建符号样式
创建符号时,符号的样式可以指定以下3个可选项:
- 内置符号(QScintilla的内置符号):
- QsciScintilla::Circle
- QsciScintilla::Rectangle
- QsciScintilla::RightTriangle
- QsciScintilla::SmallRectangle
- 更多可见
MarkerSymbol
枚举变量
- QPixmap:任何QPixmap对象都可以用作符号。
- QImage:任何QImage对象都可以用作符号。
例如,定义如下5种符号:
QImage sym_0 = QImage("green_dot.png").scaled(QSize(16,16));
QImage sym_1 = QImage("green_arrow.png").scaled(QSize(16,16));
QImage sym_2 = QImage("red_dot.png").scaled(QSize(16,16));
QImage sym_3 = QImage("red_arrow.png").scaled(QSize(16,16));
QsciScintilla::MarkerSymbol sym_4 = QsciScintilla::Circle;
(2)绑定符号标记
每个符号都可以绑定一个标记,通过标记来使用不同的符号。使用如下重载函数可以来分配标记:
int QsciScintilla::markerDefine(MarkerSymbol sym, int markerNumber = -1);
int QsciScintilla::markerDefine(char ch, int markerNumber = -1);
int QsciScintilla::markerDefine(const QPixmap& pm, int markerNumber = -1);
int QsciScintilla::markerDefine(const QImage& im,int markerNumber = -1);
例如:
editor->markerDefine(sym_0, 0);
editor->markerDefine(sym_1, 1);
editor->markerDefine(sym_2, 2);
editor->markerDefine(sym_3, 3);
editor->markerDefine(sym_4, 4);
(3)显示符号
要在特定行的符号区中显示符号,必须要使用该函数将符号添加到该行:
int QsciScintilla::markerAdd(int linenr, int markerNumber);
注意:该函数没有参数用以指定特定的边界栏,它实际上尝试将符号放在左侧的每个符号区中,如图:
但是,QScintilla提供了⼀种从边界栏中过滤掉一些符号的机制:
virtual void QsciScintilla::setMarginMarkerMask(int margin, int mask);
其中,参数mask
是通过二进制掩码的形式来指定该边界栏显示哪个符号(1表示显示),不显示哪个符号(0表示不显示)。标记掩码最多可以有32个。这是因为它实际上是⼀个32位宽的二进制数。因此,这也是编辑器中可定义的最大符号数量。
例如:
// 只显示标记为0、2、4的符号,即红点、绿点、黑圈
// 10101 - 显示 不显示 显示 不显示 显示
editor->setMarginMarkerMask(1, 0b10101);
// 只显示标记为1、3的符号,即红箭头、绿箭头
// 01010 - 不显示 显示 不显示 显示 不显示
editor->setMarginMarkerMask(3, 0b01010);
(4)符号句柄
假设现在已经将数百万个标符号记添加到代码栏,但是如何追踪它们?QScintilla提供了⼀个简便的机制。每个标记在添加到代码栏时,都会获得⼀个唯⼀的ID号:⼀个handler
。如果将相同的绿色标记添加到2个不同的代码行,则每个代码行都会获得一个不同的ID号,因此,ID号表示特定代码行上的特定标记。
符号的句柄可以在向特定行添加符号时通过函数返回值获得,如下所示:
// 将标记号markerNumber的实例添加到行号linenr。返回标记的句柄,可用于跟踪标记的位置,如果markerNumber无效,则返回-1。
int QsciScintilla::markerAdd(int linenr, int markerNumber);
(5)其他功能
如下,是一些其他常用的函数接口:
// 删除linenr行中带有标记号markerNumber的所有标记。如果markerNumber为-1,则从linenr中删除所有标记。
void QsciScintilla::markerDelete(int linenr, int markerNumber = -1);
// 使用标记句柄mhandle删除标记实例。
void QsciScintilla::markerDeleteHandle(int mhandle);
// 删除标记号为markerNumber的所有标记。如果markerNumber为 -1,则删除所有标记。
void QsciScintilla::markerDeleteAll(int markerNumber = -1);
// 返回行号linenr处标记的32位掩码。
unsigned QsciScintilla::markersAtLine(int linenr) const;
// 返回包含带有标记句柄mhandle的标记实例的行号。
int QsciScintilla::markerLine(int mhandle) const;
// 返回下一行的编号以包含来自32位标记掩码的至少一个标记。linenr是开始搜索的行号。mask是要搜索的标记的掩码。
int QsciScintilla::markerFindNext(int linenr, unsigned mask)const;
// 返回前一行的编号以包含至少一个来自32位标记掩码的标记。linenr是开始搜索的行号。mask是要搜索的标记的掩码。
int QsciScintilla::markerFindPrevious(int linenr, unsigned mask )const;
1.4.3 折叠区
折叠区用于显示折叠代码和展开代码的折叠符号,折叠区实际上是符号栏中的一种。
可以通过如下函数设置折叠栏样式。其默认样式为NoFoldStyle
(即禁用折叠),默认边界区为2。
virtual void QsciScintilla::setFolding(FoldStyle fold, int margin = 2);
其中,FoldStyle
是一个枚举变量,其值如下:
- NoFoldStyle:禁用折叠区。
- PlainFoldStyle:使用加号和减号的普通折叠样式。
- CircledFoldStyle:使用带圆圈的加号和减号符号的带圆圈的折叠样式。
- BoxedFoldStyle:使用盒形加号和减号的盒形折叠样式。
- CircledTreeFoldStyle:使用带有圆形加号和减号以及圆角的扁平树样式。
- BoxedTreeFoldStyle:使用带有盒形加号和减号以及直角的扁平树样式。
二、边界栏示例
定义如下枚举变量以及符号掩码:
// 符号样式
enum SymbolHandler
{
POINT = 0, // 断点
LABEL, // 标签
ARROW // 箭头
};
// 符号掩码
const int S_BREAK = 0b001; // 显示断点
const int S_LABEL = 0b010; // 显示标签
const int S_ARROW = 0b100; // 显示箭头
在mainwindow.cpp
中定义如下代码:
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
editor = new QsciScintilla(this);
setCentralWidget(editor);
// 编辑器设置
// ----------------------------------------
// 设置Tab键为4个空格
editor->setTabWidth(4);
// 开启自动缩进
editor->setAutoIndent(true);
// 高亮当前行
editor->setCaretLineVisible(true);
// 设置当前行颜色
editor->setCaretLineBackgroundColor(QColor("#E8E8FF"));
// 编辑器放大
editor->zoomIn(8);
// 边界栏设置
// ----------------------------------------
// 设置行号区和行号区宽度
editor->setMarginType(0, QsciScintilla::NumberMargin);
editor->setMarginWidth(0, 30);
// 设置符号区和符号区宽度
editor->setMarginType(1, QsciScintilla::SymbolMargin);
editor->setMarginWidth(1, 20);
// 设置符号样式
QImage breakpoint = QImage(":/breakpoint.png").scaled(QSize(25, 25));
QImage label = QImage(":/label.png").scaled(QSize(25, 25));
QImage arrow = QImage(":/arrow.png").scaled(QSize(25, 25));
// 为每个符号赋予编号
editor->markerDefine(breakpoint, SymbolHandler::POINT);
editor->markerDefine(label, SymbolHandler::LABEL);
editor->markerDefine(arrow, SymbolHandler::ARROW);
// 设置符号区掩码 全部都可以显示
editor->setMarginMarkerMask(1, S_BREAK | S_LABEL | S_ARROW);
// 添加符号
editor->markerAdd(0, SymbolHandler::ARROW);
运行结果如图:
三、鼠标点击
要想使鼠标点击边界区时显示符号,可以使用Qt的信号和槽机制使得边界区可以在鼠标点击时做出相应地反应。
3.1 设置符号区
首先,需要启用符号区的鼠标点击响应功能:
// 如果用户在开启点击能力的边界栏中单击,则会发出marginClicked()信号。
virtual void QsciScintilla::setMarginSensitivity(int margin, bool sens);
3.2 连接信号和槽
必须通过如下方式连接信号和槽:
connect(editor, SIGNAL(marginClicked(int, int, Qt::KeyboardModifiers)), this, SLOT(function(int, int, Qt::KeyboardModifiers)));
注意,不要使用如下方式连接信号和槽:
connect(editor, &QsciScintilla::marginClicked, this, &QWidget::function);
3.3 实现槽函数
槽函数定义如下:
void function_name(int margin ,int line ,Qt::KeyboardModifiers state);
其中,各参数的解释如下:
- margin:所单击的符号区索引,从0开始算
- line:所单击的行号,从0开始算(注意,行号的显示是从1开始的)
- state:此参数用于指示用户单击时是否发生了特殊情况,即是否在单击时按下了特殊按键。
- Qt::ControlModifier:Ctrl键
- Qt::ShiftModifier:Shift键
- Qt::AltModifier:Alt键
- Qt::MetaModifier:编辑器Emacs中用到的键位,国人键盘实际上并没有此键位
四、鼠标点击示例
mainwindow.h
代码如下:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class QsciScintilla;
enum SymbolHandler
{
POINT = 0,
LABEL,
ARROW
};
const int S_BREAK = 0b001;
const int S_LABEL = 0b010;
const int S_ARROW = 0b100;
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
typedef unsigned int mode_t;
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void updateSymbol(int, int, Qt::KeyboardModifiers);
private:
inline bool isBreak(mode_t mode) { return ((mode & S_BREAK) == S_BREAK); }
inline bool isLabel(mode_t mode) { return ((mode & S_LABEL) == S_LABEL); }
inline bool isArrow(mode_t mode) { return ((mode & S_ARROW) == S_ARROW); }
private:
Ui::MainWindow *ui;
QsciScintilla* editor;
};
#endif // MAINWINDOW_H
mainwindow.cpp
代码如下:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <Qsci/qsciscintilla.h>
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
editor = new QsciScintilla(this);
setCentralWidget(editor);
// 编辑器设置
// ----------------------------------------
// 设置Tab键为4个空格
editor->setTabWidth(4);
// 开启自动缩进
editor->setAutoIndent(true);
// 高亮当前行
editor->setCaretLineVisible(true);
// 设置当前行颜色
editor->setCaretLineBackgroundColor(QColor("#E8E8FF"));
// 编辑器放大
editor->zoomIn(8);
// 边界栏设置
// ----------------------------------------
// 设置行号区和行号区宽度
editor->setMarginType(0, QsciScintilla::NumberMargin);
editor->setMarginWidth(0, 30);
// 设置符号区和符号区宽度 开启符号区可点击功能
editor->setMarginType(1, QsciScintilla::SymbolMargin);
editor->setMarginWidth(1, 20);
editor->setMarginSensitivity(1, true);
// 设置符号样式
QImage breakpoint = QImage(":/breakpoint.png").scaled(QSize(25, 25));
QImage label = QImage(":/label.png").scaled(QSize(25, 25));
QImage arrow = QImage(":/arrow.png").scaled(QSize(25, 25));
// 为每个符号赋予编号
editor->markerDefine(breakpoint, SymbolHandler::POINT);
editor->markerDefine(label, SymbolHandler::LABEL);
editor->markerDefine(arrow, SymbolHandler::ARROW);
// 设置符号区掩码 全部都可以显示
editor->setMarginMarkerMask(1, S_BREAK | S_LABEL | S_ARROW);
// 连接信号和槽
connect(editor, SIGNAL(marginClicked(int, int, Qt::KeyboardModifiers)),
this, SLOT(updateSymbol(int, int, Qt::KeyboardModifiers)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::updateSymbol(int margin, int line, Qt::KeyboardModifiers state)
{
mode_t mask = editor->markersAtLine(line);
switch (state) {
case Qt::ControlModifier: // 按下Ctrl键
if (isArrow(mask))
editor->markerDelete(line, SymbolHandler::ARROW);
else
editor->markerAdd(line, SymbolHandler::ARROW); // 添加箭头
break;
case Qt::AltModifier: // 按下Alt键
if (isLabel(mask))
editor->markerDelete(line, SymbolHandler::LABEL);
else
editor->markerAdd(line, SymbolHandler::LABEL); // 添加标签
break;
default:
if (isBreak(mask))
editor->markerDelete(line, SymbolHandler::POINT);
else
editor->markerAdd(line, SymbolHandler::POINT); // 添加断点
}
}
运行结果如下图所示:
其余该系列文章如下: