wxWidgets使用wxStyledTextCtrl(Scintilla编辑器)的正确姿势

开发CuteMySQL/CuteSqlite开源客户端的时候,需要使用Scintilla编辑器,来高亮显示SQL语句,作为C/C++领域最成熟稳定又小巧的开源编辑器,Scintilla提供了强大的功能,wxWidgets对Scintilla进行包装后的是控件类:wxStyledTextCtrl。下面我们用正确的姿势来打开使用它。

先看下效果:

我们对该SQL编辑器的需求是:

1.显示行号

2.SQL语法高亮

3.输入SQL智能提示

4.适配DARK风格

好的,我们直接看下源码,程序员还是代码说话,其他都是废话,不多说了。

一、声明wxStyledTextCtrl的子类QSqlEditor

#pragma once
#include <wx/stc/stc.h>
#include <vector>

class QSqlEditor : public wxStyledTextCtrl
{
    ...
    // 重要函数一:初始化编辑器,适配SQL语法,高亮,行号等
    void setupSqlSyntax(int nSize, const char* face);

    // 重要函数二:自动弹出智能提示,参数tags为表名,视图,字段名,函数,存储过程等
    void autoShow(const std::vector<std::string> & tags);

    // 重要函数三:自动替换光标当前单词
    void autoReplaceWord();

private:
    ...
    
    ...
    
};

二、初始化编辑器,适配SQL语法


