You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C语言基于SDL2创建文本框及解决适配疑问

在C语言中基于SDL2实现文本框的两种方案

方案一:手动基于SDL2+SDL_ttf实现文本框

因为大部分SDL GUI库是C++实现的,用纯C的话自己实现轻量文本框更直接,核心是处理输入事件、维护文本状态、渲染文本。

核心步骤与代码示例

  1. 依赖准备:确保链接SDL2和SDL2_ttf库,编译时用gcc your_code.c -o app pkg-config --cflags --libs sdl2 SDL2_ttf``

  2. 定义文本框状态结构体:

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <string.h>
#include <stdlib.h>

typedef struct {
    SDL_Rect rect;          // 文本框位置大小
    char* content;          // 输入内容
    size_t max_len;         // 最大输入长度
    int is_focused;         // 是否获得焦点
    int cursor_pos;         // 光标位置(字符索引)
    TTF_Font* font;         // 渲染字体
    SDL_Color text_color;   // 文本颜色
    SDL_Color bg_color;     // 背景颜色
    SDL_Color border_color; // 边框颜色
} TextBox;
  1. 初始化文本框:
TextBox* textbox_init(int x, int y, int w, int h, size_t max_len, const char* font_path, int font_size) {
    TextBox* tb = malloc(sizeof(TextBox));
    tb->rect = (SDL_Rect){x, y, w, h};
    tb->content = calloc(max_len + 1, sizeof(char));
    tb->max_len = max_len;
    tb->is_focused = 0;
    tb->cursor_pos = 0;
    tb->font = TTF_OpenFont(font_path, font_size);
    tb->text_color = (SDL_Color){255,255,255,255};
    tb->bg_color = (SDL_Color){50,50,50,255};
    tb->border_color = (SDL_Color){100,100,100,255};
    return tb;
}
  1. 处理输入事件:
void textbox_handle_event(TextBox* tb, SDL_Event* e) {
    if (e->type == SDL_MOUSEBUTTONDOWN) {
        // 点击判断是否聚焦
        tb->is_focused = SDL_PointInRect(&(SDL_Point){e->button.x, e->button.y}, &tb->rect);
    }
    if (!tb->is_focused) return;

    if (e->type == SDL_KEYDOWN) {
        switch(e->key.keysym.sym) {
            case SDLK_BACKSPACE:
                if (tb->cursor_pos > 0) {
                    memmove(&tb->content[tb->cursor_pos-1], &tb->content[tb->cursor_pos], strlen(tb->content) - tb->cursor_pos + 1);
                    tb->cursor_pos--;
                }
                break;
            case SDLK_LEFT:
                if (tb->cursor_pos > 0) tb->cursor_pos--;
                break;
            case SDLK_RIGHT:
                if (tb->cursor_pos < strlen(tb->content)) tb->cursor_pos++;
                break;
            default:
                // 处理可打印字符
                if (e->key.keysym.sym >= SDLK_SPACE && e->key.keysym.sym <= SDLK_z && strlen(tb->content) < tb->max_len) {
                    char c = e->key.keysym.sym;
                    // 处理大小写(根据Shift键状态)
                    if (!(SDL_GetModState() & KMOD_SHIFT) && c >= 'A' && c <= 'Z') c += 32;
                    else if ((SDL_GetModState() & KMOD_SHIFT) && c >= 'a' && c <= 'z') c -= 32;
                    // 插入字符到光标位置
                    memmove(&tb->content[tb->cursor_pos+1], &tb->content[tb->cursor_pos], strlen(tb->content) - tb->cursor_pos + 1);
                    tb->content[tb->cursor_pos] = c;
                    tb->cursor_pos++;
                }
                break;
        }
    }
}
  1. 渲染文本框:
void textbox_render(TextBox* tb, SDL_Renderer* renderer) {
    // 绘制背景
    SDL_SetRenderDrawColor(renderer, tb->bg_color.r, tb->bg_color.g, tb->bg_color.b, tb->bg_color.a);
    SDL_RenderFillRect(renderer, &tb->rect);
    // 绘制边框(聚焦时高亮)
    SDL_Color border = tb->is_focused ? (SDL_Color){200,200,200,255} : tb->border_color;
    SDL_SetRenderDrawColor(renderer, border.r, border.g, border.b, border.a);
    SDL_RenderDrawRect(renderer, &tb->rect);
    // 渲染文本
    SDL_Surface* text_surf = TTF_RenderText_Solid(tb->font, tb->content, tb->text_color);
    SDL_Texture* text_tex = SDL_CreateTextureFromSurface(renderer, text_surf);
    SDL_Rect text_rect = {tb->rect.x + 5, tb->rect.y + (tb->rect.h - text_surf->h)/2, text_surf->w, text_surf->h};
    SDL_RenderCopy(renderer, text_tex, NULL, &text_rect);
    // 渲染光标(聚焦时)
    if (tb->is_focused) {
        // 计算光标位置
        char temp[tb->max_len + 1];
        strncpy(temp, tb->content, tb->cursor_pos);
        temp[tb->cursor_pos] = '\0';
        SDL_Surface* cursor_surf = TTF_RenderText_Solid(tb->font, temp, tb->text_color);
        int cursor_x = tb->rect.x + 5 + cursor_surf->w;
        int cursor_y = tb->rect.y + 5;
        int cursor_h = tb->rect.h - 10;
        SDL_SetRenderDrawColor(renderer, 255,255,255,255);
        SDL_RenderDrawLine(renderer, cursor_x, cursor_y, cursor_x, cursor_y + cursor_h);
        SDL_FreeSurface(cursor_surf);
    }
    // 清理资源
    SDL_FreeSurface(text_surf);
    SDL_DestroyTexture(text_tex);
}
  1. 主循环示例:
