fix(sdk/hello): M2.71控件真机bug批次 — 圆形进度脏区+整环修复/Slider拖动/Dialog·Sheet背景快照scrim不叠黑/Disabled圆角α/点击重置息屏/滑动跟手/IME残影·键位·主题配色/List选中点击 (agent-162)
This commit is contained in:
1 parent
b12404a986
commit
b8c36bd33c
14 files changed
+554
-79
No files matched your search
@@ -158,6 +158,11 @@ typedef struct meow_ui_events {
|
||||
void (*on_swipe)(int dir, int dx, int dy); /* 左滑=返回(左缘起点+抬手触发) */
|
||||
void (*on_tick)(void); /* 空闲周期回调(动画/时钟) */
|
||||
void (*on_swipe_progress)(int x_start, int dx);/* 左缘返回手势过程实时回调;x_start=按下X, dx=当前X-按下X */
|
||||
/* M2.71-bug2/6:拖动过程回调(跟手)。on_touch_down=按下后首个有效坐标(起点);
|
||||
* on_touch_move=拖动中坐标变化(坐标变了才回调)。用于 slider 拖动 / 列表/页面滚动跟手。
|
||||
* 均在 IME 弹出或模态阻塞时被吞(键盘/模态优先喂)。可 NULL。 */
|
||||
void (*on_touch_down)(int x, int y);
|
||||
void (*on_touch_move)(int x, int y);
|
||||
} meow_ui_events;
|
||||
|
||||
void meow_ui_run(const meow_ui_events *ev); /* 阻塞:事件循环,直到 meow_ui_request_exit */
|
||||
@@ -500,6 +505,13 @@ int meow_list_max_scroll(meow_widget *w); /* max(0, count*item -
|
||||
void meow_list_scroll_by(meow_widget *w, int dy); /* 相对滚动(App on_swipe 用) */
|
||||
int meow_list_set_item_h(meow_widget *w, int item); /* 改行高/列宽 */
|
||||
int meow_list_hit_index(meow_widget *w, int x, int y); /* 命中项下标(含滚动换算);-1=未命中项/越界 */
|
||||
/* M2.71-bug8:列表行选中 + 行点击回调。选中行已有高亮(现有绘制读 w->sel)。
|
||||
* meow_list_tap_at:命中一行则置选中并回调 tap_cb(若设),返回该行 idx;未命中返回 -1。 */
|
||||
void meow_list_set_selected(meow_widget *w, int idx); /* 选中行(越界 clamp);标脏 */
|
||||
int meow_list_selected(meow_widget *w);
|
||||
typedef void (*meow_list_tap_fn)(int idx, void *ctx);
|
||||
void meow_list_set_on_tap(meow_widget *w, meow_list_tap_fn cb, void *ctx); /* 命中行→回调 */
|
||||
int meow_list_tap_at(meow_widget *w, int x, int y); /* 命中行:置选中+回调,返回 idx/-1 */
|
||||
|
||||
/* ============================================================ */
|
||||
/* §5.8.6 模态(App 层,非系统叠加):Dialog / BottomSheet
|
||||
|
||||
@@ -20,6 +20,39 @@
|
||||
#define MEOW_MODAL_ROW 48
|
||||
#define MEOW_MODAL_BTN 44
|
||||
|
||||
/* M2.71-bug3:模态(Dialog/BottomSheet)背景快照/恢复。
|
||||
* 旧实现直接往 back_buf 画 scrim+卡片并 flush:①每次 open 再现叠一层 α150 遮罩(叠加变黑);
|
||||
* ②关闭后 scrim+卡片像素留在屏上(残影);③要整屏刷新才盖掉。此处 open 前把整帧背景拍下,
|
||||
* 关闭后回填 + 整帧上屏 → scrim 每次只叠一层、关闭即还原、无残留。 */
|
||||
static unsigned char *g_modal_cap = NULL;
|
||||
static int g_modal_cap_line = 0, g_modal_cap_h = 0, g_modal_cap_bpp = 0;
|
||||
|
||||
static void modal_capture(void)
|
||||
{
|
||||
unsigned char *bb = meow_ui_back_buf();
|
||||
int line = meow_ui_back_buf_pitch();
|
||||
int h = meow_ui_height();
|
||||
int bpp = meow_ui_back_buf_bpp();
|
||||
if (!bb || line <= 0 || h <= 0) { g_modal_cap = NULL; return; }
|
||||
size_t n = (size_t)line * (size_t)h;
|
||||
if (!g_modal_cap || g_modal_cap_line != line || g_modal_cap_h != h || g_modal_cap_bpp != bpp) {
|
||||
free(g_modal_cap);
|
||||
g_modal_cap = (unsigned char *)malloc(n);
|
||||
g_modal_cap_line = line; g_modal_cap_h = h; g_modal_cap_bpp = bpp;
|
||||
}
|
||||
if (g_modal_cap) memcpy(g_modal_cap, bb, n);
|
||||
}
|
||||
|
||||
static void modal_restore(void)
|
||||
{
|
||||
unsigned char *bb = meow_ui_back_buf();
|
||||
if (!g_modal_cap || !bb) return;
|
||||
size_t n = (size_t)g_modal_cap_line * (size_t)g_modal_cap_h;
|
||||
memcpy(bb, g_modal_cap, n);
|
||||
meow_gfx_mark_full();
|
||||
meow_ui_flush();
|
||||
}
|
||||
|
||||
/* ---- Dialog ---- */
|
||||
struct meow_dialog {
|
||||
meow_dlg_t type;
|
||||
@@ -62,58 +95,73 @@ static void dlg_calc(meow_dialog *d, const meow_theme *th, dlg_geom *g)
|
||||
g->btn_w = (g->dw - 2 * pad - 12) / 2;
|
||||
}
|
||||
|
||||
static void dlg_paint(meow_dialog *d, const meow_theme *th)
|
||||
/* 只画卡片内容(不含遮罩)——供「仅高亮变化」时重绘卡片区,不会把遮罩再叠一层(防叠加变黑)。 */
|
||||
static void dlg_card(meow_dialog *d, const meow_theme *th, const dlg_geom *g)
|
||||
{
|
||||
int W = meow_ui_width(), H = meow_ui_height();
|
||||
meow_ui_fill_rect_alpha(0, 0, W, H, 0x000000, 150); /* 遮罩压暗 */
|
||||
dlg_geom g;
|
||||
dlg_calc(d, th, &g);
|
||||
int pad = 24;
|
||||
meow_gfx_fill_round(g.dx, g.dy, g.dw, g.dh, th->radius_card, th->surfaceContainerHighest);
|
||||
meow_ui_draw_rect_round(g.dx, g.dy, g.dw, g.dh, th->radius_card, th->outline);
|
||||
meow_ui_text(g.dx + pad, g.dy + pad, d->title, th->font_title, th->onSurface);
|
||||
meow_gfx_fill_round(g->dx, g->dy, g->dw, g->dh, th->radius_card, th->surfaceContainerHighest);
|
||||
meow_ui_draw_rect_round(g->dx, g->dy, g->dw, g->dh, th->radius_card, th->outline);
|
||||
meow_ui_text(g->dx + pad, g->dy + pad, d->title, th->font_title, th->onSurface);
|
||||
|
||||
if (d->type == MEOW_DLG_SELECT) {
|
||||
for (int i = 0; i < d->nitems; i++) {
|
||||
int ry = g.row_y + i * g.rowh;
|
||||
if (ry + g.rowh > g.dy + g.dh - 12) break;
|
||||
int ry = g->row_y + i * g->rowh;
|
||||
if (ry + g->rowh > g->dy + g->dh - 12) break;
|
||||
if (i == d->cur) {
|
||||
meow_ui_fill_rect(g.dx + 8, ry, g.dw - 16, g.rowh, th->primary);
|
||||
meow_ui_text(g.dx + pad, ry + (g.rowh - meow_ui_text_height(th->font_body)) / 2,
|
||||
meow_ui_fill_rect(g->dx + 8, ry, g->dw - 16, g->rowh, th->primary);
|
||||
meow_ui_text(g->dx + pad, ry + (g->rowh - meow_ui_text_height(th->font_body)) / 2,
|
||||
d->items[i], th->font_body, 0xFFFFFF);
|
||||
} else {
|
||||
meow_ui_text(g.dx + pad, ry + (g.rowh - meow_ui_text_height(th->font_body)) / 2,
|
||||
meow_ui_text(g->dx + pad, ry + (g->rowh - meow_ui_text_height(th->font_body)) / 2,
|
||||
d->items[i], th->font_body, th->onSurface);
|
||||
}
|
||||
meow_ui_fill_rect(g.dx + pad, ry + g.rowh - 1, g.dw - 2 * pad, 1, th->outlineVariant);
|
||||
meow_ui_fill_rect(g->dx + pad, ry + g->rowh - 1, g->dw - 2 * pad, 1, th->outlineVariant);
|
||||
}
|
||||
} else {
|
||||
/* INFO/CONFIRM:正文行;INPUT:文本预览 */
|
||||
int nmsg = (d->type == MEOW_DLG_INFO) ? (d->nitems > 0 ? d->nitems : 1)
|
||||
: (d->nitems > 0 ? d->nitems : 1);
|
||||
int y = g.row_y;
|
||||
int y = g->row_y;
|
||||
if (d->type == MEOW_DLG_INPUT) {
|
||||
char buf[300];
|
||||
snprintf(buf, sizeof buf, "%s", d->ime.buf[0] ? d->ime.buf : "(tap OK to edit...)");
|
||||
meow_ui_text(g.dx + pad, y, buf, th->font_body, th->onSurfaceVariant);
|
||||
meow_ui_text(g->dx + pad, y, buf, th->font_body, th->onSurfaceVariant);
|
||||
} else {
|
||||
for (int i = 0; i < nmsg; i++) {
|
||||
if (i < d->nitems && d->items[i][0])
|
||||
meow_ui_text(g.dx + pad, y, d->items[i], th->font_body, th->onSurfaceVariant);
|
||||
meow_ui_text(g->dx + pad, y, d->items[i], th->font_body, th->onSurfaceVariant);
|
||||
y += meow_ui_text_height(th->font_body) + 8;
|
||||
}
|
||||
}
|
||||
/* 取消(左,text 型)/ 确定(右,filled) */
|
||||
int bx = g.dx + pad, cx = bx + g.btn_w + 12, by = g.btn_y;
|
||||
meow_ui_draw_rect_round(bx, by, g.btn_w, g.btn_h, 10, th->primary);
|
||||
meow_ui_text_center(bx + g.btn_w / 2, by + (g.btn_h - meow_ui_text_height(th->font_body)) / 2,
|
||||
int bx = g->dx + pad, cx = bx + g->btn_w + 12, by = g->btn_y;
|
||||
meow_ui_draw_rect_round(bx, by, g->btn_w, g->btn_h, 10, th->primary);
|
||||
meow_ui_text_center(bx + g->btn_w / 2, by + (g->btn_h - meow_ui_text_height(th->font_body)) / 2,
|
||||
"CANCEL", th->font_body, th->primary);
|
||||
meow_gfx_fill_round(cx, by, g.btn_w, g.btn_h, 10, th->primary);
|
||||
meow_ui_text_center(cx + g.btn_w / 2, by + (g.btn_h - meow_ui_text_height(th->font_body)) / 2,
|
||||
meow_gfx_fill_round(cx, by, g->btn_w, g->btn_h, 10, th->primary);
|
||||
meow_ui_text_center(cx + g->btn_w / 2, by + (g->btn_h - meow_ui_text_height(th->font_body)) / 2,
|
||||
"OK", th->font_body, th->onPrimary);
|
||||
}
|
||||
}
|
||||
|
||||
/* 整帧绘制:遮罩 + 卡片。 */
|
||||
static void dlg_paint(meow_dialog *d, const meow_theme *th)
|
||||
{
|
||||
meow_ui_fill_rect_alpha(0, 0, meow_ui_width(), meow_ui_height(), 0x000000, 150); /* 遮罩压暗 */
|
||||
dlg_geom g;
|
||||
dlg_calc(d, th, &g);
|
||||
dlg_card(d, th, &g);
|
||||
}
|
||||
|
||||
/* 仅重绘卡片区(遮罩不变),flush 卡片矩形:高亮变化用,不叠加遮罩。 */
|
||||
static void dlg_repaint(meow_dialog *d, const meow_theme *th)
|
||||
{
|
||||
dlg_geom g;
|
||||
dlg_calc(d, th, &g);
|
||||
dlg_card(d, th, &g);
|
||||
meow_ui_flush_region(g.dx, g.dy, g.dw, g.dh);
|
||||
}
|
||||
|
||||
meow_dialog *meow_dialog_create(meow_dlg_t t, const char *title)
|
||||
{
|
||||
meow_dialog *d = (meow_dialog *)calloc(1, sizeof *d);
|
||||
@@ -155,12 +203,14 @@ void meow_dialog_show(meow_dialog *d)
|
||||
meow_input_start();
|
||||
meow_input_drain(); /* 排空残留触摸(goodix 长尾) */
|
||||
d->done = 0; d->result = 0;
|
||||
modal_capture(); /* M2.71-bug3:拍下背景,关闭即还原(scrim 只叠一层/无残影) */
|
||||
dlg_paint(d, th);
|
||||
meow_ui_flush();
|
||||
|
||||
while (!d->done) {
|
||||
meow_input_ev e;
|
||||
while (meow_input_pop(&e)) {
|
||||
meow_ui_activity_ping(); /* M2.71-bug5:模态期间交互也重置息屏计时 */
|
||||
if (e.type == MEOW_EV_TAP) {
|
||||
int x = e.a, y = e.b;
|
||||
dlg_geom g;
|
||||
@@ -168,8 +218,13 @@ void meow_dialog_show(meow_dialog *d)
|
||||
int in_card = x >= g.dx && x < g.dx + g.dw && y >= g.dy && y < g.dy + g.dh;
|
||||
if (!in_card) { d->done = 1; d->result = 0; break; } /* 遮罩外=取消 */
|
||||
if (d->type == MEOW_DLG_SELECT) {
|
||||
/* M2.71-bug3:点项 → 立即刷「选中」高亮(不下线、不叠加遮罩);
|
||||
* 再点同一项 → 确认(返回该项下标)。点卡片外=取消。 */
|
||||
int idx = (y - g.row_y) / g.rowh;
|
||||
if (idx >= 0 && idx < d->nitems) { d->cur = idx; d->result = idx; d->done = 1; }
|
||||
if (idx >= 0 && idx < d->nitems) {
|
||||
if (idx == d->cur) { d->result = idx; d->done = 1; }
|
||||
else { d->cur = idx; dlg_repaint(d, th); }
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* 非选择:按钮命中 */
|
||||
@@ -195,6 +250,7 @@ void meow_dialog_show(meow_dialog *d)
|
||||
if (d->done) break;
|
||||
usleep(16000);
|
||||
}
|
||||
modal_restore(); /* M2.71-bug3:关闭还原背景,无残影 */
|
||||
}
|
||||
|
||||
int meow_dialog_result(meow_dialog *d) { return d ? d->result : 0; }
|
||||
@@ -257,6 +313,7 @@ int meow_sheet_open(meow_sheet *s)
|
||||
int W = meow_ui_width(), H = meow_ui_height();
|
||||
int ph = sheet_h(s, th);
|
||||
int py = H - ph, pad = 24;
|
||||
modal_capture(); /* M2.71-bug3:拍下背景,关闭即还原 */
|
||||
/* 首次绘制:遮罩 + 底部面板(顶部圆角) */
|
||||
meow_ui_fill_rect_alpha(0, 0, W, H, 0x000000, 150);
|
||||
meow_gfx_fill_round(0, py, W, ph, th->radius_card, th->surfaceContainerHighest);
|
||||
@@ -273,6 +330,7 @@ int meow_sheet_open(meow_sheet *s)
|
||||
while (!s->done) {
|
||||
meow_input_ev e;
|
||||
while (meow_input_pop(&e)) {
|
||||
meow_ui_activity_ping(); /* M2.71-bug5:模态期间交互也重置息屏计时 */
|
||||
if (e.type == MEOW_EV_TAP) {
|
||||
int y = e.b;
|
||||
if (y < py) { s->done = 1; s->result = -1; break; } /* 点面板外=取消 */
|
||||
@@ -288,6 +346,7 @@ int meow_sheet_open(meow_sheet *s)
|
||||
if (s->done) break;
|
||||
usleep(16000);
|
||||
}
|
||||
modal_restore(); /* M2.71-bug3:关闭还原背景,无残影 */
|
||||
return s->result;
|
||||
}
|
||||
void meow_sheet_free(meow_sheet *s) { if (s) free(s); }
|
||||
@@ -179,6 +179,10 @@ void meow_gfx_mark_dirty(int x, int y, int w, int h) { dirty_add(x, y, w, h); }
|
||||
void meow_gfx_mark_full(void) { dirty_full(); }
|
||||
void meow_gfx_dirty_reset(void) { dirty_reset(); }
|
||||
|
||||
/* back_buf 每行字节数 / 位深(M2.71-bug3:模态/IME 整帧快照恢复用)。 */
|
||||
int meow_ui_back_buf_pitch(void) { return g_line; }
|
||||
int meow_ui_back_buf_bpp(void) { return g_bpp; }
|
||||
|
||||
/* launcher(系统组件)与 SDK 共用同一 back_buf:launcher 的 fb 绘制指针指向它,
|
||||
* 从而 launcher 与 app 统一渲染栈(同一离屏 + 同一 present)。 */
|
||||
unsigned char *meow_ui_back_buf(void) { return g_back_buf; }
|
||||
@@ -393,6 +397,23 @@ void meow_ui_draw_rect_round(int x, int y, int w, int h, int r, uint32_t color)
|
||||
dirty_add(x1, y1, x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
/* 圆角矩形半透明填充(M2.71-bug4):按圆角裁剪后逐像素混合 α。供「禁用变暗」等状态层用,
|
||||
* 避免在圆角外的方形区域也叠色(旧 fill_rect_alpha 会在圆角外露出灰方块)。 */
|
||||
void meow_gfx_fill_round_alpha(int x, int y, int w, int h, int r, uint32_t color, int alpha)
|
||||
{
|
||||
if (!g_init_ok) return;
|
||||
if (alpha <= 0) return;
|
||||
if (alpha >= 255) { meow_gfx_fill_round(x, y, w, h, r, color); return; }
|
||||
if (w <= 0 || h <= 0) return;
|
||||
int x1 = x, y1 = y, x2 = x + w, y2 = y + h;
|
||||
if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0;
|
||||
if (x2 > g_xres) x2 = g_xres; if (y2 > g_yres) y2 = g_yres;
|
||||
for (int py = y1; py < y2; py++)
|
||||
for (int px = x1; px < x2; px++)
|
||||
if (rr_inside(px, py, x, y, w, h, r)) put_px_blend(px, py, color, alpha);
|
||||
dirty_add(x1, y1, x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
/* ================= §5.8.6 batch2:圆形/线段/圆弧环(软渲,无 -lm) =================
|
||||
* 无 GPU 合成、构建只链 libz+pthread(无 -lm),故不用 math.h:COS 用象限对称查表、
|
||||
* 圆用 bbox 距离测试、弧带用「沿弧采样成粗线(圆帽)」近似。 */
|
||||
@@ -462,7 +483,9 @@ void meow_gfx_ring_arc(int cx, int cy, int r, int th, int start_deg, int sweep_d
|
||||
if (!g_init_ok || r <= 0) return;
|
||||
int sw = sweep_deg % 360;
|
||||
if (sw < 0) sw += 360;
|
||||
if (sw >= 360) { /* 整环:bbox 距离带(无角度判定,最快) */
|
||||
/* 整环(|sweep|>=360):bbox 距离带(无角度判定,最快)。注意 sweep=360 → sw=0,
|
||||
* 若靠 sw>=360 判整环会落下走弧分支只画一个点(顺带让 Radio 外圈/圆形进度轨道环渲染成点)。 */
|
||||
if (sweep_deg >= 360 || sweep_deg <= -360) {
|
||||
int r1 = r - th / 2, r2 = r + th / 2;
|
||||
if (r1 < 0) r1 = 0;
|
||||
int x1 = cx - r2, y1 = cy - r2, x2 = cx + r2 + 1, y2 = cy + r2 + 1;
|
||||
@@ -478,6 +501,7 @@ void meow_gfx_ring_arc(int cx, int cy, int r, int th, int start_deg, int sweep_d
|
||||
dirty_add(x1, y1, x2 - x1, y2 - y1);
|
||||
return;
|
||||
}
|
||||
if (sw == 0) return; /* 零弧长:不画(sweep=0;整环已在上面处理) */
|
||||
/* 弧带:沿弧采样相邻点、粗线连接(圆帽)近似。方向:deg 右(0)→上(90)→左(180)→下(270),顺时针。 */
|
||||
int n = sw / 2 + 1;
|
||||
if (n < 2) n = 2;
|
||||
|
||||
+50
-15
@@ -22,6 +22,36 @@
|
||||
#define GAP 4 /* 键间距 */
|
||||
#define GCOL 48 /* 列步进 = GW+GAP */
|
||||
|
||||
/* M2.71-bug7:键盘区背景快照/恢复。旧实现关闭后键盘像素留在屏底(残影)。此处打开前拍下
|
||||
* 键盘区 [KB_TOP,H),关闭后回填 + 上屏 → 无残影。 */
|
||||
static unsigned char *g_ime_cap = NULL;
|
||||
static int g_ime_cap_line = 0, g_ime_cap_h = 0;
|
||||
|
||||
static void ime_capture(void)
|
||||
{
|
||||
unsigned char *bb = meow_ui_back_buf();
|
||||
int line = meow_ui_back_buf_pitch();
|
||||
int h = meow_ui_height() - KB_TOP;
|
||||
if (!bb || line <= 0 || h <= 0) { g_ime_cap = NULL; return; }
|
||||
size_t n = (size_t)line * (size_t)h;
|
||||
if (!g_ime_cap || g_ime_cap_line != line || g_ime_cap_h != h) {
|
||||
free(g_ime_cap);
|
||||
g_ime_cap = (unsigned char *)malloc(n);
|
||||
g_ime_cap_line = line; g_ime_cap_h = h;
|
||||
}
|
||||
if (g_ime_cap) memcpy(g_ime_cap, bb + (size_t)line * KB_TOP, n);
|
||||
}
|
||||
|
||||
static void ime_restore(void)
|
||||
{
|
||||
unsigned char *bb = meow_ui_back_buf();
|
||||
if (!g_ime_cap || !bb) return;
|
||||
size_t n = (size_t)g_ime_cap_line * (size_t)g_ime_cap_h;
|
||||
memcpy(bb + (size_t)g_ime_cap_line * KB_TOP, g_ime_cap, n);
|
||||
meow_gfx_mark_dirty(0, KB_TOP, meow_ui_width(), g_ime_cap_h);
|
||||
meow_ui_flush();
|
||||
}
|
||||
|
||||
typedef enum { K_CH, K_SHIFT, K_SPACE, K_BACK, K_ENTER, K_ESC } kkind;
|
||||
|
||||
struct kbkey { int x, y, w, h; kkind kind; char ch; };
|
||||
@@ -91,18 +121,18 @@ static void build_kb(meow_ime_mode mode)
|
||||
add_key(x + GCOL, y + GCOL * 4, (GW + GAP) * 6, GH, K_SPACE, ' ');
|
||||
add_key(x + GCOL * 8, y + GCOL * 4, 2 * GW + GAP, GH, K_ENTER, '>');
|
||||
}
|
||||
} else { /* MEOW_IME_NUM */
|
||||
} else { /* MEOW_IME_NUM:ESC(取消/关闭) + 空格 + 退格;单独一行回车(OK=提交并关闭)。
|
||||
* M2.71-bug7:修掉旧版「同格重叠的 BACK+ENTER」与重复回车。 */
|
||||
add_ch_row(y, "1234567890");
|
||||
add_ch_row(y + GCOL, "-/.:*#+=");
|
||||
{
|
||||
int total = 10 * GW + 9 * GAP;
|
||||
int x = (480 - total) / 2;
|
||||
add_key(x, y + GCOL * 2, GW, GH, K_ESC, 'X');
|
||||
add_key(x + GCOL, y + GCOL * 2, (GW + GAP) * 7, GH, K_SPACE, ' ');
|
||||
add_key(x + GCOL * 9, y + GCOL * 2, GW, GH, K_BACK, '<');
|
||||
add_key(x + GCOL * 9, y + GCOL * 2, GW, GH, K_ENTER, '>'); /* 覆盖:回车 */
|
||||
add_key(x, y + GCOL * 2, GW, GH, K_ESC, 'X'); /* 取消/关闭(丢弃) */
|
||||
add_key(x + GCOL, y + GCOL * 2, (GW + GAP) * 7, GH, K_SPACE, ' '); /* 空格 */
|
||||
add_key(x + GCOL * 8, y + GCOL * 2, (GW + GAP) * 2 - GAP, GH, K_BACK, '<'); /* 退格 */
|
||||
}
|
||||
/* 单独一行回车 */
|
||||
/* 回车(OK=提交并关闭) */
|
||||
{
|
||||
int total = 10 * GW + 9 * GAP;
|
||||
int x = (480 - total) / 2;
|
||||
@@ -149,25 +179,27 @@ void meow_ime_draw(void)
|
||||
{
|
||||
if (!g_ime_active) return;
|
||||
const meow_theme *th = meow_theme_get();
|
||||
/* 面板 */
|
||||
meow_ui_fill_rect(0, KB_TOP, 480, 720 - KB_TOP, 0x1C1C22);
|
||||
/* 预览行 */
|
||||
meow_ui_fill_rect(0, KB_TOP, 480, 42, 0x2A2A33);
|
||||
char preview[256];
|
||||
/* M2.71-bug7:配色全走 meow_theme(深/浅主题自动切),不再硬编码黑底白键。 */
|
||||
meow_ui_fill_rect(0, KB_TOP, 480, 720 - KB_TOP, th->surfaceContainerHighest); /* 面板 */
|
||||
meow_ui_fill_rect(0, KB_TOP, 480, 42, th->surfaceContainerHigh); /* 回显条 */
|
||||
char preview[260]; /* g_ime_buf(≤255) + '_' + NUL → 需 ≥257,防截断告警 */
|
||||
/* 回显=用户已输入的字符(live echo,非候选/联想),随键入实时刷新。 */
|
||||
snprintf(preview, sizeof preview, "%s_", g_ime_buf);
|
||||
meow_ui_text(14, KB_TOP + 9, preview, th->font_body, 0xFFFFFF);
|
||||
meow_ui_text(14, KB_TOP + 9, preview, th->font_body, th->onSurface);
|
||||
/* 键 */
|
||||
for (int i = 0; i < g_nkeys; i++) {
|
||||
const struct kbkey *k = &g_keys[i];
|
||||
int is_fn = (k->kind != K_CH);
|
||||
uint32_t bg = is_fn ? 0x33333C : 0xFFFFFF;
|
||||
uint32_t bg, fg;
|
||||
if (k->kind == K_ENTER) { bg = th->primary; fg = th->onPrimary; } /* OK=提交 */
|
||||
else if (k->kind == K_CH) { bg = th->surfaceContainerLow; fg = th->onSurface; } /* 字符键 */
|
||||
else { bg = th->primaryContainer; fg = th->onPrimaryContainer; } /* 功能键(ESC/SHIFT/空格/退格) */
|
||||
meow_gfx_fill_round(k->x, k->y, k->w, k->h, 8, bg);
|
||||
char label[2] = { k->ch, 0 };
|
||||
if (is_letter(k->ch)) label[0] = g_shift ? (char)toupper((unsigned char)k->ch)
|
||||
: (char)tolower((unsigned char)k->ch);
|
||||
uint32_t fg = is_fn ? 0xFFFFFF : 0x1A1A1E;
|
||||
if (k->kind == K_SPACE) { meow_ui_text_center(k->x + k->w / 2, k->y + (k->h - meow_ui_text_height(20)) / 2, "SPACE", th->font_body, fg); }
|
||||
else if (k->kind == K_ENTER) { meow_ui_text_center(k->x + k->w / 2, k->y + (k->h - meow_ui_text_height(20)) / 2, "OK", 20, fg); }
|
||||
else if (k->kind == K_ESC) { meow_ui_text_center(k->x + k->w / 2, k->y + (k->h - meow_ui_text_height(20)) / 2, "X", 20, fg); }
|
||||
else { meow_ui_text_center(k->x + k->w / 2, k->y + (k->h - meow_ui_text_height(20)) / 2, label, 20, fg); }
|
||||
}
|
||||
}
|
||||
@@ -199,6 +231,7 @@ void meow_ime_close(int result)
|
||||
g_ime_orig->buf[sizeof g_ime_orig->buf - 1] = 0;
|
||||
}
|
||||
g_ime_active = 0; g_ime_done = 0;
|
||||
ime_restore(); /* M2.71-bug7:关闭还原键盘区背景,无残影 */
|
||||
}
|
||||
|
||||
void meow_ui_text_input_async(meow_ime_ctx *ctx)
|
||||
@@ -211,6 +244,7 @@ void meow_ui_text_input_async(meow_ime_ctx *ctx)
|
||||
g_maxlen = ctx->maxlen > 0 ? ctx->maxlen : 255;
|
||||
g_shift = 0; g_ime_result = 0; g_ime_done = 0; g_ime_active = 1;
|
||||
build_kb(ctx->mode);
|
||||
ime_capture(); /* M2.71-bug7:打开前拍下键盘区背景(关闭即还原) */
|
||||
meow_input_start();
|
||||
meow_input_drain();
|
||||
}
|
||||
@@ -223,6 +257,7 @@ int meow_ui_text_input(meow_ime_ctx *ctx)
|
||||
while (meow_ime_active()) {
|
||||
meow_input_ev e;
|
||||
while (meow_input_pop(&e)) {
|
||||
meow_ui_activity_ping(); /* M2.71-bug5:IME 打字交互也重置息屏计时 */
|
||||
if (e.type == MEOW_EV_TAP) meow_ime_handle_tap(e.a, e.b);
|
||||
}
|
||||
meow_ime_draw();
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#define EDGE_W 24 /* 左滑返回的「左缘起点」宽度(t_dx < EDGE_W 才认返回手势) */
|
||||
#define QUEUE_N 128
|
||||
#define MAX_DEVS 8
|
||||
#define MEOW_APP_ACTIVITY_FILE "/tmp/meow_app_activity" /* M2.71-bug5:App 活动→launcher 重置息屏计时 */
|
||||
|
||||
/* ── 内部事件:系统级「下拉展开通知中心」(M2.5x 接线)──
|
||||
* 触摸状态机在抬手判定处识别「SWIPE_DOWN 起始于顶部热区」后压入;事件循环按系统手势处理
|
||||
@@ -126,6 +127,8 @@ static int t_dx = 0, t_dy = 0; /* 按下原点(BTN_TOUCH 后首个有效 A
|
||||
static int t_cx = 0, t_cy = 0; /* 当前坐标(-1=本次尚未见到有效坐标) */
|
||||
static int t_origin_valid = 0; /* 原点是否已锁定 */
|
||||
static uint64_t t_down_ms = 0; /* 按下时刻(monotonic ms),供实时按住时长用 */
|
||||
/* M2.71-bug2/6:拖动过程事件上次上报坐标(坐标变化才入队 MEOW_EV_TOUCH_MOVE,防刷屏)。 */
|
||||
static int t_mv_x = -1, t_mv_y = -1;
|
||||
|
||||
/* 按下边沿:只标记按下,绝不用传入坐标立即锁原点(避免用上一手势残留坐标当原点)。
|
||||
* t_cx/t_cy 归 -1 —— 本次手势以「无坐标」起步,只接受随后到来的有效 ABS。 */
|
||||
@@ -138,22 +141,32 @@ static void touch_press(int x, int y)
|
||||
t_cx = -1; t_cy = -1;
|
||||
t_dx = 0; t_dy = 0;
|
||||
t_down_ms = now_ms(); /* 记按下时刻:供实时按住时长/按住视觉 */
|
||||
t_mv_x = -1; t_mv_y = -1; /* M2.71:拖动过程起点未定 */
|
||||
}
|
||||
/* 由 ABS_X/ABS_Y 驱动:若本次按下且原点未锁且坐标有效 → 锁原点;再更新当前坐标。 */
|
||||
/* 由 ABS_X/ABS_Y 驱动:若本次按下且原点未锁且坐标有效 → 锁原点;再更新当前坐标。
|
||||
* M2.71-bug2/6:原点锁定即报 MEOW_EV_TOUCH_DOWN(拖动起点);随后坐标变化报 MEOW_EV_TOUCH_MOVE
|
||||
* (坐标变了才报,省队列)。这两类事件供 slider 拖动 / 列表&页面滚动跟手。 */
|
||||
static void touch_update(int x, int y)
|
||||
{
|
||||
if (t_down && !t_origin_valid && x >= 0 && y >= 0) {
|
||||
t_dx = x; t_dy = y; t_origin_valid = 1;
|
||||
t_mv_x = x; t_mv_y = y;
|
||||
push_ev(MEOW_EV_TOUCH_DOWN, x, y);
|
||||
}
|
||||
if (x >= 0) t_cx = x;
|
||||
if (y >= 0) t_cy = y;
|
||||
if (t_down && t_origin_valid && t_cx >= 0 && t_cy >= 0 &&
|
||||
(t_cx != t_mv_x || t_cy != t_mv_y)) {
|
||||
t_mv_x = t_cx; t_mv_y = t_cy;
|
||||
push_ev(MEOW_EV_TOUCH_MOVE, t_cx, t_cy);
|
||||
}
|
||||
}
|
||||
static void touch_release(void)
|
||||
{
|
||||
if (!t_down) return;
|
||||
t_down = 0; t_press_valid = 0;
|
||||
/* 本次按下从未锁到有效坐标(幽灵触摸/坐标未就绪):丢弃,且清空全部轨迹。 */
|
||||
if (!t_origin_valid) { t_cx = t_cy = -1; t_dx = t_dy = 0; t_down_ms = 0; return; }
|
||||
if (!t_origin_valid) { t_cx = t_cy = -1; t_dx = t_dy = 0; t_down_ms = 0; t_mv_x = t_mv_y = -1; return; }
|
||||
t_origin_valid = 0;
|
||||
int mdx = t_cx - t_dx, mdy = t_cy - t_dy;
|
||||
int adx = mdx < 0 ? -mdx : mdx, ady = mdy < 0 ? -mdy : mdy;
|
||||
@@ -194,7 +207,7 @@ static void touch_release(void)
|
||||
push_ev(MEOW_EV_TAP, t_cx, t_cy);
|
||||
}
|
||||
/* 手势级干净起点:清空全部轨迹,下一手势从零开始,杜绝污染无自愈。 */
|
||||
t_cx = t_cy = -1; t_dx = t_dy = 0; t_down_ms = 0;
|
||||
t_cx = t_cy = -1; t_dx = t_dy = 0; t_down_ms = 0; t_mv_x = t_mv_y = -1;
|
||||
}
|
||||
|
||||
/* ---- 实时触摸态(内部:控件绘制按压/按住视觉用)----
|
||||
@@ -221,6 +234,7 @@ void meow_input_touch_set(int down, int x, int y, unsigned hold_ms)
|
||||
} else {
|
||||
t_down = 0; t_press_valid = 0; t_origin_valid = 0;
|
||||
t_cx = -1; t_cy = -1; t_dx = 0; t_dy = 0; t_down_ms = 0;
|
||||
t_mv_x = -1; t_mv_y = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,6 +315,19 @@ void meow_input_start(void)
|
||||
/* ---- 事件循环 ---- */
|
||||
void meow_ui_request_exit(void) { g_exit = 1; }
|
||||
|
||||
/* M2.71-bug5:App 活动信号(节流 ≤2/s 写标记文件,限 I/O)。
|
||||
* 仅真机写(宿主/qemu 无 launcher 消费,避免噪音);launcher 前台 app 循环消费它重置息屏计时。 */
|
||||
static uint64_t g_activity_last_ms = 0;
|
||||
void meow_ui_activity_ping(void)
|
||||
{
|
||||
uint64_t now = now_ms();
|
||||
if (now - g_activity_last_ms < 500) return; /* 节流:任意交互连续触摸也只 ~2 次/s */
|
||||
g_activity_last_ms = now;
|
||||
if (!meow_gfx_is_device()) return; /* 离屏/宿主:无 launcher 消费 */
|
||||
int fd = open(MEOW_APP_ACTIVITY_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (fd >= 0) { if (write(fd, "1", 1) < 0) { } close(fd); }
|
||||
}
|
||||
|
||||
void meow_ui_run(const meow_ui_events *ev)
|
||||
{
|
||||
if (ev) { g_events = *ev; g_events_set = 1; }
|
||||
@@ -319,6 +346,7 @@ void meow_ui_run(const meow_ui_events *ev)
|
||||
while (meow_input_pop(&e)) {
|
||||
if (g_exit) break; /* 已请求退出:不再分发剩余事件,杜绝「退出后又触发」的偶发再进/退 */
|
||||
got = 1;
|
||||
meow_ui_activity_ping(); /* M2.71-bug5:任意交互事件都喂 launcher 重置息屏计时(节流) */
|
||||
int in_startup = (now_ms() < startup_cooldown_until); /* M2.22(C) */
|
||||
if (e.type == MEOW_EV_TAP) {
|
||||
if (in_startup) continue; /* 启动窗口也吞 TAP:旧手势残留点按(如上一次左滑抬起)
|
||||
@@ -354,6 +382,17 @@ void meow_ui_run(const meow_ui_events *ev)
|
||||
/* 系统级「下拉展开通知中心」:起始于顶部热区的下滑为系统保留手势,不下发
|
||||
* APP on_swipe;路由到系统钩子(当前为记录占位,通知中心 UI 为后续里程碑)。 */
|
||||
meow_ui_sys_notif_pull();
|
||||
} else if (e.type == MEOW_EV_TOUCH_DOWN || e.type == MEOW_EV_TOUCH_MOVE) {
|
||||
/* M2.71-bug2/6:拖动过程(跟手)。IME 弹出时触摸优先喂键盘(键盘覆盖层拦截,
|
||||
* on_touch_* 丢弃,避免按到键盘却在背后控件上锁拖拽);启动窗口也吞(防残留点按
|
||||
* 误锁拖拽)。模态(dialog/sheet)期间本主循环被阻塞,天然不在此分发。 */
|
||||
if (in_startup) continue;
|
||||
if (meow_ime_active()) continue;
|
||||
if (g_events_set && g_events.on_touch_down && e.type == MEOW_EV_TOUCH_DOWN)
|
||||
g_events.on_touch_down(e.a, e.b);
|
||||
if (g_events_set && g_events.on_touch_move && e.type == MEOW_EV_TOUCH_MOVE)
|
||||
g_events.on_touch_move(e.a, e.b);
|
||||
got = 1; /* 交互事件:不睡眠,保持跟手帧同步 */
|
||||
} else if (e.type == MEOW_EV_SWIPE_PROGRESS) {
|
||||
if (in_startup) continue; /* 启动窗口进度条静音 */
|
||||
int x0 = e.a, xc = e.b, dx = xc - x0; /* dx=当前X-按下X */
|
||||
|
||||
@@ -108,9 +108,14 @@ void meow_gfx_dirty_reset(void);
|
||||
|
||||
/* 返回离屏 back_buf 指针(launcher 系统组件与 SDK 共用同一 back_buf 用)。 */
|
||||
unsigned char *meow_ui_back_buf(void);
|
||||
/* back_buf 的每行字节数(pitch)与位深(16/32)。M2.71-bug3:模态/IME 快照恢复用。 */
|
||||
int meow_ui_back_buf_pitch(void);
|
||||
int meow_ui_back_buf_bpp(void);
|
||||
|
||||
/* 实心圆角矩形(控件按钮底色用;公共 §3 只有空心 draw_rect_round)。[D3] */
|
||||
void meow_gfx_fill_round(int x, int y, int w, int h, int r, uint32_t color);
|
||||
/* 圆角矩形半透明填充(M2.71-bug4):禁用变暗等状态层用,圆角外不叠色(防灰方块)。 */
|
||||
void meow_gfx_fill_round_alpha(int x, int y, int w, int h, int r, uint32_t color, int alpha);
|
||||
|
||||
/* §5.8.6 batch2:软渲圆形/线段/圆弧环原语(无 GPU、无 -lm,内部整数/查表实现)。
|
||||
* 供 Radio(外圈/内点)、Badge(红点)、ProgressBar(环形轨道/进度弧)、Checkbox(勾线段)使用。 */
|
||||
@@ -131,6 +136,8 @@ int meow_gfx_mfont_ready(void);
|
||||
#define MEOW_EV_TAP 2 /* a=x, b=y(触摸抬起落点) */
|
||||
#define MEOW_EV_SWIPE 3 /* a=MEOW_SWIPE_*, b=距离(px) */
|
||||
#define MEOW_EV_SWIPE_PROGRESS 4 /* a=按下X(t_dx), b=当前X(t_cx);dx=b-a(>0=左缘向内划=返回手势) */
|
||||
#define MEOW_EV_TOUCH_DOWN 6 /* a=x, b=y:按下后首个有效坐标(拖动起点,M2.71-bug2/6) */
|
||||
#define MEOW_EV_TOUCH_MOVE 7 /* a=x, b=y:拖动过程坐标变化(坐标变才入队;跟手用) */
|
||||
|
||||
typedef struct meow_input_ev {
|
||||
uint8_t type;
|
||||
@@ -146,6 +153,12 @@ void meow_input_inject(int type, int a, int b);
|
||||
/* 清空队列(IME 打开前丢弃残留触摸,避免误敲)。 */
|
||||
void meow_input_drain(void);
|
||||
|
||||
/* ---- M2.71-bug5:App 活动 → 通知 launcher 重置息屏计时 ----
|
||||
* launcher 前台 app 会话的 app_last_input 只在物理键边沿重置(它看不到应用内触摸)。SDK 在
|
||||
* 收到任意交互事件(tap/swipe/拖动/IME/模态)时节流 touch 出 /tmp/meow_app_activity 标记;
|
||||
* launcher app 循环探测到即 unlink + 重置计时(若已自动息屏则照 key-wake 路径唤醒)。 */
|
||||
void meow_ui_activity_ping(void);
|
||||
|
||||
/* ---- 实时触摸态(供控件绘制按压/按住视觉;输入线程写、app 主线读)----
|
||||
* 与语义事件队列无关:这些直接读输入线程的触摸状态机当前值,让 meow_widget_draw
|
||||
* 在「手指按下期间」能如实反映按钮的按下/按住视觉,而无需 app 自行追踪按下。 */
|
||||
|
||||
@@ -26,6 +26,11 @@ enum { W_BTN = 0, W_SLIDER = 1, W_SWITCH = 2, W_LIST = 3,
|
||||
* 视觉区分「短按按下」与「按住」;触发语义不变(on_tap 仍按抬手判定)。 */
|
||||
#define MEOW_BTN_HOLD_MS 500u
|
||||
|
||||
/* 圆形 ProgressBar 环带厚 / 外扩边距(bgg 修复 M2.71-bug1:控件几何必须覆盖真实像素)。
|
||||
* 实际像素范围 = 中心 ± (r + thk/2);margin = thk/2 + 1 保证外接框覆盖环带外缘与圆帽。 */
|
||||
#define MEOW_PROGRESS_ARC_THK 4
|
||||
#define MEOW_PROGRESS_ARC_MARGIN (MEOW_PROGRESS_ARC_THK / 2 + 1)
|
||||
|
||||
/* MD3 状态层混色:c 为基色、o 为状态色、t 为 o 的占比(0-100)。
|
||||
* 用于把主题 accent 向黑/白调出「按下/按住」状态层(日/夜主题各配色)。 */
|
||||
static uint32_t blend_color(uint32_t c, uint32_t o, int t)
|
||||
@@ -73,6 +78,9 @@ struct meow_widget {
|
||||
char items[MEOW_CONTAINER_MAX][24]; /* W_TOPAPPBAR 动作 / W_NAVBAR 项 / W_TABBAR 页签 */
|
||||
int nitems; /* 上述项数 */
|
||||
int selected; /* W_NAVBAR / W_TABBAR 选中下标 */
|
||||
/* M2.71-bug8:W_LIST 行点击回调(可选)。 */
|
||||
meow_list_tap_fn tap_cb; /* 命中行回调(可 NULL) */
|
||||
void *tap_ctx; /* 回调 ctx */
|
||||
};
|
||||
|
||||
/* ---- §5.8 batch1 内部辅助:opts 取值(opts_used==0 时按控件内建默认)----
|
||||
@@ -584,7 +592,13 @@ meow_widget *meow_progress_circular_create(int cx, int cy, int r, int value)
|
||||
{
|
||||
meow_widget *t = (meow_widget *)calloc(1, sizeof *t);
|
||||
if (!t) return NULL;
|
||||
t->type = W_PROGRESS; t->x = cx; t->y = cy; t->w = r; t->h = r;
|
||||
/* bbox = 整圆像素外接框(中心 ± (r + margin)):使 w->x/y/w/h 即真实绘制范围,
|
||||
* 于是 meow_progress_set 的 mark_dirty 与 App 的 flush_region(w->x,w->y,w->w,w->h)
|
||||
* 都天然对齐,不漏刷/不多刷(修复 bgg M2.71 圆形进度脏区)。 */
|
||||
int m = MEOW_PROGRESS_ARC_MARGIN;
|
||||
t->type = W_PROGRESS;
|
||||
t->x = cx - r - m; t->y = cy - r - m;
|
||||
t->w = 2 * (r + m); t->h = 2 * (r + m);
|
||||
t->owner = meow_ui_activity_active();
|
||||
t->state = MEOW_WS_ENABLED;
|
||||
t->variant = 1; /* 圆形 */
|
||||
@@ -791,6 +805,34 @@ int meow_list_hit_index(meow_widget *w, int x, int y)
|
||||
return (idx >= 0 && idx < w->count) ? idx : -1;
|
||||
}
|
||||
|
||||
/* M2.71-bug8:选中行(越界 clamp)+ 标脏(高亮刷新)。 */
|
||||
void meow_list_set_selected(meow_widget *w, int idx)
|
||||
{
|
||||
if (!w || w->type != W_LIST) return;
|
||||
if (idx < 0) idx = 0;
|
||||
if (w->count > 0 && idx >= w->count) idx = w->count - 1;
|
||||
if (idx == w->sel) return;
|
||||
w->sel = idx;
|
||||
meow_gfx_mark_dirty(w->x, w->y, w->w, w->h);
|
||||
}
|
||||
int meow_list_selected(meow_widget *w) { return (w && w->type == W_LIST) ? w->sel : -1; }
|
||||
|
||||
void meow_list_set_on_tap(meow_widget *w, meow_list_tap_fn cb, void *ctx)
|
||||
{
|
||||
if (!w || w->type != W_LIST) return;
|
||||
w->tap_cb = cb; w->tap_ctx = ctx;
|
||||
}
|
||||
|
||||
/* 命中行:置选中 + 回调(若设)+ 返回 idx;未命中返回 -1(不置选中)。 */
|
||||
int meow_list_tap_at(meow_widget *w, int x, int y)
|
||||
{
|
||||
int idx = meow_list_hit_index(w, x, y);
|
||||
if (idx < 0) return -1;
|
||||
meow_list_set_selected(w, idx);
|
||||
if (w->tap_cb) w->tap_cb(idx, w->tap_ctx);
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int hit(meow_widget *w, int x, int y)
|
||||
{
|
||||
if (!w) return 0;
|
||||
@@ -816,7 +858,9 @@ void meow_slider_set(meow_widget *w, int val)
|
||||
{
|
||||
if (!w || w->type != W_SLIDER) return;
|
||||
if (val < w->min) val = w->min; if (val > w->max) val = w->max;
|
||||
if (val == w->val) return;
|
||||
w->val = val;
|
||||
meow_gfx_mark_dirty(w->x, w->y, w->w, w->h); /* 滑块/轨道随值变化:标脏 */
|
||||
}
|
||||
int meow_slider_get(meow_widget *w) { return w && w->type == W_SLIDER ? w->val : 0; }
|
||||
void meow_switch_set(meow_widget *w, int on)
|
||||
@@ -899,8 +943,8 @@ void meow_widget_draw(meow_widget *w)
|
||||
if (sw > 0 && sc) meow_ui_draw_rect_round(w->x, w->y, w->w, w->h, r, sc);
|
||||
if (w->state & MEOW_WS_FOCUSED)
|
||||
meow_ui_draw_rect_round(w->x, w->y, w->w, w->h, r, th->primary);
|
||||
/* 禁用:整体变暗(状态层向 onSurface 叠 α) */
|
||||
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);
|
||||
/* 禁用:整体变暗(圆角 α,防在圆角外叠出灰方块——M2.71-bug4) */
|
||||
if (disabled) meow_gfx_fill_round_alpha(w->x, w->y, w->w, w->h, r, th->onSurface, 90);
|
||||
break;
|
||||
}
|
||||
case W_SLIDER: {
|
||||
@@ -922,7 +966,7 @@ void meow_widget_draw(meow_widget *w)
|
||||
int knob_r = w->h / 2 - 3;
|
||||
int kx = w->on ? w->x + w->w - knob_r * 2 - 3 : w->x + 3;
|
||||
meow_gfx_fill_round(kx, w->y + 3, knob_r * 2, knob_r * 2, knob_r, 0xFFFFFF);
|
||||
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);
|
||||
if (disabled) meow_gfx_fill_round_alpha(w->x, w->y, w->w, w->h, w->h / 2, th->onSurface, 90);
|
||||
break;
|
||||
}
|
||||
case W_LIST: {
|
||||
@@ -995,7 +1039,7 @@ void meow_widget_draw(meow_widget *w)
|
||||
int tw = meow_ui_text_width(w->buf, th->font_body);
|
||||
meow_ui_fill_rect(tx + tw, ty, 2, meow_ui_text_height(th->font_body), th->primary);
|
||||
}
|
||||
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);
|
||||
if (disabled) meow_gfx_fill_round_alpha(w->x, w->y, w->w, w->h, r, th->onSurface, 90);
|
||||
break;
|
||||
}
|
||||
case W_CARD: {
|
||||
@@ -1008,7 +1052,7 @@ void meow_widget_draw(meow_widget *w)
|
||||
meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->primary, 24);
|
||||
if (w->nchildren > 0 && w->children[0]) meow_widget_draw(w->children[0]);
|
||||
if (w->state & MEOW_WS_FOCUSED) meow_ui_draw_rect_round(w->x, w->y, w->w, w->h, r, th->primary);
|
||||
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);
|
||||
if (disabled) meow_gfx_fill_round_alpha(w->x, w->y, w->w, w->h, r, th->onSurface, 90);
|
||||
break;
|
||||
}
|
||||
case W_DIVIDER: {
|
||||
@@ -1046,7 +1090,7 @@ void meow_widget_draw(meow_widget *w)
|
||||
meow_ui_draw_rect_round(w->x, w->y, sz, sz, r, th->outline);
|
||||
}
|
||||
if (w->state & MEOW_WS_FOCUSED) meow_ui_draw_rect_round(w->x, w->y, sz, sz, r, th->primary);
|
||||
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, sz, sz, th->onSurface, 90);
|
||||
if (disabled) meow_gfx_fill_round_alpha(w->x, w->y, sz, sz, r, th->onSurface, 90);
|
||||
break;
|
||||
}
|
||||
case W_RADIO: {
|
||||
@@ -1054,13 +1098,16 @@ void meow_widget_draw(meow_widget *w)
|
||||
int ccx = w->x + w->w / 2, ccy = w->y + w->h / 2;
|
||||
meow_gfx_ring_arc(ccx, ccy, 12, 2, 0, 360, sel ? th->primary : th->outline);
|
||||
if (sel) meow_gfx_fill_circle(ccx, ccy, 6, th->primary);
|
||||
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);
|
||||
if (disabled) meow_gfx_fill_round_alpha(w->x, w->y, w->w, w->h, w->w / 2, th->onSurface, 90);
|
||||
break;
|
||||
}
|
||||
case W_PROGRESS: {
|
||||
if (w->variant == 1) { /* 圆形 */
|
||||
int ccx = w->x, ccy = w->y, rr = w->w;
|
||||
int thk = 4;
|
||||
if (w->variant == 1) { /* 圆形(几何 = 整圆像素外接框;由此解回圆心/半径) */
|
||||
int thk = MEOW_PROGRESS_ARC_THK;
|
||||
int m = MEOW_PROGRESS_ARC_MARGIN;
|
||||
int ccx = w->x + w->w / 2, ccy = w->y + w->h / 2;
|
||||
int rr = w->w / 2 - m; /* 与 create 一致:回到调用方传入的半径 r */
|
||||
if (rr <= 0) break;
|
||||
meow_gfx_ring_arc(ccx, ccy, rr, thk, 0, 360, th->surfaceVariant);
|
||||
if (w->value < 0) { /* 不定:按时间推进一段弧(App 在 on_tick 里重绘驱动) */
|
||||
int start = (int)((meow_ui_time_ms() / 30) % 360);
|
||||
@@ -1094,7 +1141,7 @@ void meow_widget_draw(meow_widget *w)
|
||||
meow_ui_text_center(w->x + w->w / 2, w->y + (w->h - meow_ui_text_height(th->font_body)) / 2,
|
||||
w->label, th->font_body, tc);
|
||||
if (w->state & MEOW_WS_FOCUSED) meow_ui_draw_rect_round(w->x, w->y, w->w, w->h, r, th->primary);
|
||||
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);
|
||||
if (disabled) meow_gfx_fill_round_alpha(w->x, w->y, w->w, w->h, r, th->onSurface, 90);
|
||||
break;
|
||||
}
|
||||
case W_TOPAPPBAR: {
|
||||
|
||||
@@ -144,11 +144,18 @@ int main(void)
|
||||
assert(meow_list_hit_index(lc, 30, 480) == ((480 - 460 + 3500) / 40)); /* 可见项换算 */
|
||||
meow_list_set_item_h(lc, 50);
|
||||
assert(meow_widget_hit(lc, 30, 470) == 1);
|
||||
/* M2.71-bug8:行选中 + 命中点击回调 */
|
||||
meow_list_set_selected(lc, 5);
|
||||
assert(meow_list_selected(lc) == 5);
|
||||
/* 验证 tap_at 返回命中下标并置选中 */
|
||||
int hidx = meow_list_tap_at(lc, 30, 480);
|
||||
assert(hidx == ((480 - 460 + meow_list_scroll(lc)) / 50));
|
||||
assert(meow_list_selected(lc) == hidx);
|
||||
/* LazyRow 横向 */
|
||||
meow_widget *lr = W(meow_lazyrow_create(20, 640, 380, 40, 60, 100, list_src, NULL));
|
||||
meow_list_scroll_by(lr, 200);
|
||||
assert(meow_list_hit_index(lr, 40, 660) == ((40 - 20 + 200) / 60));
|
||||
printf("PASS lazy list: scroll clamp/virtualize/hit_index (col+row)\n");
|
||||
printf("PASS lazy list: scroll clamp/virtualize/hit_index/tap_at/select (col+row)\n");
|
||||
|
||||
/* ---------- Dialog / BottomSheet 纯逻辑(create/set/add;show 阻塞故不调) ---------- */
|
||||
meow_dialog *dlg = meow_dialog_create(MEOW_DLG_SELECT, "Pick one");
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/* test_sdk_fixes.c — M2.71 修复项宿主验证(离屏 480x720,16bpp)。
|
||||
* 覆盖:
|
||||
* bug4 disabled 按钮圆角外无灰方块(角点 = 页面背景而非叠暗);
|
||||
* bug1 圆形进度环带确实画到 = 半径+thk/2 处(bbox 与真实像素一致,漏刷修复的几何前提);
|
||||
* bug3 modal(Dialog)open→cancel 后背景还原(scrim 不累积/无残影);
|
||||
* bug7 IME 回显实时同步(on_change 随键入触发)+ 关闭后键盘区背景还原。
|
||||
*
|
||||
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/activity/dialog/ime + 打桩 font/global):
|
||||
* gcc -Wall -o test_sdk_fixes test_sdk_fixes.c test_host_stub.c \
|
||||
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
||||
* meow_ui_widget.c meow_ui_input.c meow_ui_activity.c meow_ui_dialog.c meow_ui_ime.c \
|
||||
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread
|
||||
*/
|
||||
#include "meow_ui.h"
|
||||
#include "meow_ui_internal.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static void rd_px(int x, int y, int *r, int *g, int *b)
|
||||
{
|
||||
unsigned char *bb = meow_ui_back_buf();
|
||||
int line = meow_ui_back_buf_pitch(), bpp = meow_ui_back_buf_bpp();
|
||||
assert(bb && line > 0);
|
||||
if (bpp == 16) {
|
||||
uint16_t px = *(uint16_t *)(bb + (size_t)y * line + (size_t)x * 2);
|
||||
*r = (((px >> 11) & 0x1F) << 3) | ((px >> 11) & 0x1F) >> 2;
|
||||
*g = (((px >> 5) & 0x3F) << 2) | ((px >> 5) & 0x3F) >> 4;
|
||||
*b = ((px & 0x1F) << 3) | ((px & 0x1F) >> 2);
|
||||
} else {
|
||||
uint32_t px = *(uint32_t *)(bb + (size_t)y * line + (size_t)x * 4);
|
||||
*r = (px >> 16) & 0xFF; *g = (px >> 8) & 0xFF; *b = px & 0xFF;
|
||||
}
|
||||
}
|
||||
static int near(int a, int b) { int d = a - b; if (d < 0) d = -d; return d <= 12; }
|
||||
|
||||
/* ---- 为阻塞模态/IME 在后台注入事件 ---- */
|
||||
static int g_inj_tap_x = 0, g_inj_tap_y = 0, g_inj_delay = 0;
|
||||
static void *inject_thread(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
usleep((useconds_t)g_inj_delay);
|
||||
meow_input_inject(MEOW_EV_TAP, g_inj_tap_x, g_inj_tap_y);
|
||||
return NULL;
|
||||
}
|
||||
static void inject_after(int ms, int x, int y)
|
||||
{
|
||||
pthread_t th; g_inj_tap_x = x; g_inj_tap_y = y; g_inj_delay = ms;
|
||||
pthread_create(&th, NULL, inject_thread, NULL);
|
||||
pthread_detach(th);
|
||||
}
|
||||
|
||||
static void test_disabled_corner(void)
|
||||
{
|
||||
const meow_theme *th = meow_theme_get();
|
||||
meow_ui_clear(th->bg);
|
||||
meow_widget *b = meow_btn_create(100, 100, 200, 60, "DISABLED");
|
||||
meow_widget_set_enabled(b, 0); /* 禁用:old 用方形 α 盖整 bbox → 圆角外露灰 */
|
||||
meow_widget_draw(b);
|
||||
meow_ui_flush();
|
||||
int r, g, bb;
|
||||
rd_px(100, 100, &r, &g, &bb); /* bbox 左上角(圆角外):应为页面背景 */
|
||||
printf("[bug4] disabled corner rgb=%02X%02X%02X (bg=%02X%02X%02X) ",
|
||||
r, g, bb, (th->bg >> 16) & 0xFF, (th->bg >> 8) & 0xFF, th->bg & 0xFF);
|
||||
assert(near(r, (th->bg >> 16) & 0xFF) && near(g, (th->bg >> 8) & 0xFF) && near(bb, th->bg & 0xFF));
|
||||
printf("PASS\n");
|
||||
meow_widget_free(b);
|
||||
}
|
||||
|
||||
static void test_circular_extent(void)
|
||||
{
|
||||
const meow_theme *th = meow_theme_get();
|
||||
meow_ui_clear(th->bg);
|
||||
/* r=30, thk=4 → 环外缘 = r+thk/2 = 32;create 几何外接框 = 中心±(r+margin)=±33 */
|
||||
int cx = 240, cy = 200, r = 30;
|
||||
meow_widget *pg = meow_progress_circular_create(cx, cy, r, 100);
|
||||
meow_widget_draw(pg);
|
||||
meow_ui_flush();
|
||||
int rd, g, b;
|
||||
rd_px(cx - 32, cy, &rd, &g, &b); /* 最左环外缘:应被画(非背景) */
|
||||
int bg_r = (th->bg >> 16) & 0xFF, bg_g = (th->bg >> 8) & 0xFF, bg_b = th->bg & 0xFF;
|
||||
printf("[bug1] ring-left-extent rgb=%02X%02X%02X bg=%02X%02X%02X ", rd, g, b, bg_r, bg_g, bg_b);
|
||||
assert(!(near(rd, bg_r) && near(g, bg_g) && near(b, bg_b))); /* 环带已画到外缘 */
|
||||
printf("PASS\n"); fflush(stdout);
|
||||
meow_widget_free(pg);
|
||||
}
|
||||
|
||||
static void test_modal_restore(void)
|
||||
{
|
||||
const meow_theme *th = meow_theme_get();
|
||||
meow_ui_clear(th->bg);
|
||||
meow_ui_flush();
|
||||
meow_dialog *d = meow_dialog_create(MEOW_DLG_CONFIRM, "Sure?");
|
||||
/* 两次 open→cancel:背景应两次均还原(scrim 不累积、无残影) */
|
||||
for (int i = 0; i < 2; i++) {
|
||||
inject_after(30, 1, 1); /* (1,1) 在居中卡片外 → 取消 */
|
||||
meow_dialog_show(d);
|
||||
int r, g, b;
|
||||
rd_px(400, 100, &r, &g, &b); /* 卡片外右上:应 == 背景 */
|
||||
printf("[bug3] open%d cancel bg rgb=%02X%02X%02X bg=%02X%02X%02X ", i, r, g, b,
|
||||
(th->bg >> 16) & 0xFF, (th->bg >> 8) & 0xFF, th->bg & 0xFF);
|
||||
assert(near(r, (th->bg >> 16) & 0xFF) && near(g, (th->bg >> 8) & 0xFF) && near(b, th->bg & 0xFF));
|
||||
}
|
||||
printf("PASS\n");
|
||||
meow_dialog_free(d);
|
||||
}
|
||||
|
||||
/* IME 回显同步回调 */
|
||||
static char g_echo[64] = "";
|
||||
static void ime_cb(const char *t, void *ctx) { (void)ctx; snprintf(g_echo, sizeof g_echo, "%s", t); }
|
||||
|
||||
static void test_ime_echo_and_restore(void)
|
||||
{
|
||||
const meow_theme *th = meow_theme_get();
|
||||
meow_ui_clear(th->bg);
|
||||
meow_ui_flush();
|
||||
meow_ime_ctx c; memset(&c, 0, sizeof c);
|
||||
c.mode = MEOW_IME_ASCII; c.maxlen = 32; c.on_change = ime_cb;
|
||||
/* --- 异步:验『回显/输入框实时同步(on_change 随键入触发)』--- */
|
||||
meow_ui_text_input_async(&c);
|
||||
assert(meow_ime_active() == 1);
|
||||
meow_input_inject(MEOW_EV_TAP, 20, 580); /* ASCII 第3行第1键 = 'A'(shift 关 → 缓冲 'A') */
|
||||
meow_ime_handle_tap(20, 580);
|
||||
assert(strcmp(g_echo, "A") == 0 && strcmp(meow_ime_text(), "A") == 0);
|
||||
printf("[bug7] ime echo live-sync after 'A' -> \"%s\" (on_change fired) PASS\n", g_echo); fflush(stdout);
|
||||
meow_input_inject(MEOW_EV_TAP, 20, 680); /* ESC(X) → 取消 */
|
||||
meow_ime_handle_tap(20, 680);
|
||||
assert(meow_ime_active() == 0);
|
||||
/* --- 阻塞:验『关闭后键盘区背景还原』--- */
|
||||
meow_ui_clear(th->bg);
|
||||
meow_ui_flush();
|
||||
inject_after(30, 20, 680); /* ESC 关闭 */
|
||||
int rc = meow_ui_text_input(&c);
|
||||
assert(rc == 0); /* 取消 */
|
||||
int r, g, b;
|
||||
rd_px(240, 600, &r, &g, &b); /* 键盘区内一点:应 == 背景 */
|
||||
printf("[bug7] ime close restore px rgb=%02X%02X%02X bg=%02X%02X%02X ", r, g, b,
|
||||
(th->bg >> 16) & 0xFF, (th->bg >> 8) & 0xFF, th->bg & 0xFF);
|
||||
assert(near(r, (th->bg >> 16) & 0xFF) && near(g, (th->bg >> 8) & 0xFF) && near(b, th->bg & 0xFF));
|
||||
printf("PASS\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
setenv("MEOW_UI_DUMP", "/tmp/fixes.ppm", 1);
|
||||
meow_ui_opts o = { 480, 720 };
|
||||
assert(meow_ui_init(&o) == 0);
|
||||
test_disabled_corner();
|
||||
test_circular_extent();
|
||||
test_modal_restore();
|
||||
test_ime_echo_and_restore();
|
||||
meow_ui_shutdown();
|
||||
printf("ALL PASS\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user