소스 검색

update: easylvgl, 初步支持实体键盘的中英文输入和输入框UI显示

zengeshuai 3 달 전
부모
커밋
cc0c65b4f9

+ 45 - 2
components/easylvgl/src/components/widgets/luat_easylvgl_textarea.c

@@ -11,8 +11,23 @@
 #include "lvgl9/src/widgets/textarea/lv_textarea.h"
 #include "lvgl9/src/widgets/keyboard/lv_keyboard.h"
 #include "lvgl9/src/misc/lv_event.h"
+#include "lvgl9/src/core/lv_obj.h"
 #include <string.h>
 
+#if defined(LUAT_USE_EASYLVGL_SDL2)
+/**
+ * 设置 SDL2 文本输入矩形区域,让中文输入法能够定位到焦点
+ * @param ctx EasyLVGL 上下文
+ * @param target 目标对象
+ */
+void easylvgl_platform_sdl2_set_text_input_rect(easylvgl_ctx_t *ctx, lv_obj_t *target);
+#else
+static inline void easylvgl_platform_sdl2_set_text_input_rect(easylvgl_ctx_t *ctx, lv_obj_t *target) {
+    (void)ctx;
+    (void)target;
+}
+#endif
+
 /**
  * 从 Lua 注册表获取 EasyLVGL 上下文
  * @pre Lua 已调用 easylvgl.init 并保存 ctx
@@ -41,6 +56,7 @@ static void easylvgl_textarea_focus_cb(lv_event_t *e)
         return;
     }
 
+    // 获取事件目标和关联元数据
     lv_obj_t *target = lv_event_get_target(e);
     easylvgl_component_meta_t *meta = easylvgl_component_meta_get(target);
     if (meta == NULL || meta->ctx == NULL) {
@@ -49,11 +65,35 @@ static void easylvgl_textarea_focus_cb(lv_event_t *e)
 
     lv_event_code_t code = lv_event_get_code(e);
     if (code == LV_EVENT_FOCUSED) {
+        // 获得焦点时设置当前焦点 textarea 到上下文
         easylvgl_ctx_set_focused_textarea(meta->ctx, target);
+#if defined(LUAT_USE_EASYLVGL_SDL2)
+        easylvgl_platform_sdl2_set_text_input_rect(meta->ctx, target);
+#endif
     } else if (code == LV_EVENT_DEFOCUSED) {
+        // 失去焦点时,仅当当前焦点是本控件才置空
         if (easylvgl_ctx_get_focused_textarea(meta->ctx) == target) {
             easylvgl_ctx_set_focused_textarea(meta->ctx, NULL);
         }
+    } else if (code == LV_EVENT_PRESSED) {
+        // 按下时强制设置当前 textarea 为焦点
+        easylvgl_ctx_set_focused_textarea(meta->ctx, target);
+        if (!lv_obj_has_state(target, LV_STATE_FOCUSED)) {
+            // 如果还没有 FOCUSED 状态,则添加该状态
+            lv_obj_add_state(target, LV_STATE_FOCUSED);
+        }
+#if defined(LUAT_USE_EASYLVGL_SDL2)
+        easylvgl_platform_sdl2_set_text_input_rect(meta->ctx, target);
+#endif
+    } else if (code == LV_EVENT_CLICKED) {
+        // 点击时,如果未处于 FOCUSED 状态,主动设为 FOCUSED 并发送 LV_EVENT_FOCUSED 事件
+        if (!lv_obj_has_state(target, LV_STATE_FOCUSED)) {
+            lv_obj_add_state(target, LV_STATE_FOCUSED);
+            lv_event_send(target, LV_EVENT_FOCUSED, NULL);
+        }
+#if defined(LUAT_USE_EASYLVGL_SDL2)
+        easylvgl_platform_sdl2_set_text_input_rect(meta->ctx, target);
+#endif
     }
 }
 
@@ -117,8 +157,11 @@ lv_obj_t *easylvgl_textarea_create_from_config(void *L, int idx)
         return NULL;
     }
 
-    // 监听 focus 事件以同步系统键盘焦点
-    lv_obj_add_event_cb(textarea, easylvgl_textarea_focus_cb, LV_EVENT_FOCUSED | LV_EVENT_DEFOCUSED, NULL);
+    // 监听 focus 事件依次同步系统键盘焦点
+    lv_obj_add_event_cb(textarea, easylvgl_textarea_focus_cb, LV_EVENT_FOCUSED, NULL);
+    lv_obj_add_event_cb(textarea, easylvgl_textarea_focus_cb, LV_EVENT_DEFOCUSED, NULL);
+    lv_obj_add_event_cb(textarea, easylvgl_textarea_focus_cb, LV_EVENT_PRESSED, NULL);
+    lv_obj_add_event_cb(textarea, easylvgl_textarea_focus_cb, LV_EVENT_CLICKED, NULL);
 
     easylvgl_textarea_data_t *data = (easylvgl_textarea_data_t *)luat_heap_malloc(sizeof(easylvgl_textarea_data_t));
     if (data == NULL) {

+ 2 - 0
components/easylvgl/src/core/luat_easylvgl_input.c

@@ -7,6 +7,8 @@
 #include "luat_easylvgl.h"
 #include "lvgl9/src/widgets/textarea/lv_textarea.h"
 #include <stdint.h>
+#define LUAT_LOG_TAG "easylvgl_input"
+#include "luat_log.h"
 
 /**
  * 设置当前聚焦的 textarea

+ 2 - 0
components/easylvgl/src/platform/sdl/luat_easylvgl_display_sdl.c

@@ -46,6 +46,8 @@ static int sdl_display_init(easylvgl_ctx_t *ctx, uint16_t w, uint16_t h, lv_colo
         if (SDL_Init(SDL_INIT_VIDEO) < 0) {
             return EASYLVGL_ERR_INIT_FAILED;
         }
+        // 启用 SDL 输入法 UI
+        SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
         sdl_inited = true;
     }
     

+ 113 - 1
components/easylvgl/src/platform/sdl/luat_easylvgl_input_sdl.c

@@ -10,6 +10,11 @@
 #include <SDL2/SDL.h>
 #include <stdlib.h>
 #include <string.h>
+#include "lvgl9/src/widgets/label/lv_label.h"
+#include "lvgl9/src/widgets/textarea/lv_textarea.h"
+
+#define LUAT_LOG_TAG "easylvgl_input_sdl"
+#include "luat_log.h"
 
 /** SDL 输入驱动私有数据 */
 typedef struct {
@@ -24,8 +29,30 @@ static void sdl_process_keyboard_event(const SDL_Event *event, easylvgl_ctx_t *c
         return;
     }
 
+    if (event->type == SDL_TEXTEDITING) {
+        LLOGD("SDL_TEXTEDITING text=%s cursor=%d length=%d", event->edit.text, event->edit.start, event->edit.length);
+        return;
+    }
+
     if (event->type == SDL_KEYDOWN) {
-        easylvgl_system_keyboard_post_key(ctx, (uint32_t)event->key.keysym.sym, true);
+        uint32_t lv_key = 0;
+        switch (event->key.keysym.sym) {
+            case SDLK_BACKSPACE:
+                lv_key = LV_KEY_BACKSPACE;
+                break;
+            case SDLK_RETURN:
+            case SDLK_KP_ENTER:
+                lv_key = LV_KEY_ENTER;
+                break;
+            case SDLK_ESCAPE:
+                lv_key = LV_KEY_ESC;
+                break;
+            default:
+                break;
+        }
+        if (lv_key != 0) {
+            easylvgl_system_keyboard_post_key(ctx, lv_key, true);
+        }
     } else if (event->type == SDL_TEXTINPUT) {
         easylvgl_system_keyboard_post_text(ctx, event->text.text);
     }
@@ -165,6 +192,91 @@ static void sdl_input_calibration(easylvgl_ctx_t *ctx, int16_t *x, int16_t *y)
     (void)y;
 }
 