void QSqlEditor::setupSqlSyntax(int nSize, const char* face)
{
	// - lex setup (lex语法解释器配置)
	SetLexer(wxSTC_LEX_SQL); // 选择SQL解释器
	// Divide each styling byte into lexical class bits (default: 5) and indicator
	// bits (default: 3). If a lexer requires more than 32 lexical states, then this
	// is used to expand the possible states.
	// 将每个样式字节划分为词法类位(默认值:5)和指示符位(默认值:3)。
	// 如果词法分析器需要超过 32 个词法状态,则用于扩展可能的状态。
	// SetStyleBitsEx(5);
	StyleSetForeground(wxSTC_STYLE_DEFAULT, wxColour(255, 0, 0)); // 编辑器的文本默认的前景色(文本默认的颜色)
	StyleSetBackground(wxSTC_STYLE_DEFAULT, bkgColor); // 编辑器默认的背景色
	StyleClearAll(); // 清理编辑器所有的样式


	// - Other
	SetIndent(4); // 缩进4字符
	SetIndentationGuides(wxSTC_IV_LOOKBOTH); // 显示或隐藏缩进参考线
	UsePopUpEx(true); // 设置当用户在某些区域上按错鼠标按钮时是否自动显示弹出菜单
	
	// Error marker
	MarkerDefine(0, wxSTC_MARK_ARROW); // 设置用于箭头标记编号的符号,以及(可选)前景色(第三参数)和背景色(第四参数)
	MarkerSetBackground(0, wxColour(255, 255, 255)); // 设置第0个Marker的背景色
	MarkerSetForeground(0, wxColour(0, 0, 0)); // 设置第0个Marker的前景色

	// Font And Size 编辑器的字体和大小
	wxFont font(wxSize(0, nSize), wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, face);
	StyleSetFont(wxSTC_STYLE_DEFAULT, font);
	StyleSetSize(wxSTC_STYLE_DEFAULT, nSize);

	// - Margins
    // number margin 
	SetMarginType(0, wxSTC_MARGIN_NUMBER); // 行号边距,将边距设置为数字。此处设置第0个边距是行号数字
	SetMarginWidth(0, 37); // 设置边距宽度,第0个边距边宽
	// SetMarginBackground(0, bkgColor); // 注意,这里不生效的原因是SetMarginBackground只对SC_MARGIN_COLOUR类型的margin生效,这里第0边距是wxSTC_MARGIN_NUMBER
	

	// folding margin 折叠线边距
	SetMarginMask(1, wxSTC_MASK_FOLDERS);  // 设置第2个边距为折叠标记
	SetMarginWidth(1, 12); // 设置边距宽度,第2个边距边宽
	SetMarginSensitive(1, true); // 使第2个边距对鼠标单击敏感或不敏感。

	// - Choose folding icons 选择折叠的小图标
	MarkerDefine(wxSTC_MARKNUM_FOLDEROPEN, wxSTC_MARK_BOXMINUS); // 定义折叠线打开的图标:方框减号
	MarkerDefine(wxSTC_MARKNUM_FOLDER, wxSTC_MARK_BOXPLUS); // 定义折叠线收缩的图标:方框加号
	MarkerDefine(wxSTC_MARKNUM_FOLDERSUB, wxSTC_MARK_VLINE); // 定义子折叠线:VLINE
	MarkerDefine(wxSTC_MARKNUM_FOLDERTAIL, wxSTC_MARK_LCORNER); // 定义折叠线结束的图标:L型拐角
	MarkerDefine(wxSTC_MARKNUM_FOLDEREND, wxSTC_MARK_BOXPLUSCONNECTED); // 定义折叠线结束的图标:方框加号连接
	MarkerDefine(wxSTC_MARKNUM_FOLDEROPENMID, wxSTC_MARK_BOXMINUSCONNECTED); // 定义折叠线打开中间的图标:方框减号连接
	MarkerDefine(wxSTC_MARKNUM_FOLDERMIDTAIL, wxSTC_MARK_LCORNERCURVE); // 定义折叠线中间收尾的图标:T型拐角曲线

	// - Choose folding icon colours 选择折叠的小图标颜色
	MarkerSetForeground(wxSTC_MARKNUM_FOLDEROPEN, textColor); // 设置折叠线打开小图标的前景色
	MarkerSetBackground(wxSTC_MARKNUM_FOLDEROPEN, bkgColor); // 设置折叠线打开小图标的背景色
	MarkerSetForeground(wxSTC_MARKNUM_FOLDER, textColor); // 设置折叠线收缩小图标的前景色
	MarkerSetBackground(wxSTC_MARKNUM_FOLDER, bkgColor); // 设置折叠线收缩小图标的背景色
	MarkerSetForeground(wxSTC_MARKNUM_FOLDERSUB, textColor); // 设置子折叠线的背景色
	MarkerSetBackground(wxSTC_MARKNUM_FOLDERSUB, textColor); // 设置子折叠线的背景色
	MarkerSetForeground(wxSTC_MARKNUM_FOLDERTAIL, textColor); // 设置折叠线收缩结束小图标的背景色
	MarkerSetBackground(wxSTC_MARKNUM_FOLDERTAIL, textColor); // 设置折叠线收缩结束小图标的背景色
	MarkerSetForeground(wxSTC_MARKNUM_FOLDEREND, textColor); // 设置折叠线结束小图标的前景色
	MarkerSetBackground(wxSTC_MARKNUM_FOLDEREND, bkgColor); // 设置折叠线结束小图标的背景色
	MarkerSetForeground(wxSTC_MARKNUM_FOLDEROPENMID, textColor); // 设置折叠线打开中间小图标的前景色
	MarkerSetBackground(wxSTC_MARKNUM_FOLDEROPENMID, bkgColor); // 设置折叠线打开中间小图标的背景色
	MarkerSetForeground(wxSTC_MARKNUM_FOLDERMIDTAIL, textColor); // 设置折叠线中间收尾的小图标的前景色
	MarkerSetBackground(wxSTC_MARKNUM_FOLDERMIDTAIL, textColor); // 设置折叠线中间收尾的小图标的背景色

	// 行号文本颜色,仅仅对SetMarginType(0, wxSTC_MARGIN_NUMBER);起作用
	StyleSetForeground(wxSTC_STYLE_LINENUMBER, textColor);
	// 行号背景颜色	
	StyleSetBackground(wxSTC_STYLE_LINENUMBER, bkgColor2);
	// 折叠边背景色
	SetFoldMarginHiColour(true, bkgColor2);
	SetFoldMarginColour(true, bkgColor2);

	// - Set carlet 光标
	// Color of Carlet 光标的颜色
	SetCaretForeground(wxColour(0x00CEC0D6));
	// Width of Carlet 光标大小
	SetCaretWidth(2);
	// set the caret blinking time to 400 milliseconds 光标闪烁间隔
	SetCaretPeriod(400);

	// - Set caret line colour (设置光标所处行的颜色)
	SetCaretLineBackground(wxColour(38, 40, 46)); // 光标所处行的背景色
	SetCaretLineVisible(true); // 显示光标所处行

	// - Comment block (注释块)
	StyleSetForeground(wxSTC_SQL_DEFAULT, wxColour(0, 0, 0)); // 默认SQL的前景色
	StyleSetForeground(wxSTC_SQL_COMMENT, wxColour(32768)); // SQL注释的前景色

	// - Single comment line (注释行)
	StyleSetForeground(wxSTC_SQL_COMMENTLINE, wxColour(32768)); // 默认SQL注释的前景色

	// - SQL number 数字
	StyleSetForeground(wxSTC_SQL_NUMBER, wxColour(0x002AACB8)); // SQL数字的前景色
	StyleSetBold(wxSTC_SQL_NUMBER, true); // SQL数字加粗

	// - SQL string/operator/identifier (字符串/操作符/标识符)
	StyleSetForeground(wxSTC_SQL_STRING, wxColour(0x00cc99ff)); // SQL字符串的前景色
	StyleSetForeground(wxSTC_SQL_CHARACTER, wxColour(0x00cc99ff)); // SQL字符的前景色
	StyleSetForeground(wxSTC_SQL_OPERATOR, wxColour(0x00BCBEC4)); // SQL操作符的前景色
	StyleSetBold(wxSTC_SQL_OPERATOR, true); // SQL操作符加粗
	StyleSetForeground(wxSTC_SQL_IDENTIFIER, wxColour(0x00BCBEC4));// SQL标识符的前景色

	// - Color Of Selection (选中的颜色)
	SetSelBackground(true, wxColour(49, 106, 197)); // 选中项启用并设置背景色
	SetSelForeground(true, wxColour(255, 255, 255)); // 选中项启用并设置前景色

	// Set Keywords
	wxString keywords(sqlKeyWords);
	SetKeyWords(0, keywords);
	// Color Of Keyword
	StyleSetForeground(wxSTC_SQL_WORD, wxColour(0x00ff9966)); // 0x00CF8E6D
	StyleSetForeground(wxSTC_SQL_WORD2, wxColour(0x00ff9966));
	StyleSetForeground(wxSTC_SQL_USER1, wxColour(0x00ff9966));

	// 自动停顿的字符
	AutoCompStops(autoStopChars);

	// ignore the cmd key for CTRL+[key] 忽略CTRL+[key]
	int  n = static_cast<int>(sizeof(ignoreCtrlKey));
	for (int i = 0; i < n; i++) {
		CmdKeyClear(ignoreCtrlKey[i], wxSTC_KEYMOD_CTRL);
	}
	
	// Working Fold 
	SetProperty("fold", "1");
	SetProperty("fold.compact", "1");
	SetProperty("fold.html", "1");
	SetProperty("fold.html.preprocessor", "1");
	SetProperty("fold.comment", "1");
	SetProperty("fold.at.else", "1");
	SetProperty("fold.flags", "1");
	SetProperty("fold.preprocessor", "1");
	SetProperty("styling.within.preprocessor", "1");

	// set tab width to 4
	SetTabWidth(4);
}

