raylib 中实现搜索框,主要需要解决三个问题:键盘输入捕捉光标闪烁动画、以及文本溢出处理

1. 核心逻辑:即时模式输入

搜索框的本质是一个字符串。每一帧,你都要检查键盘是否有按键被按下,并将其追加到字符串末尾。

2. 代码实现示例

这段代码演示了如何创建一个带圆角阴影、支持退格键(Backspace)和动态光标的现代搜索框。

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include "raylib.h"
#include <string.h>

int main() {
InitWindow(800, 450, "Raylib Search Box");

// 搜索框状态
char inputText[64] = "\0"; // 存储输入的文本
int letterCount = 0; // 当前字符数量
int maxLength = 32; // 最大允许长度
bool isFocused = false; // 是否获得焦点
int framesCounter = 0; // 用于控制光标闪烁

Rectangle searchBox = { 200, 200, 400, 50 };

SetTargetFPS(60);

while (!WindowShouldClose()) {
// --- 1. 点击逻辑:判断是否点击了搜索框以激活焦点 ---
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
if (CheckCollisionPointRec(GetMousePosition(), searchBox)) isFocused = true;
else isFocused = false;
}

// --- 2. 输入逻辑:获取键盘按键 ---
if (isFocused) {
// 设置鼠标指针形状为文本输入型
SetMouseCursor(MOUSE_CURSOR_IBEAM);

// 获取按下的字符(处理 Unicode 字符)
int key = GetCharPressed();
while (key > 0) {
// 仅允许可见的 ASCII 字符且未达到长度限制
if ((key >= 32) && (key <= 125) && (letterCount < maxLength)) {
inputText[letterCount] = (char)key;
inputText[letterCount + 1] = '\0'; // 确保字符串以 null 结尾
letterCount++;
}
key = GetCharPressed(); // 检查缓冲区中下一个按下的字符
}

// 处理退格键删除字符
if (IsKeyPressed(KEY_BACKSPACE)) {
letterCount--;
if (letterCount < 0) letterCount = 0;
inputText[letterCount] = '\0';
}
framesCounter++;
}
else {
SetMouseCursor(MOUSE_CURSOR_DEFAULT);
framesCounter = 0;
}

// --- 3. 渲染逻辑 ---
BeginDrawing();
ClearBackground(RAYWHITE);

// 绘制搜索框背景与投影
DrawRectangleRounded(Rectangle{ searchBox.x + 2, searchBox.y + 2, searchBox.width, searchBox.height }, 0.2f, 8, Color{ 0, 0, 0, 40 }); // 阴影
DrawRectangleRounded(searchBox, 0.2f, 8, isFocused ? WHITE : LIGHTGRAY);
DrawRectangleRoundedLines(searchBox, 0.2f, 8, isFocused ? SKYBLUE : GRAY);

// 绘制提示文字(占位符)
if (letterCount == 0 && !isFocused) {
DrawText("Search something...", (int)searchBox.x + 15, (int)searchBox.y + 15, 20, GRAY);
}

// 绘制用户输入的文本
DrawText(inputText, (int)searchBox.x + 15, (int)searchBox.y + 15, 20, DARKGRAY);

// 绘制闪烁的光标
if (isFocused) {
if (((framesCounter / 20) % 2) == 0) {
int textWidth = MeasureText(inputText, 20);
DrawRectangle((int)searchBox.x + 15 + textWidth + 2, (int)searchBox.y + 12, 2, 26, SKYBLUE);
}
}

DrawText("Click the box to type!", 280, 270, 20, GRAY);
EndDrawing();
}

CloseWindow();
return 0;
}

3. 技术要点解析

  • **GetCharPressed() vs IsKeyPressed()**: 这是新手最容易混淆的地方。IsKeyPressed 适合判断单个功能键(如 Enter、Esc)。而 GetCharPressed 会返回键盘缓冲区里的 Unicode 编码,它能正确处理大写字母(Shift + 键)和其他符号,是做输入框的唯一正确选择。
  • 光标闪烁逻辑: 利用 framesCounter。例如 (framesCounter / 20) % 2 == 0 代表每 20 帧切换一次显示状态,从而产生“呼吸感”。
  • **命中测试 (Hit Testing)**: 搜索框必须先被“激活”(Focused)。只有当用户点击了矩形区域,键盘输入才会被监听,这防止了用户在玩游戏移动角色时意外触发搜索。

