sdk: 补同步 swipe-delete 缺失文件(widget/widgets.h/internal.h/build_sdk.sh)

This commit is contained in:
AskaEth committed 2026-09-03 05:59:59 +08:00
1 parent 64cde13c8d
commit a70cdbc0f7
3 files changed
+295 -29

No files matched your search

+30
View File
@@ -210,6 +210,17 @@ enum {
MEOW_ICON_HEART = 29,
};
/* ---- IconButton(圆形/位图图标按钮,M7.0a 缺口#2)----
* 圆形命中 + 位图图标(sb_icons[],Material Symbols Rounded 单通道 alpha)+ 按压态(实时)。
* 用途:电源菜单圆钮 / Now Playing 播放控制 / 锁屏常用件等「位图图标 + 圆形」按钮。
* icon_idx = sb_icons[] 下标(用上面 MEOW_ICON_* 枚举或 SB_I_*;越界画空位图,命中照常)。
* size = 按钮直径(外接正方形 w=h=size;图标 ink 高 = size*6/10,MD3 图标比)。
* 语义色:底 surfaceContainerLow、图标 onSurface;按压叠 onSurface 状态层(实时,同 meow_btn);
* 禁用变淡;选中态 primaryContainer/onPrimaryContainer(meow_widget_set_selected)。
* 命中:圆形(半径=size/2,圆心=按钮中心),owner==active + enabled 双闸门。
* 返回按钮句柄;App 在 on_tap 里用 meow_widget_hit 判定是否命中本钮(命中即触发动作)。 */
meow_widget *meow_iconbtn_create(int x, int y, int size, int icon_idx);
/* ---- NavigationBar(底部导航栏)---- */
meow_widget *meow_navigationbar_create(int x, int y, int w, int h);
int meow_navigationbar_add_item(meow_widget *w, const char *label);
@@ -375,6 +386,25 @@ int meow_listview_set_item_draw(meow_widget *lv, meow_listview_draw_fn draw,
/* 空态占位:count==0 时视口内居中画 empty_text(NULL 不画)。返回 0=成功。 */
int meow_listview_set_empty_text(meow_widget *lv, const char *text);
/* ============================================================ */
/* §5.5 左滑删除(M7.3 通知中心缺口 #9):列表行横向手势 + 删除/回弹动画 */
/* ------------------------------------------------------------
* 行自绘 set_item_draw 配合(SDK 在自绘行前把该行横向偏移 swipe_dx 传给 x,并画右露删除区)。
* 手势由 App 在 on_touch_move 喂三级:begin(touch-down)/ move(touch-move)/ end(release)。
* 判定:横向小位移|dx|>|dy| 且超容差=swipe(move 返回 1=本列表消费,App 勿滚动);纵向
* |dy|>|dx| 且超容差=滚动手势优先(返回 0 并取消本行 swipe,App 照常 scroll_by);点按
* 无位移(end 时未 started)=行点击(返回 0,App 照常 tap_at)。不修改 tap_at/滚动语义。
* 动画:超阈值 → 删除动画(行滑出 meow_anim,结束后回调 on_delete(idx));未超 → 回弹。
* 颜色:删除区用主题 error/onError;不与行点击/滚动冲突(见判定表)。 */
typedef void (*meow_listview_swipe_fn)(int idx, void *ctx);
void meow_listview_set_swipe_delete(meow_widget *lv, int enabled,
meow_listview_swipe_fn on_delete, void *ctx);
int meow_listview_swipe_begin(meow_widget *lv, int x, int y); /* touch-down:1=命中可滑行(喂后续 move) */
int meow_listview_swipe_move(meow_widget *lv, int x, int y); /* touch-move:1=消费(横向);0=让位(取消/纵向) */
int meow_listview_swipe_end(meow_widget *lv, int x, int y); /* release:1=消费(删/回弹动画);0=未滑动(tap) */
int meow_listview_swipe_active(meow_widget *lv); /* 1=有行处于滑动/动画中 */
int meow_listview_swipe_offset(meow_widget *lv); /* 当前滑动行的 dx(<0=左移;自绘行画用) */
#ifdef __cplusplus
}
#endif
+263 -29
View File
@@ -15,7 +15,8 @@ enum { W_BTN = 0, W_SLIDER = 1, W_SWITCH = 2, W_LIST = 3,
W_TEXTFIELD = 7, W_CARD = 8, W_DIVIDER = 9, W_BADGE = 10,
W_CHECKBOX = 11, W_RADIO = 12, W_PROGRESS = 13,
W_TOPAPPBAR = 14, W_NAVBAR = 15, W_TABBAR = 16, W_FAB = 17,
W_LISTVIEW = 18 }; /* §5.5 结构化列表(行=图标+标题+副标题+右侧操作+选中) */
W_LISTVIEW = 18, /* §5.5 结构化列表(行=图标+标题+副标题+右侧操作+选中) */
W_ICONBTN = 19 }; /* M7.0a 缺口#2:圆形/位图图标按钮(圆形命中 + 按压态) */
/* 布局子项数组上限(固定尺寸子项先做;超出静默拒收)。 */
#define MEOW_LAYOUT_MAX_CHILD 32
@@ -100,6 +101,8 @@ struct meow_widget {
meow_listview_tap_fn lv_tap_cb; /* 行点击回调(idx, op) */
void *lv_tap_ctx; /* 回调 ctx */
/* W_ICONBTN 位图图标:sb_icons[] 下标(= MEOW_ICON_* 或 SB_I_*;<0 画空位图,命中照常)。 */
int icon_id;
/* §5.5 虚拟化层(W_LISTVIEW 扩展:固定 item 池 + 多行渲染器 + 完全自绘 + 空态。
* calloc 归零,安全;vs_count/vs_bind 均非 NULL 才进入 pool 模式,src2 模式不受影响。) */
meow_listview_count_fn vs_count; /* 虚拟化总数回调(NULL=src2 模式) */
@@ -113,6 +116,21 @@ struct meow_widget {
int show_icon; /* 1=画左图标/缩略图(默认 1) */
int show_right;/* 1=画右尾件(默认 1) */
char empty_text[64]; /* 空态占位文案(首字符 0=不画) */
/* §5.5 左滑删除(列表行横向手势:滑动偏移/删除动画/回弹;calloc 归零,安全)。
* 手势由 App 在 on_touch_move 喂(swipe_begin/move/end);横向小位移判 swipe、纵向滚动优先;
* 行自绘 set_item_draw 配合(M7.3 通知行)。swipe_anim 由 meow_anim 静态池驱动。 */
int swipe_enabled; /* 1=左滑删除可用(on_delete 回调有效) */
int swipe_active; /* 1=当前行处于滑动态(swipe_idx 有效) */
int swipe_idx; /* 正被滑动的行号(-1=无) */
int swipe_dx; /* 当前行横向偏移(<=0=左移;滑动/动画中) */
int swipe_drag; /* 1=手指拖动中(限行内) */
int swipe_start_x, swipe_start_y; /* 手势起点(判定方向用) */
int swipe_started; /* 1=已裁定为横向 swipe(纵向滚动手势被抑制) */
int swipe_anim_delete;/* 1=删除动画(目标 -w);0=回弹(目标 0) */
meow_listview_swipe_fn swipe_cb; /* 删除回调(idx) */
void *swipe_ctx;
meow_anim *swipe_anim; /* 滑动/回弹动画(非 NULL=进行中) */
};
/* ---- §5.8 batch1 内部辅助:opts 取值(opts_used==0 时按控件内建默认)----
@@ -225,7 +243,9 @@ static int g_live_prev_hold = 0; /* 该按钮上次渲染是否为按住
static void live_btn_register(meow_widget *w)
{
if (!w || w->type != W_BTN) return;
if (!w) return;
/* M7.0a 缺口#2:IconButton 也是实时按压钮(同 meow_btn 的按压/按住即时重绘)。 */
if (w->type != W_BTN && w->type != W_ICONBTN) return;
for (int i = 0; i < g_live_btn_n; i++)
if (g_live_btns[i] == w) return;
if (g_live_btn_n < MEOW_LIVE_BTN_MAX) g_live_btns[g_live_btn_n++] = w;
@@ -246,6 +266,15 @@ static void live_btn_unregister(meow_widget *w)
/* 命中:返回坐标所在的「当前 active Activity」的最上层按钮(创建序 = 绘制序,最后者在上)。
* §6.1 ①:仅扫描 owner == 当前 active Activity 的按钮 —— 非 active Activity 的控件
* 绝不参与命中(根治 agent-119 遗留的「全屏页触摸到主页按钮位置 → 闪现 accent 底」)。 */
/* 圆形命中(IconButton;圆心=按钮中心、半径=size/2)。 */
static int iconbtn_circular_hit(const meow_widget *w, int x, int y)
{
int cx = w->x + w->w / 2, cy = w->y + w->h / 2;
int r = (w->w < w->h ? w->w : w->h) / 2;
int dx = x - cx, dy = y - cy;
return (dx * dx + dy * dy) <= (r * r);
}
static meow_widget *live_btn_hit(int x, int y)
{
int active = meow_ui_activity_active();
@@ -253,8 +282,12 @@ static meow_widget *live_btn_hit(int x, int y)
for (int i = 0; i < g_live_btn_n; i++) {
meow_widget *w = g_live_btns[i];
if (w->owner != active) continue; /* 非 active activity:跳过 */
if (x >= w->x && x < w->x + w->w && y >= w->y && y < w->y + w->h)
/* M7.0a 缺口#2:IconButton 按圆形命中(角部按下不点亮圆钮);其余按矩形。 */
if (w->type == W_ICONBTN) {
if (iconbtn_circular_hit(w, x, y)) r = w;
} else if (x >= w->x && x < w->x + w->w && y >= w->y && y < w->y + w->h) {
r = w; /* 越后创建越上:保留最后命中 */
}
}
return r;
}
@@ -540,6 +573,20 @@ int meow_btn_set_variant(meow_widget *w, int variant)
return 0;
}
/* ---- §5.8.6 M7.0a 缺口#2:IconButton(圆形/位图图标按钮)---- */
meow_widget *meow_iconbtn_create(int x, int y, int size, int icon_idx)
{
meow_widget *t = (meow_widget *)calloc(1, sizeof *t);
if (!t) return NULL;
t->type = W_ICONBTN; t->x = x; t->y = y; t->w = size; t->h = size;
t->owner = meow_ui_activity_active(); /* §6.1 ①:同既有控件绑当前 active Activity */
t->state = MEOW_WS_ENABLED;
t->icon_id = icon_idx;
live_btn_register(t); /* 实时按压/按住即时重绘(同 meow_btn,agent-119) */
meow_draw_widget_register(t); /* §6.2 自动注册(创建即注册,App 零调用) */
return t;
}
/* ---- TextField ---- */
meow_widget *meow_textfield_create(int x, int y, int w, int h, const char *placeholder,
char *buf, int maxlen, meow_ime_mode mode)
@@ -1129,7 +1176,7 @@ meow_widget *meow_listview_create(int x, int y, int w, int h, int count,
t->owner = meow_ui_activity_active();
t->state = MEOW_WS_ENABLED;
t->count = count > 0 ? count : 0; t->src2 = src; t->ctx = ctx;
t->sel = -1; t->item_h = 56; t->scroll = 0;
t->sel = -1; t->item_h = 56; t->scroll = 0; t->swipe_idx = -1;
t->sel_mode = (mode >= MEOW_LIST_SEL_NONE && mode <= MEOW_LIST_SEL_MULTI) ? mode : MEOW_LIST_SEL_NONE;
t->lines = 2; t->show_icon = 1; t->show_right = 1; /* 默认双行富 item + 图标 + 右尾件(src2 布局不变) */
if (t->sel_mode == MEOW_LIST_SEL_MULTI && t->count > 0) {
@@ -1361,6 +1408,127 @@ int meow_listview_set_empty_text(meow_widget *w, const char *text)
return 0;
}
/* ============================================================ */
/* §5.5 左滑删除(M7.3 缺口 #9):手势判定 + 删除/回弹动画 */
/* ============================================================ */
#define MEOW_SWIPE_SLOP 10 /* 手势判定容差(px):超过才裁定方向 */
/* 删除阈值:|dx| ≥ 行宽*2/5。 */
static int swipe_thresh(const meow_widget *w) { return w->w * 2 / 5; }
/* 动画更新:设行偏移 + 重绘列表(只画可见行;widget clip 已裁剪)。 */
static void swipe_anim_update(meow_anim *a, float v, void *ctx)
{
(void)a;
meow_widget *lv = (meow_widget *)ctx;
if (!lv) return;
lv->swipe_dx = (int)v;
meow_widget_draw(lv);
}
static void swipe_anim_done(meow_anim *a, int canceled, void *ctx)
{
(void)a;
meow_widget *lv = (meow_widget *)ctx;
if (!lv) return;
int del = lv->swipe_anim_delete;
int idx = lv->swipe_idx;
lv->swipe_anim = NULL;
lv->swipe_anim_delete = 0;
lv->swipe_active = 0; lv->swipe_idx = -1; lv->swipe_dx = 0;
if (canceled) return; /* 取消:回正常位(无回调) */
if (del && lv->swipe_cb) /* 删除:滑出完成 → 回调(App 移除行 + 刷新 count) */
lv->swipe_cb(idx, lv->swipe_ctx);
}
void meow_listview_set_swipe_delete(meow_widget *lv, int enabled,
meow_listview_swipe_fn on_delete, void *ctx)
{
if (!lv || lv->type != W_LISTVIEW) return;
if (lv->swipe_anim) { meow_anim_cancel(lv->swipe_anim); lv->swipe_anim = NULL; }
lv->swipe_enabled = enabled ? 1 : 0;
lv->swipe_cb = on_delete; lv->swipe_ctx = ctx;
lv->swipe_active = 0; lv->swipe_idx = -1; lv->swipe_dx = 0; lv->swipe_drag = 0; lv->swipe_started = 0;
}
int meow_listview_swipe_begin(meow_widget *lv, int x, int y)
{
if (!lv || lv->type != W_LISTVIEW || !lv->swipe_enabled) return 0;
int idx = meow_listview_hit(lv, x, y);
if (idx < 0) return 0;
if (lv->swipe_anim) return 1; /* 已有动画:吞掉,不干扰当前行 */
lv->swipe_drag = 1; lv->swipe_started = 0;
lv->swipe_idx = idx; lv->swipe_dx = 0;
lv->swipe_start_x = x; lv->swipe_start_y = y;
return 1;
}
int meow_listview_swipe_move(meow_widget *lv, int x, int y)
{
if (!lv || lv->type != W_LISTVIEW || !lv->swipe_drag) return 0;
int dx = x - lv->swipe_start_x, dy = y - lv->swipe_start_y;
int adx = dx < 0 ? -dx : dx, ady = dy < 0 ? -dy : dy;
if (!lv->swipe_started) {
if (ady > adx && ady > MEOW_SWIPE_SLOP) { /* 纵向优先:取消 swipe,让位滚动 */
lv->swipe_drag = 0; lv->swipe_active = 0; lv->swipe_idx = -1; lv->swipe_dx = 0;
return 0;
}
if (adx > MEOW_SWIPE_SLOP && adx > ady) { /* 横向小位移:裁定为 swipe */
lv->swipe_started = 1; lv->swipe_active = 1;
} else {
return 0; /* 太小未裁定:暂不消费 */
}
} else {
if (ady > adx && ady > MEOW_SWIPE_SLOP) { /* 中途纵向接管:取消,让位滚动 */
lv->swipe_drag = 0; lv->swipe_active = 0; lv->swipe_idx = -1; lv->swipe_dx = 0;
return 0;
}
}
/* 只左滑(dx<0)露右删除区;右滑 clamp 到 0;触底 clamp 到 -w。 */
int off = dx < 0 ? dx : 0;
if (off < -lv->w) off = -lv->w;
if (off == lv->swipe_dx) return 1;
lv->swipe_dx = off;
meow_gfx_mark_dirty(lv->x, lv->y, lv->w, lv->h);
return 1;
}
int meow_listview_swipe_end(meow_widget *lv, int x, int y)
{
(void)x; (void)y;
if (!lv || lv->type != W_LISTVIEW) return 0;
if (!lv->swipe_drag) return 0; /* 非 swipe 行(tap/滚动)→ App 自理 */
lv->swipe_drag = 0;
if (!lv->swipe_started) return 0; /* 无位移 = 点击 → App 走 tap_at */
int dx = lv->swipe_dx;
int del = (-dx >= swipe_thresh(lv)); /* 超阈值 → 删除;否则回弹 */
int item = listv_item_h(lv);
int ry = lv->y + ((unsigned)lv->swipe_idx * (unsigned)item - lv->scroll);
if (lv->swipe_anim) { meow_anim_cancel(lv->swipe_anim); lv->swipe_anim = NULL; }
lv->swipe_anim_delete = del;
meow_anim_desc d; memset(&d, 0, sizeof d);
d.from = (float)dx;
d.to = (float)(del ? -lv->w : 0);
d.duration_ms = del ? 160u : 140u;
d.ease = del ? MEOW_EASE_OUT : MEOW_EASE_OVERSHOOT;
d.update = swipe_anim_update; d.done = swipe_anim_done; d.ctx = lv;
meow_anim *an = meow_anim_start(&d);
if (!an) { /* 池满:直接终态(防卡死) */
if (del) { int i = lv->swipe_idx; lv->swipe_idx = -1; lv->swipe_dx = 0;
if (lv->swipe_cb) lv->swipe_cb(i, lv->swipe_ctx); }
lv->swipe_active = 0; lv->swipe_dx = 0;
meow_gfx_mark_dirty(lv->x, lv->y, lv->w, lv->h);
return 1;
}
lv->swipe_anim = an;
meow_anim_set_region(an, lv->x, ry < lv->y ? lv->y : ry, lv->w, item); /* 动画帧只刷该行带 */
return 1;
}
int meow_listview_swipe_active(meow_widget *lv)
{ return (lv && lv->type == W_LISTVIEW && (lv->swipe_active || lv->swipe_anim)) ? 1 : 0; }
int meow_listview_swipe_offset(meow_widget *lv)
{ return (lv && lv->type == W_LISTVIEW) ? lv->swipe_dx : 0; }
static int hit(meow_widget *w, int x, int y)
{
if (!w) return 0;
@@ -1374,9 +1542,11 @@ int meow_widget_hit(meow_widget *w, int x, int y)
if (w->owner != meow_ui_activity_active()) return 0;
/* §5.8.3:禁用不响应命中(也不进 pressed)。 */
if (widget_disabled(w)) return 0;
/* §5.8.1:自定义画布用 App 回调判定命中(可非矩形);其余矩形内含。 */
/* §5.8.1:自定义画布用 App 回调判定命中(可非矩形);IconButton 圆形命中;其余矩形内含。 */
if (w->type == W_CUSTOM)
return w->hit_cb ? w->hit_cb(w, x, y, w->ctx) : hit(w, x, y);
if (w->type == W_ICONBTN)
return iconbtn_circular_hit(w, x, y);
return hit(w, x, y);
}
int meow_btn_pressed(meow_widget *w)
@@ -1480,6 +1650,35 @@ void meow_widget_draw(meow_widget *w)
if (disabled) meow_gfx_fill_round_alpha(w->x, w->y, w->w, w->h, r, th->onSurface, 90);
break;
}
case W_ICONBTN: {
/* M7.0a 缺口#2:圆形底 + 位图图标 + 按压/按住状态层(实时)。
* 按压三路来源(同 meow_btn,agent-119):状态机 MEOW_WS_PRESSED / 实时触摸命中 /
* 位图取自 sb_icons[](meow_gfx_blit_icon_ink,只写像素不标顶栏脏区——控件矩形由
* 本 draw 的 clip + invalidation 管理);图标墨色 onSurface,选中/按住淡向底。 */
int live = meow_input_touch_down() &&
iconbtn_circular_hit(w, meow_input_touch_x(), meow_input_touch_y());
int press = (w->state & MEOW_WS_PRESSED) || live;
int hold = live && meow_input_touch_hold_ms() >= (unsigned)meow_ui_long_press_ms();
int disabled = widget_disabled(w);
if (disabled) press = 0;
int cx = w->x + w->w / 2, cy = w->y + w->h / 2;
int r = (w->w < w->h ? w->w : w->h) / 2;
int inkh = (w->h * 6) / 10; /* MD3 图标比:约 24/40 → 0.6*size */
if (inkh < 8) inkh = (w->h > 0) ? w->h : 8;
int selected = (w->state & MEOW_WS_SELECTED);
uint32_t base = selected ? th->primaryContainer : widget_bg(w, th, th->surfaceContainerLow);
uint32_t fgc = selected ? th->onPrimaryContainer : widget_fg(w, th, th->onSurface);
uint32_t bg = (hold) ? state_tint(base, th, 24)
: (press ? state_tint(base, th, 12) : base);
if (disabled) bg = state_tint(base, th, 38); /* 禁用变淡(圆形底直接变灰) */
if (w->state & MEOW_WS_FOCUSED) bg = state_tint(base, th, 8); /* 焦点浅提亮 */
meow_gfx_fill_circle(cx, cy, r, bg);
if (w->icon_id >= 0)
meow_gfx_blit_icon_ink(w->icon_id, cx - MEOW_SB_ICON_SIZE / 2, inkh, fgc, cy);
if (w->state & MEOW_WS_FOCUSED)
meow_gfx_ring_arc(cx, cy, r - 1, 2, 0, 360, th->primary); /* 焦点圆形描边 */
break;
}
case W_SLIDER: {
/* 语义色:轨道=onSurfaceVariant(dim)、填充=primary(accent)、滑块=fg(onSurface)。 */
int trace_y = w->y + w->h / 2 - 3;
@@ -1606,7 +1805,17 @@ void meow_widget_draw(meow_widget *w)
meow_input_touch_x() >= vx0 && meow_input_touch_x() < vx0 + w->w &&
meow_input_touch_y() >= ry && meow_input_touch_y() < ry + item)
st |= MEOW_LISTVIEW_ST_PRESSED;
w->vs_draw(i, vx0, ry, w->w, item, st, w->vs_ctx);
int rx0 = vx0; /* 左滑行:行 x 左移 + 右露删除区 */
if (w->swipe_active && w->swipe_idx == i && w->swipe_dx < 0) {
meow_ui_fill_rect(vx0 + w->w + w->swipe_dx, ry, -w->swipe_dx, item, th->error);
int thh2 = meow_ui_text_height(th->font_body);
int dly = ry + (item - thh2) / 2;
if (dly >= vy0 && dly + thh2 <= vy1)
meow_ui_text_center(vx0 + w->w + w->swipe_dx + (-w->swipe_dx) / 2, dly,
meow_str("common.delete"), th->font_body, th->onError);
rx0 = vx0 + w->swipe_dx;
}
w->vs_draw(i, rx0, ry, w->w, item, st, w->vs_ctx);
}
} else { /* ② 默认 renderer:固定 item 池(进入视口才 bind) */
for (int i = first; i < last; i++) {
@@ -1621,23 +1830,37 @@ void meow_widget_draw(meow_widget *w)
int press = meow_input_touch_down() &&
meow_input_touch_x() >= vx0 && meow_input_touch_x() < vx0 + w->w &&
meow_input_touch_y() >= ry && meow_input_touch_y() < ry + item;
/* 左滑删除(M7.3):本行被滑动 → 右露删除区 + 行 x 左移。非滑动行 shift=0 → 输出不变。 */
int shift = 0;
if (w->swipe_active && w->swipe_idx == i && w->swipe_dx < 0) {
meow_ui_fill_rect(vx0 + w->w + w->swipe_dx, ry, -w->swipe_dx, item, th->error);
int thh2 = meow_ui_text_height(th->font_body);
int dly = ry + (item - thh2) / 2;
if (dly >= vy0 && dly + thh2 <= vy1)
meow_ui_text_center(vx0 + w->w + w->swipe_dx + (-w->swipe_dx) / 2, dly,
meow_str("common.delete"), th->font_body, th->onError);
shift = w->swipe_dx;
}
int rlx = vx0 + shift; /* 行内容左移后的 x */
int rtx = text_x + shift; /* 文本 x(随行左移) */
int rop = op_start + shift; /* 操作区分界 x */
if (it->section) { /* 段头占位行(非粘性):surfaceContainerHigh 底 + primary 段名 */
meow_ui_fill_rect(vx0, cl, w->w, cr - cl, th->surfaceContainerHigh);
meow_ui_fill_rect(rlx, cl, w->w, cr - cl, th->surfaceContainerHigh);
int sty = ry + (item - thh) / 2;
if (it->title && sty >= vy0 && sty + thh <= vy1)
listv_text_trunc(text_x, sty, it->title, text_maxw, th->font_body, th->primary);
listv_text_trunc(rtx, sty, it->title, text_maxw, th->font_body, th->primary);
int sy = ry + item - 1;
if (sy >= cl && sy < cr)
meow_ui_fill_rect(vx0, sy, w->w, 1, th->outlineVariant);
meow_ui_fill_rect(rlx, sy, w->w, 1, th->outlineVariant);
if (!it->enabled)
meow_ui_fill_rect_alpha(vx0, cl, w->w, cr - cl, th->onSurface, 90);
meow_ui_fill_rect_alpha(rlx, cl, w->w, cr - cl, th->onSurface, 90);
continue;
}
/* 选中/按压背景(primaryContainer / 半透明主题色按压层) */
if (sel) meow_ui_fill_rect(vx0, cl, w->w, cr - cl, th->primaryContainer);
else if (press) meow_ui_fill_rect_alpha(vx0, cl, w->w, cr - cl, th->primary, 26);
if (sel) meow_ui_fill_rect(rlx, cl, w->w, cr - cl, th->primaryContainer);
else if (press) meow_ui_fill_rect_alpha(rlx, cl, w->w, cr - cl, th->primary, 26);
uint32_t textcol = sel ? th->onPrimaryContainer : th->onSurface;
uint32_t subcol = sel ? th->onPrimaryContainer : th->onSurfaceVariant;
@@ -1648,38 +1871,38 @@ void meow_widget_draw(meow_widget *w)
: th->onSurfaceVariant);
int icy = ry + (item - isz) / 2;
if (icy + isz >= vy0 && icy < vy1)
listv_draw_icon(th, vx0 + pad, icy, it->icon_kind, it->icon, iccol);
listv_draw_icon(th, rlx + pad, icy, it->icon_kind, it->icon, iccol);
}
/* 标题 + 副标题(双行居中;单行(lines==1 或无副标题)单行居中);越界不画 */
if (w->lines == 2 && it->subtitle && it->subtitle[0]) {
int ty = ry + (item - (2 * thh + MEOW_LISTV_SUB_GAP)) / 2;
if (it->title && ty >= vy0 && ty + thh <= vy1)
listv_text_trunc(text_x, ty, it->title, text_maxw, th->font_body, textcol);
listv_text_trunc(rtx, ty, it->title, text_maxw, th->font_body, textcol);
int sy = ty + thh + MEOW_LISTV_SUB_GAP;
if (sy >= vy0 && sy + thh <= vy1)
listv_text_trunc(text_x, sy, it->subtitle, text_maxw, th->font_body, subcol);
listv_text_trunc(rtx, sy, it->subtitle, text_maxw, th->font_body, subcol);
} else {
int ty = ry + (item - thh) / 2;
if (it->title && ty >= vy0 && ty + thh <= vy1)
listv_text_trunc(text_x, ty, it->title, text_maxw, th->font_body, textcol);
listv_text_trunc(rtx, ty, it->title, text_maxw, th->font_body, textcol);
}
/* 右尾件(show_right=1:箭头/开关/复选/⋯) */
if (w->show_right && it->right != MEOW_LIST_RIGHT_NONE) {
int ax = w->x + w->w - pad - 24; /* 右缘动作槽(约 24px) */
if (ax >= op_start)
int ax = rlx + w->w - pad - 24; /* 右缘动作槽(约 24px) */
if (ax >= rop)
listv_draw_right(th, ax, ry, item, it->right, it->on, subcol);
}
/* 分隔线(仅落在可见带内才画) */
int sy = ry + item - 1;
if (sy >= cl && sy < cr)
meow_ui_fill_rect(vx0, sy, w->w, 1, th->outlineVariant);
meow_ui_fill_rect(rlx, sy, w->w, 1, th->outlineVariant);
/* 禁用行整体变淡(§5.5/§5.8.3 enabled=0 → 变暗) */
if (!it->enabled)
meow_ui_fill_rect_alpha(vx0, cl, w->w, cr - cl, th->onSurface, 90);
meow_ui_fill_rect_alpha(rlx, cl, w->w, cr - cl, th->onSurface, 90);
}
}
} else {
@@ -1697,7 +1920,18 @@ void meow_widget_draw(meow_widget *w)
int sel = (i == w->sel);
uint32_t textcol = sel ? th->onPrimaryContainer : th->onSurface;
uint32_t subcol = sel ? th->onPrimaryContainer : th->onSurfaceVariant;
if (sel) meow_ui_fill_rect(vx0, cl, w->w, cr - cl, th->primaryContainer);
/* 左滑删除(M7.3):本行被滑动 → 右露删除区 + 行 x 左移。非滑动行 shift=0 → 输出不变。 */
int shift = 0, rlx = vx0, rtx = text_x, rop = op_start;
if (w->swipe_active && w->swipe_idx == i && w->swipe_dx < 0) {
meow_ui_fill_rect(vx0 + w->w + w->swipe_dx, ry, -w->swipe_dx, item, th->error);
int thh2 = meow_ui_text_height(th->font_body);
int dly = ry + (item - thh2) / 2;
if (dly >= vy0 && dly + thh2 <= vy1)
meow_ui_text_center(vx0 + w->w + w->swipe_dx + (-w->swipe_dx) / 2, dly,
meow_str("common.delete"), th->font_body, th->onError);
shift = w->swipe_dx; rlx = vx0 + shift; rtx = text_x + shift; rop = op_start + shift;
}
if (sel) meow_ui_fill_rect(rlx, cl, w->w, cr - cl, th->primaryContainer);
/* 图标(左) */
uint32_t iccol = sel ? th->onPrimaryContainer
@@ -1705,38 +1939,38 @@ void meow_widget_draw(meow_widget *w)
: th->onSurfaceVariant);
int icy = ry + (item - isz) / 2;
if (icy + isz >= vy0 && icy < vy1) /* 图标不越视口边界才画(防泄漏) */
listv_draw_icon(th, vx0 + pad, icy, it.icon_kind, it.icon, iccol);
listv_draw_icon(th, rlx + pad, icy, it.icon_kind, it.icon, iccol);
/* 标题 + 副标题(两行居中;无副标题=单行居中;越界不画防泄漏) */
int ty;
if (it.subtitle && it.subtitle[0]) {
ty = ry + (item - (2 * thh + MEOW_LISTV_SUB_GAP)) / 2;
if (it.title && ty >= vy0 && ty + thh <= vy1)
listv_text_trunc(text_x, ty, it.title, text_maxw, th->font_body, textcol);
listv_text_trunc(rtx, ty, it.title, text_maxw, th->font_body, textcol);
int sy = ty + thh + MEOW_LISTV_SUB_GAP;
if (sy >= vy0 && sy + thh <= vy1)
listv_text_trunc(text_x, sy, it.subtitle, text_maxw, th->font_body, subcol);
listv_text_trunc(rtx, sy, it.subtitle, text_maxw, th->font_body, subcol);
} else {
ty = ry + (item - thh) / 2;
if (it.title && ty >= vy0 && ty + thh <= vy1)
listv_text_trunc(text_x, ty, it.title, text_maxw, th->font_body, textcol);
listv_text_trunc(rtx, ty, it.title, text_maxw, th->font_body, textcol);
}
/* 右侧操作区 */
if (it.right != MEOW_LIST_RIGHT_NONE) {
int ax = w->x + w->w - pad - 24; /* 右缘动作槽(约 24px) */
if (ax >= op_start)
int ax = rlx + w->w - pad - 24; /* 右缘动作槽(约 24px) */
if (ax >= rop)
listv_draw_right(th, ax, ry, item, it.right, it.on, subcol);
}
/* 分隔线(仅落在可见带内才画) */
int sy = ry + item - 1;
if (sy >= cl && sy < cr)
meow_ui_fill_rect(vx0, sy, w->w, 1, th->outlineVariant);
meow_ui_fill_rect(rlx, sy, w->w, 1, th->outlineVariant);
/* 禁用行整体变淡(§5.5/§5.8.3 enabled=0 → 变暗) */
if (!it.enabled)
meow_ui_fill_rect_alpha(vx0, cl, w->w, cr - cl, th->onSurface, 90);
meow_ui_fill_rect_alpha(rlx, cl, w->w, cr - cl, th->onSurface, 90);
}
}
if (disabled) meow_ui_fill_rect_alpha(w->x, w->y, w->w, w->h, th->onSurface, 90);