上述初始化代码实现了SQL语法高亮,行号,折叠线,以及适配DARK风格的功能。因此我们的4个需求已经实现了3个,下面我们再用代码实现智能提示。

三、智能提示

智能提示,我们首先需要一个外层QSqlEditor的类QueryPageEditor,该类主要的作用:

1.创建编辑器,并显示到界面上。

2.捕捉QSqlEditor的各类事件。

3.调用QSqlEditor::autoShow函数,实现智能提示。

我们通过捕捉wxStyledTextCtrl的EVT_STC_AUTOCOMP_SELECTION事件,来实现智能提示。声明的代码简略如下:

class QueryPageEditor :  public QPanel<DatabaseSupplier>
{
	DECLARE_EVENT_TABLE()
public:
    ...
private:
    ...
    // 编辑器
    QSqlEditor* editor;
    
    // 创建编辑器
    void createEditor();
    
    // 响应EVT_STC_CHARADDED字符输入,智能提示
    void OnStcCharAdded(wxStyledTextEvent& event);

    // 响应EVT_STC_AUTOCOMP_SELECTION,自动替换当前单词
    void OnAutoCSelection(wxStyledTextEvent& event);
}

// 事件表
BEGIN_EVENT_TABLE(QueryPageEditor, wxPanel)
	...
	EVT_STC_CHARADDED(Config::DATABASE_QUERY_EDITOR_ID, OnStcCharAdded) // 字符输入 
	
	EVT_STC_AUTOCOMP_SELECTION(Config::DATABASE_QUERY_EDITOR_ID, OnAutoCSelection) // 提示选择
	...
