Chapter 175 · XUI 专业控件

CodeEdit 编辑操作

Selection 选区模型 + Editing 编辑命令:插入/删除/缩进/注释切换/多光标。

175

概念

CodeEdit 编辑操作分两层:
• Selection Model:管理光标/选区/多光标,支持 SetRange/SelectAll/GotoLineColumn
• Editing:基于 Document+Selection 执行编辑命令(插入/删除/缩进/注释)
所有编辑操作自动维护 Undo/Redo 栈,bReadonly 参数保护只读文档。

API 速查

函数说明
int xuiCodeSelectionSetRange(pSel, pDoc, iAnchor, iCaret)设置选区范围
int xuiCodeSelectionSelectAll(pSel, pDoc)全选
int xuiCodeSelectionSelectWord(pSel, pDoc, iOffset)选中单词
int xuiCodeSelectionGotoLineColumn(pSel, pDoc, iLine, iCol, bExtend)跳转行列
int xuiCodeSelectionMove(pSel, pDoc, iCommand)方向移动
int xuiCodeEditingInsertText(pDoc, pSel, sText, bReadonly)插入文本
int xuiCodeEditingDeleteBackward(pDoc, pSel, bReadonly)退格删除
int xuiCodeEditingDeleteForward(pDoc, pSel, bReadonly)前向删除
int xuiCodeEditingIndentSelection(pDoc, pSel, sIndent, bReadonly)增加缩进
int xuiCodeEditingOutdentSelection(pDoc, pSel, iCols, bReadonly)减少缩进
int xuiCodeEditingToggleLineComment(pDoc, pSel, sComment, bReadonly)切换行注释

编译本章示例

gcc -O2 -I. -o ch175.exe ch175_main.c src\xui_*.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch175.exe

示例:编程式编辑

xui_code_document pDoc = xuiCodeEditGetDocument(pEditor);
xui_code_selection_model pSel = xuiCodeEditGetSelection(pEditor);

// 跳转到第 10 行第 1 列
xuiCodeSelectionGotoLineColumn(pSel, pDoc, 10, 1, 0);

// 插入文本(自动 Undo)
xuiCodeEditingInsertText(pDoc, pSel, "#include <stdio.h>\n", 0);

// 选中第 5-8 行并增加缩进
xuiCodeSelectionGotoLineColumn(pSel, pDoc, 5, 1, 0);
xuiCodeSelectionGotoLineColumn(pSel, pDoc, 8, 1, 1); // bExtend=1
xuiCodeEditingIndentSelection(pDoc, pSel, "    ", 0);

// 切换行注释 (C 风格)
xuiCodeEditingToggleLineComment(pDoc, pSel, "//", 0);

// 确保光标可见
xuiCodeEditEnsureCaretVisible(pEditor);

完整代码

将以下代码保存为 ch175_main.c,编译后运行即可看到编程式插入/缩进/注释效果:

#include "xge.h"
#include "xui.h"

static xui_context g_pCtx;
static xui_widget g_pEditor;
static int g_bDone = 0;

static int onFrame(void* pUser) {
    (void)pUser;
    xgeClear(0x1E1E1EFF);
    xui_vec2_t pos;
    xgeMouseGet(&pos.x, &pos.y);
    xuiInputPointerMove(g_pCtx, pos.x, pos.y);
    if (xgeMouseDown(XGE_MOUSE_LEFT))
        xuiInputPointerDown(g_pCtx, pos.x, pos.y, 0);
    else
        xuiInputPointerUp(g_pCtx, pos.x, pos.y, 0);

    // 首帧执行编程式编辑
    if (!g_bDone) {
        g_bDone = 1;
        xui_code_document pDoc = xuiCodeEditGetDocument(g_pEditor);
        xui_code_selection_model pSel = xuiCodeEditGetSelection(g_pEditor);

        // 跳到第 3 行插入代码
        xuiCodeSelectionGotoLineColumn(pSel, pDoc, 3, 1, 0);
        xuiCodeEditingInsertText(pDoc, pSel,
            "    int x = 42;\n", 0);

        // 选中第 2-4 行并增加缩进
        xuiCodeSelectionGotoLineColumn(pSel, pDoc, 2, 1, 0);
        xuiCodeSelectionGotoLineColumn(pSel, pDoc, 4, 1, 1);
        xuiCodeEditingIndentSelection(pDoc, pSel, "    ", 0);

        // 切换行注释
        xuiCodeEditingToggleLineComment(pDoc, pSel, "//", 0);

        xuiCodeEditEnsureCaretVisible(g_pEditor);
    }

    xuiUpdate(g_pCtx, xgeGetDelta());
    xuiLayout(g_pCtx);
    xuiRender(g_pCtx, NULL, NULL, 0);
    return XGE_OK;
}

int main(void) {
    xge_desc_t d = { 0 };
    d.iWidth = 900; d.iHeight = 640;
    d.sTitle = "ch175 - Editing Ops";
    d.iFlags = XGE_INIT_VSYNC;
    xgeInit(&d);

    xuiCreate(&g_pCtx);
    extern const xui_proxy_t tXgeProxy;
    xui_proxy_t tXgeProxy = xuiProxyXge();
xuiSetProxy(g_pCtx, &tXgeProxy);
    xuiSetViewportSize(g_pCtx, 900, 640);

    xui_code_edit_desc_t desc = { 0 };
    xuiCodeEditCreate(g_pCtx, &g_pEditor, &desc);
    xuiCodeEditSetLanguage(g_pEditor, "c");
    xuiCodeEditSetDefaultMargins(g_pEditor, 1, 0, 0, 0);

    const char* sCode =
        "#include <stdio.h>\n"
        "int main(void) {\n"
        "    return 0;\n"
        "}\n";
    xuiCodeEditSetText(g_pEditor, sCode);

    xui_widget pRoot = xuiGetRootWidget(g_pCtx);
    xuiWidgetAddChild(pRoot, g_pEditor);
    xuiWidgetSetRect(g_pEditor, (xui_rect_t){0, 0, 900, 640});

    xgeRun(onFrame, NULL);
    xuiDestroy(g_pCtx);
    xgeUnit();
    return 0;
}

代码讲解

  • GetDocument / GetSelection:从编辑器获取文档和选区模型,所有编辑操作基于这两个对象。
  • GotoLineColumn:bExtend=0 移动光标,bExtend=1 扩展选区(用于选中多行)。
  • InsertText:在光标处插入文本,自动压入 Undo 栈。
  • IndentSelection / ToggleLineComment:对选中行批量缩进或添加/移除行注释。
  • EnsureCaretVisible:编辑后滚动视图确保光标可见。

要点与陷阱

  • bExtend:GotoLineColumn 的 bExtend=1 扩展选区而非移动光标。
  • 多光标:SelectionAdd 添加额外选区,编辑命令对所有选区生效。
  • bReadonly:传 1 时编辑操作被拒绝,返回错误码。
  • CommandMap:键盘操作通过 CommandMap 映射到 Editing 命令。

进阶练习

  1. 代码格式化:选中全部 → 逐行调整缩进。
  2. 自定义快捷键:CommandMapBind 绑定 Ctrl+/ 切换注释。
CodeEdit 编辑操作效果
图:CodeEdit 编辑操作渲染效果