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(); // 清理编辑器所有的样式// - OtherSetIndent(4); // 缩进4字符SetIndentationGuides(wxSTC_IV_LOOKBOTH); // 显示或隐藏缩进参考线UsePopUpEx(true); // 设置当用户在某些区域上按错鼠标按钮时是否自动显示弹出菜单// Error markerMarkerDefine(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); // 定义子折叠线:VLINEMarkerDefine(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 KeywordswxString keywords(sqlKeyWords);SetKeyWords(0, keywords);// Color Of KeywordStyleSetForeground(wxSTC_SQL_WORD, wxColour(0x00ff9966)); // 0x00CF8E6DStyleSetForeground(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 4SetTabWidth(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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

构建高性能异步任务引擎:FastAPI + Celery + Redis

在现代应用开发中&#xff0c;异步任务处理是一个常见的需求。无论是数据处理、图像生成&#xff0c;还是复杂的计算任务&#xff0c;异步执行都能显著提升系统的响应速度和吞吐量。今天&#xff0c;我们将通过一个实际项目&#xff0c;探索如何使用 FastAPI、Celery 和 Redis …

【win10+RAGFlow+Ollama】搭建本地大模型助手(教程+源码)

一、RAGFlow简介 RAGFlow是一个基于对文档深入理解的开源RAG&#xff08;Retrieval-augmented Generation&#xff0c;检索增强生成&#xff09;引擎。 主要作用&#xff1a; 让用户创建自有知识库&#xff0c;根据设定的参数对知识库中的文件进行切块处理&#xff0c;用户向大…

C/C++圣诞树

系列文章 序号直达链接1C/C爱心代码2C/C跳动的爱心3C/C李峋同款跳动的爱心代码4C/C满屏飘字表白代码5C/C大雪纷飞代码6C/C烟花代码7C/C黑客帝国同款字母雨8C/C樱花树代码9C/C奥特曼代码10C/C精美圣诞树11C/C俄罗斯方块12C/C贪吃蛇13C/C孤单又灿烂的神-鬼怪14C/C闪烁的爱心15C…

投标心态:如何在“标海战术”中保持清醒的头脑?

在竞争激烈的市场环境下&#xff0c;“标海战术”——即大规模参与投标——已经成为许多企业争取市场份额的重要策略。然而&#xff0c;盲目追求投标数量可能导致资源浪费、团队疲劳以及战略目标的模糊化。在这种高强度的竞争模式中&#xff0c;如何保持清醒的头脑&#xff0c;…

研发效能DevOps: Vite 使用 Element Plus

目录 一、实验 1.环境 2.初始化前端项目 3.安装 vue-route 4.安装 pinia 5.安装 axios 6.安装 Element Plus 7.gitee创建工程 8. 配置路由映射 9.Vite 使用 Element Plus 二、问题 1.README.md 文档推送到gitee未自动换行 2.访问login页面显示空白 3.表单输入账户…

NVIDIA DeepStream插件之Gst-nvtracker

NVIDIA DeepStream插件之Gst-nvtracker 1. 源由2. 基础知识3. Gst-nvtracker插件3.1 插件参数3.2 插件API接口 4. 分析问题5. 总结6. 参考资料 1. 源由 这篇的主要目的是稍微吐槽下NVIDIA的设计&#xff0c;当然其实他们做的还是不错的&#xff08;从系统架构设计角度看&#…

进程内存转储工具|内存镜像提取-取证工具

1.内存转储&#xff0c;内存转储&#xff08;Memory Dump&#xff09;是将计算机的物理内存&#xff08;RAM&#xff09;内容复制到一个文件中的过程&#xff0c;这个文件通常被称为“内存转储文件”或“核心转储文件”&#xff08;Core Dump&#xff09;,内存转储的主要目的是…

Lua语言入门 - Lua 面向对象

Lua 面向对象 面向对象编程&#xff08;Object Oriented Programming&#xff0c;OOP&#xff09;是一种非常流行的计算机编程架构&#xff0c;通过创建和操作对象来设计应用程序。 以下几种编程语言都支持面向对象编程&#xff1a; CJavaObjective-CSmalltalkC#Ruby Lua 是…

Pyqt6在lineEdit中输入文件名称并创建或删除JSON文件