END_EVENT_TABLE()

// 创建创建编辑器
void QueryPageEditor::createEditor()
{
	...
	editor = new QSqlEditor();
	editor->Create(this, Config::DATABASE_QUERY_EDITOR_ID, wxDefaultPosition, wxDefaultSize, wxNO_BORDER | wxCLIP_CHILDREN);
	editor->setup(12, FN("Courier New").c_str());
	editor->SetFocus();
	...
}

// 响应EVT_STC_CHARADDED字符输入,智能提示
void QueryPageEditor::OnStcCharAdded(wxStyledTextEvent& event)
{
	wxString line, preline, word;
	line = editor->GetCurLine();
	if (line.empty()) {
		return ;
	}
	preline = editor->getPrePositionTextOfCurLine();
	word = editor->getCurWord();
	size_t curPosInLine = editor->getCurPosInLine();
	std::vector<std::string> tags = delegate->getTags(line.ToStdString(), preline.ToStdString(), word.ToStdString(), curPosInLine);
	editor->autoShow(tags);
}

// 响应EVT_STC_AUTOCOMP_SELECTION,自动替换当前单词
void QueryPageEditor::OnAutoCSelection(wxStyledTextEvent& event)
{
	wxString selText = editor->GetSelectedText();
	if (selText.empty()) {
		editor->autoReplaceWord();
		return;
	}
	if ((char)selText.at(0) == '<' && (char)selText.Last() == '>') {
		editor->autoReplaceSelectTag();
		return;
	}
	editor->autoReplaceWord();
}

 然后我们再看下上述类调用两个编辑器实现的函数:

editor->autoShow(tags); // 参数tags : 要提示的单词,比如表名,函数,字段名等

editor->autoReplaceWord(); // 自动替换光标当前的单词

// 自动提示,参数tags : 要提示的单词,比如表名,函数,字段名等
void QSqlEditor::autoShow(const std::vector<std::string>& tags)
{
	if (tags.empty()) {
		return;
	}
	size_t n = tags.size();
	size_t sum = 0;
	std::for_each(tags.begin(), tags.end(), [&sum](const std::string& str) {
		sum += str.size();
	});

	char* itemList = new char[tags.size() + sum];
	memset(itemList, 0, tags.size() + sum);
	char * ptr = itemList;

	for (size_t i = 0; i < n; i++) {
		std::string tag = tags.at(i);
		if (i < n - 1) {
			tag += separator;
		}
		memcpy(ptr, tag.c_str(), tag.size());
		ptr += tag.size();
	}
	AutoCompSetSeparator(separator);
	AutoCompSetIgnoreCase(true);
	AutoCompSetCaseInsensitiveBehaviour(1);
	AutoCompStops(autoStopChars);
	AutoCompShow(0, itemList);

	delete[] itemList;
}

//  自动替换光标当前的单词
void QSqlEditor::autoReplaceWord()
{
	int curPos = GetCurrentPos();
	int start = WordStartPosition(curPos, true);
	int end = WordEndPosition(curPos, true);

	SetSelection(start, end);

	wxString text = AutoCompGetCurrentText();
	replaceSelText(text);
	AutoCompCancel();
}

好了,最后一个需求:输入SQL智能提示,也完成了。 

四, 完整代码:

Github - 类QSqlEditor代码icon-default.png?t=O83Ahttps://github.com/CuteBitSoft/CuteMySQL/tree/master/src/ui/common/editor

