公开 SDK 落后主仓的一批 bugfix(主仓是固件构建源,这些修复已在设备上跑过)。 本次只做「主仓 → 公开仓」单向同步,图像栈方向由主仓侧另一单处理。 时钟 ABI(M16 第二弹,原文件自认「残余未修」): - meow_ui_time.c 147→345 行:futex 相对超时接管(__timedwait/_cp)、 timerfd_create/settime/gettime 的 itimerspec 64↔32 双向转换、 __clock_settime64 越界返 ENOTSUP,另有 4 个纯换算函数 - meow_ui_internal.h:struct meow_time32 / meow_itimerspec32 契约移入内部头 - CLOCK_ABI.md:补接管机制、验证配方与 objdump 证据 - 新增 test_meow_time64.c IME bug3(成对才有意义): - meow_ui_ime.c:IME_TAP_COOLDOWN_MS 200 冷却窗 + 打开时二次 drain - meow_ui_input.c:meow_input_touch_reset() 并在 drain 内调用(复位触摸状态机) - test_ime_fix.c:新增用例④ stray-tap 列表行高(M15,修用户可见 bug): - meow_ui_widget.c:listv_item_h() 由主题字体行高派生,不再写死 56 (两行列表 62 > 56 会让首行标题被裁剪守卫整条跳过) - include/meow_ui_widgets.h:新增 meow_listview_item_h() - 新增 test_meow_ui_listview_rowh.c 其余:meow_select.c / meow_ui_dialog.c 统一 MD3 形状 token; include/meow_ipc.h 补 MSG_KEY_PAGEUP/PAGEDOWN 枚举; meow_ui_sys.c / meow_cachegov.c 取时统一走 meow_ui_time_ms()。 测试辅助函数标定:tuku/test/test_settings_lock_host.c 的注入前等待 150ms→350ms。 该等待按修复前 IME 标定,新冷却窗下首键 dt_open≈114ms 会被当迟到抬起吞掉, 只录到 "68" 两位。仅改等待时长,断言原样保留。 验证:tuku 四套脚本全绿(run_tests 644 PASS / zip 106 / cbz 296 / webtoon PASS); 12 个源文件与主仓逐字节一致;sdk/build_sdk.sh 34 成员; tuku/build_tuku.sh 产出静态 MIPS32r2 ELF。
161 lines
6.8 KiB
C
161 lines
6.8 KiB
C
/*
|
||
* meow_select.c — §5.8.6 枚举/预设选择器(M7.2 设置页缺口 #4)。
|
||
*
|
||
* 封装 = BottomSheet(标题 + 选项列表)+ meow_listview(SINGLE) 单选列表。选项用 meow_listview
|
||
* 渲染(单选高亮 primaryContainer、可滚动 meow_scroll 惯性);外层模态复用 meow_ui_modal_*
|
||
* + meow_ui_modal_capture/restore(与 meow_dialog 同源:scrim 只叠一层/关闭无残影)。
|
||
* 阻塞式(meow_input_pop 轮询);打开前排空残留触摸;点面板外/左缘返回 = 取消;点选项 = 选中 +
|
||
* 关闭 + on_change 回调。选项数组定长(≤MEOW_SELECT_MAX_ITEMS)。
|
||
*/
|
||
#include "meow_ui.h"
|
||
#include "include/meow_select.h"
|
||
#include "meow_ui_internal.h" /* meow_input_pop/drain + meow_ui_modal_capture/restore */
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <unistd.h>
|
||
|
||
#define MEOW_SELECT_ROW 48 /* 选项行高(px) */
|
||
|
||
struct meow_select {
|
||
char title[96];
|
||
char items[MEOW_SELECT_MAX_ITEMS][64];
|
||
int nitems;
|
||
int cur; /* 初始/当前选中项 */
|
||
int result; /* 选中下标 / -1=取消 */
|
||
int done;
|
||
int h; /* 面板高度(0=自动) */
|
||
meow_select_fn cb;
|
||
void *ctx;
|
||
};
|
||
|
||
/* meow_listview src2 数据源:填 title=options[idx](右尾件无;enabled=1)。 */
|
||
static int select_src2(int idx, meow_list_item *out, void *ctx)
|
||
{
|
||
meow_select *s = (meow_select *)ctx;
|
||
if (idx < 0 || idx >= s->nitems) return 0;
|
||
memset(out, 0, sizeof *out);
|
||
out->title = s->items[idx];
|
||
out->right = MEOW_LIST_RIGHT_NONE;
|
||
out->enabled = 1;
|
||
return 1;
|
||
}
|
||
|
||
meow_select *meow_select_create(const char *title, int cur)
|
||
{
|
||
meow_select *s = (meow_select *)calloc(1, sizeof *s);
|
||
if (!s) return NULL;
|
||
if (title) { strncpy(s->title, title, sizeof s->title - 1); s->title[sizeof s->title - 1] = '\0'; }
|
||
s->cur = cur; s->result = -1; s->done = 0;
|
||
return s;
|
||
}
|
||
int meow_select_add_item(meow_select *s, const char *label)
|
||
{
|
||
if (!s || s->nitems >= MEOW_SELECT_MAX_ITEMS) return -1;
|
||
if (label) { strncpy(s->items[s->nitems], label, 63); s->items[s->nitems][63] = '\0'; }
|
||
int idx = s->nitems;
|
||
s->nitems++;
|
||
return idx;
|
||
}
|
||
void meow_select_set_on_change(meow_select *s, meow_select_fn cb, void *ctx)
|
||
{ if (s) { s->cb = cb; s->ctx = ctx; } }
|
||
|
||
int meow_select_open(meow_select *s)
|
||
{
|
||
if (!s || s->nitems <= 0) return -1;
|
||
const meow_theme *th = meow_theme_get();
|
||
meow_input_start();
|
||
meow_input_drain(); /* 排空残留触摸(goodix 长尾) */
|
||
|
||
s->done = 0; s->result = -1;
|
||
int W = meow_ui_width(), H = meow_ui_height();
|
||
int sb = meow_ui_statusbar_height(); if (sb < 0) sb = 0;
|
||
int pad = 24;
|
||
int title_h = meow_ui_text_height(th->font_title);
|
||
int rowh = MEOW_SELECT_ROW;
|
||
int avail = H - sb - 20;
|
||
int min_h = pad + title_h + 12 + rowh + 20;
|
||
int want = (s->h > 0) ? s->h : min_h;
|
||
int est = pad + title_h + 12 + s->nitems * rowh + 20;
|
||
if (est > want) want = est;
|
||
if (want > avail) want = avail;
|
||
if (want < min_h) want = (min_h < avail) ? min_h : avail;
|
||
int ph = want, py = H - ph; /* 底部面板(顶部圆角) */
|
||
int list_y = py + pad + title_h + 12;
|
||
int list_h = ph - (list_y - py) - pad; /* 底部余量 */
|
||
if (list_h < rowh) list_h = rowh;
|
||
|
||
/* 选项列表(meow_listview[SINGLE]:单选高亮 + 可滚动)。open 时创建,close 释放(冷路径)。 */
|
||
meow_widget *lv = meow_listview_create(0, list_y, W, list_h, s->nitems,
|
||
select_src2, s, MEOW_LIST_SEL_SINGLE);
|
||
if (!lv) return -1;
|
||
meow_listview_set_item_h(lv, rowh);
|
||
/* 定位初始选中项在视口内。 */
|
||
int target = s->cur * rowh - (list_h - rowh) / 2;
|
||
int maxs = meow_listview_max_scroll(lv);
|
||
if (target < 0) target = 0;
|
||
if (target > maxs) target = maxs;
|
||
meow_listview_set_scroll(lv, target);
|
||
meow_listview_set_sel(lv, s->cur);
|
||
meow_scroll *sc = meow_scroll_wrap(lv, 1); /* 惯性滚动(fling/snap) */
|
||
|
||
/* §6.4 模态作用域 + 背景快照(关闭即还原,无残影/不叠黑)。 */
|
||
meow_ui_modal *m = meow_ui_modal_begin();
|
||
meow_ui_modal_capture();
|
||
/* 首次绘制:遮罩 + 底部面板(顶部圆角)+ 标题 + 选项列表。 */
|
||
meow_ui_fill_rect_alpha(0, 0, W, H, 0x000000, 150);
|
||
/* MD3 sheet 顶角 = corner-extra-large-top(28)(material-web _md-comp-sheet-bottom.scss),
|
||
* 与 Dialog 一致;同 SDK meow_sheet(meow_ui_dialog.c:361)/dialog 卡(meow_ui_dialog.c:103) 取 28,
|
||
* 底部面板统一 extra-large,避免同 SDK 两个 bottom sheet 顶部圆角不一致(旧 th->radius_card=16)。 */
|
||
meow_gfx_fill_round(0, py, W, ph, MEOW_SHAPE_EXTRA_LARGE, th->surfaceContainerHighest);
|
||
meow_ui_text(pad, py + pad, s->title, th->font_title, th->onSurface);
|
||
meow_widget_draw(lv);
|
||
meow_ui_flush();
|
||
|
||
int dy_start = 0, down = 0;
|
||
while (!s->done) {
|
||
meow_input_ev e;
|
||
while (meow_input_pop(&e)) {
|
||
meow_ui_activity_ping(); /* 模态期间交互也重置息屏计时 */
|
||
if (e.type == MEOW_EV_TOUCH_DOWN) { down = 1; dy_start = e.b; }
|
||
else if (e.type == MEOW_EV_TOUCH_MOVE) {
|
||
if (down && sc) {
|
||
meow_scroll_track(sc, e.b - dy_start, meow_ui_time_ms());
|
||
dy_start = e.b;
|
||
}
|
||
} else if (e.type == MEOW_EV_TAP) {
|
||
int x = e.a, y = e.b;
|
||
if (y < py) { s->done = 1; s->result = -1; break; } /* 点面板外 = 取消 */
|
||
int idx = meow_listview_tap_at(lv, x, y); /* 单选高亮 = 选中 */
|
||
if (idx >= 0) {
|
||
s->result = idx; s->cur = idx;
|
||
if (s->cb) s->cb(idx, s->ctx); /* 关闭即回调 */
|
||
s->done = 1;
|
||
}
|
||
/* 面板内但未命中项:保持打开(等下一次点击)。 */
|
||
} else if (e.type == MEOW_EV_SWIPE) {
|
||
if (e.a == MEOW_SWIPE_LEFT) { s->done = 1; s->result = -1; } /* 返回 = 取消 */
|
||
else if (sc) meow_scroll_fling(sc);
|
||
}
|
||
}
|
||
meow_anim_tick(); /* 推进 fling */
|
||
if (!s->done) usleep(16000);
|
||
}
|
||
meow_ui_modal_end(m);
|
||
meow_ui_modal_restore();
|
||
if (sc) meow_scroll_free(sc);
|
||
meow_widget_free(lv);
|
||
return s->result;
|
||
}
|
||
void meow_select_free(meow_select *s) { if (s) free(s); }
|
||
|
||
int meow_select_show(const char *title, const char *const *opts, int n, int cur)
|
||
{
|
||
if (!opts || n <= 0) return -1;
|
||
meow_select *s = meow_select_create(title, cur);
|
||
if (!s) return -1;
|
||
for (int i = 0; i < n && i < MEOW_SELECT_MAX_ITEMS; i++) meow_select_add_item(s, opts[i]);
|
||
int r = meow_select_open(s);
|
||
meow_select_free(s);
|
||
return r;
|
||
}
|