+/**
+ * SDL2 平台同步文本输入矩形给 SDL
+ */
+void easylvgl_platform_sdl2_set_text_input_rect(easylvgl_ctx_t *ctx, lv_obj_t *target) 
+{
+    // 基础参数和平台数据校验
+    if (ctx == NULL || target == NULL || ctx->platform_data == NULL) {
+        return;
+    }
+
+    // 复制 SDL 显示私有数据类型定义(与 display 驱动同步)
+    typedef struct {
+        SDL_Window *window;
+        SDL_Renderer *renderer;
+        SDL_Texture *texture;
+        uint16_t width;
+        uint16_t height;
+        lv_color_format_t color_format;
+    } sdl_display_data_t;
+
+    // 获取 display_data 指针并判空
+    sdl_display_data_t *display_data = (sdl_display_data_t *)ctx->platform_data;
+    if (display_data == NULL || display_data->window == NULL) {
+        return;
+    }
+
+    // 获取 LVGL 对象的屏幕坐标区域
+    lv_area_t coords;
+    lv_obj_get_coords(target, &coords);
+
+    // 初始将 lvgl 区域直接转换为 SDL_Rect
+    SDL_Rect rect = {
+        .x = coords.x1,
+        .y = coords.y1,
+        .w = lv_area_get_width(&coords),
+        .h = lv_area_get_height(&coords)
+    };
+
+    // 获取窗口实际像素尺寸(窗口可缩放情况下)
+    int window_w = display_data->width;
+    int window_h = display_data->height;
+    if (display_data->window != NULL) {
+        SDL_GetWindowSize(display_data->window, &window_w, &window_h);
+    }
+
+    // 按实际窗口尺寸缩放 LVGL 坐标
+    if (window_w > 0 && window_h > 0 && ctx->width > 0 && ctx->height > 0) {
+        rect.x = (int32_t)((int64_t)coords.x1 * window_w / ctx->width);
+        rect.y = (int32_t)((int64_t)coords.y1 * window_h / ctx->height);
+        rect.w = (int32_t)((int64_t)lv_area_get_width(&coords) * window_w / ctx->width);
+        rect.h = (int32_t)((int64_t)lv_area_get_height(&coords) * window_h / ctx->height);
+    }
+
+    // 如果是文本区域,进一步精确到光标位置
+    lv_point_t letter_pos = {0};
+    lv_obj_t *label = lv_textarea_get_label(target);
+    if (label != NULL) {
+        // 获取当前光标在 label 内的字符 id
+        uint32_t cp = lv_textarea_get_cursor_pos(target);
+        // 获取 label 区域坐标
+        lv_area_t label_coords;
+        lv_obj_get_coords(label, &label_coords);
+        // 查询某字符左上角相对 label 的偏移
+        lv_label_get_letter_pos(label, cp, &letter_pos);
+        // 用 label 区域原点 + 字符偏移指定输入法聚焦位
+        rect.x = label_coords.x1 + letter_pos.x;
+        rect.y = label_coords.y1 + letter_pos.y;
+        // 推荐一个典型大小,防止输入法候选窗太大(可微调)
+        rect.w = 24;
+        rect.h = 24;
+    }
+
+    // 防止宽/高为 0,输入法无法显示(设为最小 1)
+    if (rect.w <= 0) {
+        rect.w = 1;
+    }
+    if (rect.h <= 0) {
+        rect.h = 1;
+    }
+
+    // 日志打印最终输入法矩形,并通知 SDL
+    LLOGD("easylvgl_platform_sdl2_set_text_input_rect: rect=%d,%d,%d,%d", rect.x, rect.y, rect.w, rect.h);
+    SDL_SetTextInputRect(&rect);
+}
+
 /** SDL2 输入驱动操作接口 */
 static const easylvgl_input_ops_t sdl_input_ops = {
     .read_pointer = sdl_input_read_pointer,