Github - 类QueryPageEditor代码icon-default.png?t=O83Ahttps://github.com/CuteBitSoft/CuteMySQL/tree/master/src/ui/database/rightview/page/editor

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/940706.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【基础还得练】数值分析中的样条插值

什么是三次样条&#xff08;Cubic Spline&#xff09;&#xff1f; 三次样条&#xff08;Cubic Spline&#xff09;是一种常用于数据插值和曲线拟合的数学方法&#xff0c;它利用多个三次多项式函数来平滑连接数据点&#xff0c;使得拟合曲线不仅通过所有数据点&#xff0c;同时…

AMS1117芯片驱动电路·降压芯片的驱动电路详解

目录 AMS1117常见封装 AMS1117不同系列 AMS1117驱动电路 参考数据手册 编写不易&#xff0c;仅供学习&#xff0c;请勿搬运&#xff0c;感谢理解 相同LDO芯片驱动专栏文章 LM7805系列降压芯片驱动电路降压芯片驱动电路详解-CSDN博客 ME6211C系列降压芯片驱动电路降压芯片…

[项目代码] YOLOv8 遥感航拍飞机和船舶识别 [目标检测]

项目代码下载链接 &#xff1c;项目代码&#xff1e;YOLO 遥感航拍飞机和船舶识别&#xff1c;目标检测&#xff1e;https://download.csdn.net/download/qq_53332949/90163939YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为…

《Qt Creator 4.11.1 教程》

《Qt Creator 4.11.1 教程》 一、Qt Creator 4.11.1 概述&#xff08;一&#xff09;简介&#xff08;二&#xff09;界面构成 二、常用设置指南&#xff08;一&#xff09;环境设置&#xff08;二&#xff09;文本编辑器设置&#xff08;三&#xff09;构建和运行设置 三、构建…

探索未知,乐享惊喜 —— 盲盒APP开发,开启您的个性化惊喜之旅!

在这个瞬息万变的数字时代&#xff0c;我们总在寻找那些能触动心灵、带来无限可能的小确幸。为了满足您对未知的好奇与对惊喜的渴望&#xff0c;我们匠心打造了一款全新的盲盒APP&#xff0c;旨在为您的生活增添一抹不同寻常的色彩&#xff0c;让每一次打开都是一次全新的探索与…

前端和后端解决跨域问题的方法

目前很多java web开发都是采用前后端分离框架进行开发&#xff0c;相比于单体项目容易产生跨域问题。 一、跨域问题CORS 1.什么是跨域问题&#xff1f; 后端接收到请求并返回结果了&#xff0c;浏览器把这个响应拦截了。 2.跨域问题是怎么产生的&#xff1f; 浏览器基于同源…

c#上班,上学,交通方式接口