1、创建JSON文件 代码 import osdef addModulekeyWordFile(self):if "" ! self.lineEdit_module.text():moduleFile self.lineEdit_module.text() .jsonelse:self.toolLogPrinting(请输入模块名称)returnfilePath modulekeyWordFileDir moduleFileif os.path.e…

数据结构--堆的向上调整和向下调整

文章目录 1.完全二叉树2.堆向上调整3.堆向下调整4.测试代码 1.完全二叉树 下面的这个就是对于我们的完全二叉树的这个逻辑结构和物理结构的说明&#xff1a; 逻辑结构就是我们自己认为的进行购想出来的&#xff1b; 但是这个物理结构却是我们的这个数据结构在内存里面的真是…

智能挂号系统设计典范:SSM 结合 Vue 在医院的应用实现

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了医院预约挂号系统的开发全过程。通过分析医院预约挂号系统管理的不足&#xff0c;创建了一个计算机管理医院预约挂号系统的方案。文章介绍了医院预约挂号系统的系…

Transform组件的用法

文章目录 1. 概念介绍2. 使用方法3. 示例代码我们在上一章回中介绍了Checkbox Widget相关的内容,本章回中将介绍Transform Widget.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们在这里说的Transform是一种容器类widget,它和Container组件类似。它可以包含其它的组件…

go面试问题

1 Go的内存逃逸如何分析 go build -gcflags-m main_pointer.go 2 http状态码 300 请求的资源可包括多个位置&#xff0c;相应可返回一个资源特征与地址的列表用于用户终端&#xff08;例如&#xff1a;浏览器&#xff09;选择 301 永久移动。请求的资源已被永久的移动到新U…

TouchGFX移植(5)增加触屏驱动

一&#xff09;增加驱动代码gt9xxx.c和ctiic.c到工程中的BSP目录下: 二&#xff09;更改触摸文件STM32TouchController.cpp 1&#xff09;在STM32TouchController.cpp文件中增加&#xff1a; #include “gt9xxx.h” 2&#xff09;增加gt9xxx_init(); void STM32TouchControlle…

资源型数字化平台该如何顺利运营?

一、引言 随着信息技术的迅猛发展&#xff0c;资源型数字化平台在各领域的重要性日益凸显。此类平台整合各类资源&#xff0c;以数字化手段提升资源利用效率与价值&#xff0c;但确保其顺利运营面临诸多挑战。 二、资源型数字化平台特点 资源型数字化平台具有资源整合性&…

GitLab的安装和使用

1.GitLab 环境说明 系统版本 CentOS 7.2 x86_64 软件版本 gitlab-ce-10.8.4 GitLab 是一个用于仓库管理系统的开源项目&#xff0c;使用Git作为代码管理工具&#xff0c;并在此基础上搭建起来的web服务。可通过Web界面进行访问公开的或者私人项目。它拥有与Github类似的功能…

Leetcode 串联所有单词的子串

算法思想&#xff08;中文解释&#xff09; 这道题目要求我们在字符串 s 中找到所有子串&#xff0c;这些子串是字符串数组 words 中所有单词的串联&#xff0c;并且每个单词只能使用一次&#xff0c;且顺序可以任意。下面是代码的算法思想&#xff1a; 1. 核心思路 分解问题…

解析在OceanBase创建分区的常见问题|OceanBase 用户问题精粹

在《分区策略和管理分区计划的实践方案》这篇文章中&#xff0c;我们介绍了在ODC中制定分区策略及有效管理分区计划的经验。有不少用户在该帖下提出了使用中的问题&#xff0c;其中一个关于创建分区的限制条件的问题&#xff0c;也是很多用户遭遇的老问题。因此本文以其为切入&…

有哪些免费的 ERP 软件可供选择?哪些 ERP 软件使用体验较好?

想找个 “免费” 的 ERP 软件&#xff1f; 咱得知道&#xff0c;ERP 那可是涉及财务、人力、供应链、采购、销售等好多方面的重要企业软件。功能这么全&#xff0c;能免费才怪呢&#xff01;真要是有免费的&#xff0c;早就火遍大江南北&#xff0c;说不定把市场都垄断了&…

centos-stream9系统安装docker

如果之前安装过docker需要删除之前的。 sudo dnf -y remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine 安装yum-utils工具&#xff1a; dnf -y install yum-utils dnf-plugin…