4. 进阶:如何支持中文输入?

如果你需要支持中文,事情会变得复杂一些:

  1. 字体支持:你需要使用 LoadFontEx 加载一个支持中文的 .ttf 字体文件。
  2. **输入法 (IME)**:raylib 本身对 Windows 系统输入法的集成较弱。通常游戏开发者会写一个简单的列表来显示 GetCharPressed 捕获的内容,或者调用特定的 Win32 API 来呼出系统的输入法窗口。

总结

你现在已经能够处理不规则桌面交互动态键盘输入了。

下一步:你是否想把这个搜索框与你之前的“毛玻璃”Shader 结合起来?当用户点击搜索框时,背景自动变模糊,这在游戏 UI 中是非常高端的视觉表现。

添加按下回车后打印调试信息

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include "raylib.h"
#include <stdio.h> // 用于 printf

int main() {
InitWindow(800, 450, "Raylib Search Box - Debug Mode");

char inputText[64] = "\0";
int letterCount = 0;
int maxLength = 32;
bool isFocused = false;
int framesCounter = 0;

Rectangle searchBox = { 200, 200, 400, 50 };

SetTargetFPS(60);

while (!WindowShouldClose()) {
// --- 1. 点击逻辑 ---
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
isFocused = CheckCollisionPointRec(GetMousePosition(), searchBox);
}

// --- 2. 输入逻辑 ---
if (isFocused) {
SetMouseCursor(MOUSE_CURSOR_IBEAM);

// 获取字符输入
int key = GetCharPressed();
while (key > 0) {
if ((key >= 32) && (key <= 125) && (letterCount < maxLength)) {
inputText[letterCount] = (char)key;
inputText[letterCount + 1] = '\0';
letterCount++;
}
key = GetCharPressed();
}

// 退格键删除
if (IsKeyPressed(KEY_BACKSPACE)) {
if (letterCount > 0) {
letterCount--;
inputText[letterCount] = '\0';
}
}

// --- 关键修改:按下回车打印调试信息 ---
if (IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) {
if (letterCount > 0) {
// 1. 在标准控制台打印 (stdout)
printf("[DEBUG] Search Triggered: %s (Length: %d)\n", inputText, letterCount);

// 2. 使用 raylib 自带的日志系统打印(会在终端以 INFO 形式显示)
TraceLog(LOG_INFO, "USER_INPUT: %s", inputText);

// 3. (可选) 打印完清空输入框
// letterCount = 0;
// inputText[0] = '\0';
} else {
printf("[DEBUG] Search attempted with empty string.\n");
}
}

framesCounter++;
} else {
SetMouseCursor(MOUSE_CURSOR_DEFAULT);
}

// --- 3. 渲染逻辑 ---
BeginDrawing();
ClearBackground(RAYWHITE);

// 绘制搜索框
DrawRectangleRounded(searchBox, 0.2f, 8, isFocused ? WHITE : LIGHTGRAY);
DrawRectangleRoundedLines(searchBox, 0.2f, 8, 2, isFocused ? SKYBLUE : GRAY);

// 绘制文字
DrawText(inputText, (int)searchBox.x + 15, (int)searchBox.y + 15, 20, DARKGRAY);

// 绘制提示语
if (letterCount == 0 && !isFocused) DrawText("Type and press ENTER...", (int)searchBox.x + 15, (int)searchBox.y + 15, 20, GRAY);

// 绘制光标
if (isFocused && ((framesCounter / 20) % 2 == 0)) {
DrawRectangle((int)searchBox.x + 15 + MeasureText(inputText, 20) + 2, (int)searchBox.y + 12, 2, 26, RED);
}

EndDrawing();
}

CloseWindow();
return 0;
}