using System;namespace INTERFACE {abstract class Person{public string Name { get; set; }public int Age { get; set; }public virtual void ShowInfo(){Console.WriteLine($"Name: {Name}, Age: {Age}");}}// 接口 IWorkinterface IWork{void GotoCompany();}/…

SQL血缘解析

Druid 作为使用率特别高的的数据库连接池工具,在具备完善的连接池管理功能外,同时Druid 的 SQL解析功能可以用来防止 SQL注入等安全风险。通过对 SQL 语句进行解析和检查,Druid 可以识别并阻止潜在的恶意 SQL 语句执行,黑名单(阻止特定的 SQL 语句执行)、白名单(仅允许特…

常用的JVM启动参数有哪些?

大家好&#xff0c;我是锋哥。今天分享关于【常用的JVM启动参数有哪些?】面试题。希望对大家有帮助&#xff1b; 常用的JVM启动参数有哪些? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 JVM启动参数用于配置Java虚拟机&#xff08;JVM&#xff09;的运行时行为…

JWT令牌与微服务

1. 什么是JWT JWT&#xff08;JSON Web Token&#xff09;是一种开放标准(RFC 7519)&#xff0c;它定义了一种紧凑且自包含的方式&#xff0c;用于作为JSON对象在各方之间安全地传输信息。JWT通常用于身份验证和信息交换。 以下是JWT的一些关键特性&#xff1a; 紧凑&#xff…

RadiAnt DICOM - 基本主题 :从 PACS 服务器打开研究

正版序列号获取&#xff1a;https://r-g.io/42ZopE RadiAnt DICOM Viewer PACS 客户端功能允许您从 PACS 主机&#xff08;图片存档和通信系统&#xff09;搜索和下载研究。 在开始之前&#xff0c;您需要确保您的 PACS 服务器和 RadiAnt 已正确配置。有关配置说明&#xff0c…

10. 虚拟机VMware Workstation Pro下共享Ubuntu和Win11文件夹

本文记录当前最新版虚拟机VMware Workstation Pro&#xff08;2024.12&#xff09;如何在win11下共享文件&#xff0c;以实现Windows与Ubuntu互传文件的目的。 1. 创建共享文件夹 1.1 先关闭虚拟机的客户机&#xff0c;打开虚拟机设置 1.2 在虚拟机设置界面找到“选项”->“…

java开发入门学习四-运算符

运算符 运算符&#xff1a; 运算法是一种特殊的符号&#xff0c;标识数据的运算&#xff0c;赋值等 根据分类 算数运算符 和前端运算法的方式是一致的&#xff0c;这里简单的描述% -- %: 取余 &#xff1a;增加 --&#xff1a; 减少 class Computed {public static voi…

EGO Planner代码解析bspline_optimizer部分(3)

1、 int BsplineOptimizer::earlyExit(void *func_data, const double *x, const double *g, const double fx, const double xnorm, const double gnorm, const double step, int n, int k, int ls) //如果force_stop_type_不为DONT_STOP就返回true&#xff0c;否则返回false…

Spring框架IOC

目录 一、Spring框架的介绍 1.1 Spring框架的概述 1.2 Spring框架的优点 二、Spring的核心 IOC技术 2.1 什么是IOC 2.2 IOC的程序入门 2.3 IOC技术总结 2.4 Spring框架的Bean管理的配置文件方式 一、Spring框架的介绍 1.1 Spring框架的概述 Spring是一个开放源代码的…

跨站脚本攻击的多种方式——以XSS-Labs为例二十关详解解题思路

一、XSS-Labs靶场环境搭建 1.1、XSS介绍 跨站脚本攻击&#xff08;XSS&#xff09;_跨站脚本测试-CSDN博客https://coffeemilk.blog.csdn.net/article/details/142266454 1.2、XSS-Labs XSS-Labs是一个学习XSS攻击手法的靶场&#xff0c;方便我们系统性的学习掌握跨站脚本攻击…

使用C语言编写UDP循环接收并打印消息的程序

使用C语言编写UDP循环接收并打印消息的程序 前提条件程序概述伪代码C语言实现编译和运行C改进之自由设定端口注意事项在本文中,我们将展示如何使用C语言编写一个简单的UDP服务器程序,该程序将循环接收来自指定端口的UDP消息,并将接收到的消息打印到控制台。我们将使用POSIX套…

html中实用标签dl dt dd(有些小众的标签 但是很好用)

背景描述 html <dl> <dt> <dd>是一组合标签&#xff0c;他们与ol li、ul li标签很相似 但是他却是没有默认前缀并且有缩进的标签 使用方式与table表格的标签一致 使用方式 dt和dd是放于dl标签内&#xff0c;dt与dd处于dl下相同级。就是dt不能放入dd内&am…

vue2实现word在线预览

实现附件在线预览是一个很常用的功能&#xff0c;这次正好碰到这样的需求&#xff0c;记录一下自己实现的过程。 首先是插件的选择&#xff0c;网上实现预览的方法主要有两种&#xff0c;一个是vue-office插件&#xff0c;另一个是docx-preivew插件。看网上其他网友的教程都能…

linux-----常用指令

文件和目录操作指令 ls&#xff08;list&#xff09;指令 功能&#xff1a;用于列出目录的内容&#xff0c;包括文件和子目录。示例&#xff1a; ls&#xff1a;列出当前目录下的所有非隐藏文件和目录。例如&#xff0c;在一个包含文件file1.txt、file2.txt和目录dir1的目录中&…