int main(int argc, char* argv[]) {
    SDL_Init(SDL_INIT_VIDEO);
    TTF_Init();
    SDL_Window* window = SDL_CreateWindow("SDL2 TextBox", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, 0);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    TextBox* tb = textbox_init(50, 50, 300, 40, 64, "arial.ttf", 24);

    int running = 1;
    SDL_Event e;
    while(running) {
        while(SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT) running = 0;
            textbox_handle_event(tb, &e);
        }
        SDL_SetRenderDrawColor(renderer, 30,30,30,255);
        SDL_RenderClear(renderer);
        textbox_render(tb, renderer);
        SDL_RenderPresent(renderer);
    }

    // 清理
    free(tb->content);
    TTF_CloseFont(tb->font);
    free(tb);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    TTF_Quit();
    SDL_Quit();
    return 0;
}

方案二:结合GTK与SDL2实现

如果不想手动写文本框逻辑,可以用GTK的GtkEntry控件作为文本输入,同时将SDL的渲染窗口嵌入GTK界面中,两者通过GTK的容器布局结合。

核心步骤与代码示例

  1. 依赖准备:链接SDL2和gtk+-3.0,编译命令:gcc your_code.c -o app pkg-config --cflags --libs sdl2 gtk+-3.0``

  2. 示例代码:

#include <SDL2/SDL.h>
#include <gtk/gtk.h>

static SDL_Window* sdl_window;
static SDL_Renderer* sdl_renderer;

// GTK窗口关闭回调
static void on_window_destroy(GtkWidget* widget, gpointer data) {
    SDL_DestroyRenderer(sdl_renderer);
    SDL_DestroyWindow(sdl_window);
    SDL_Quit();
    gtk_main_quit();
}

// SDL渲染回调(定时刷新)
static gboolean sdl_render_loop(gpointer data) {
    SDL_SetRenderDrawColor(sdl_renderer, 0, 128, 255, 255);
    SDL_RenderClear(sdl_renderer);
    // 这里添加你的SDL渲染逻辑
    SDL_RenderPresent(sdl_renderer);
    return G_SOURCE_CONTINUE;
}

// 获取GTK控件的原生窗口ID,用于SDL嵌入
static Uint32 get_gtk_window_id(GtkWidget* widget) {
    #ifdef __linux__
        return GDK_WINDOW_XID(gtk_widget_get_window(widget));
    #elif _WIN32
        return (Uint32)GDK_WINDOW_HWND(gtk_widget_get_window(widget));
    #endif
}

int main(int argc, char* argv[]) {
    gtk_init(&argc, &argv);
    SDL_Init(SDL_INIT_VIDEO);

    // 创建GTK主窗口
    GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "GTK + SDL2");
    gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
    g_signal_connect(window, "destroy", G_CALLBACK(on_window_destroy), NULL);

    // 创建垂直布局容器
    GtkWidget* vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
    gtk_container_add(GTK_CONTAINER(window), vbox);

    // 添加GTK文本框
    GtkWidget* entry = gtk_entry_new();
    gtk_entry_set_max_length(GTK_ENTRY(entry), 64);
    gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 10);

    // 添加SDL嵌入的绘图区域
    GtkWidget* sdl_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    gtk_box_pack_start(GTK_BOX(vbox), sdl_box, TRUE, TRUE, 10);
    gtk_widget_set_size_request(sdl_box, 780, 480);

    // 显示所有控件,获取原生窗口ID
    gtk_widget_show_all(window);
    gdk_window_process_all_updates(gdk_get_default_root_window());
    Uint32 window_id = get_gtk_window_id(sdl_box);

    // 创建SDL窗口嵌入到GTK控件中
    sdl_window = SDL_CreateWindowFrom((void*)window_id);
    sdl_renderer = SDL_CreateRenderer(sdl_window, -1, SDL_RENDERER_ACCELERATED);

    // 启动SDL渲染循环
    g_timeout_add(16, sdl_render_loop, NULL);

    gtk_main();
    return 0;
}

关键说明

  • 这种方式下,文本输入完全由GTK的GtkEntry处理,你可以通过gtk_entry_get_text()获取输入内容,无需自己处理键盘事件。
  • SDL负责渲染游戏/图形内容,两者共享同一个窗口界面,交互自然。

内容的提问来源于stack exchange,提问作者dMight

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 19:57:35