M2.60→M2.61 骨架: SDK Activity 栈 + 控件归属(agent-122) + 按钮去圈/即时重绘(agent-119) + SDK 模板文档(agent-121)

- SDK §6.1 三层控件模型: Activity栈(push/pop/active) + 页面控件归属 owner + meow_ui_global_* 骨架(②) + 系统级仅内部接口(③)
- meow_widget 加 owner; live_btn_hit/meow_widget_hit 仅 active activity 命中; touch_refresh stale 保护
- 根治"全屏页触摸主页按钮闪现"(跨页面控件残留); 控件对象复用弹栈不重建
- hello 迁移双 Activity(home/full): 全屏左滑=弹栈回主页, 根左滑=退出
- agent-119 按钮去圈(纯色accent+黑12/24%)+即时局部重绘保留; agent-121 SDK模板§5.3-5.7
This commit is contained in:
MeowOS committed 2026-08-30 15:27:10 +08:00
1 parent 07c556e907
commit 6824defdf9
13 files changed
+613 -23

No files matched your search

+121 -19
View File
@@ -11,7 +11,7 @@
enum { W_BTN = 0, W_SLIDER = 1, W_SWITCH = 2, W_LIST = 3 };
/* 按钮「按住」(长按)判定阈值(ms):超过即进入更强态(更深状态层 + 描边环)。
/* 按钮「按住」(长按)判定阈值(ms):超过即进入更强态(更深状态层)。
* 视觉区分「短按按下」与「按住」;触发语义不变(on_tap 仍按抬手判定)。 */
#define MEOW_BTN_HOLD_MS 500u
@@ -28,6 +28,7 @@ static uint32_t blend_color(uint32_t c, uint32_t o, int t)
struct meow_widget {
int type;
int x, y, w, h;
int owner; /* §6.1 ①:创建时绑定的 Activity;命中只在 owner==active 才生效 */
char label[128];
int pressed; /* 按钮按下视觉态 */
int min, max, val; /* 滑条 */
@@ -37,12 +38,105 @@ struct meow_widget {
void *ctx;
};
/* ---- 实时按压即时刷新(agent-119)----
* 按压/按住视觉由 meow_widget_draw 读实时触摸(meow_input_touch_*)呈现,但 app 只在
* on_tick(1s)/on_tap 重绘 → 快速点/按住时按压态刷新滞后/漏。此处在 SDK 触摸按下/释放/
* 按住达阈值时主动重绘命中的按钮区域并局部上屏(不等 app 重绘),即时反映按下变深/
* 释放恢复,连续快速点击每按都反映。仅针对 SDK 控件按钮(W_BTN);app 纯自绘不适用。
* 语义不变:on_tap / meow_btn_pressed / meow_widget_hit 零改动;非按钮区域不动。 */
#define MEOW_LIVE_BTN_MAX 64
static meow_widget *g_live_btns[MEOW_LIVE_BTN_MAX];
static int g_live_btn_n = 0;
static meow_widget *g_live_btn = NULL; /* 实时按压中最上层按钮(无 = NULL) */
static int g_live_prev_hold = 0; /* 该按钮上次渲染是否为按住态 */
static void live_btn_register(meow_widget *w)
{
if (!w || w->type != W_BTN) 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;
}
static void live_btn_unregister(meow_widget *w)
{
for (int i = 0; i < g_live_btn_n; i++) {
if (g_live_btns[i] == w) {
g_live_btns[i] = g_live_btns[--g_live_btn_n];
if (g_live_btn == w) g_live_btn = NULL;
return;
}
}
if (g_live_btn == w) g_live_btn = NULL;
}
/* 命中:返回坐标所在的「当前 active Activity」的最上层按钮(创建序 = 绘制序,最后者在上)。
* §6.1 ①:仅扫描 owner == 当前 active Activity 的按钮 —— 非 active Activity 的控件
* 绝不参与命中(根治 agent-119 遗留的「全屏页触摸到主页按钮位置 → 闪现 accent 底」)。 */
static meow_widget *live_btn_hit(int x, int y)
{
int active = meow_ui_activity_active();
meow_widget *r = NULL;
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)
r = w; /* 越后创建越上:保留最后命中 */
}
return r;
}
/* 重绘并局部刷新一个按钮区(绘制内部调 meow_gfx_fill_round 已标脏;flush_region 合区+上屏)。 */
static void live_btn_redraw(meow_widget *w)
{
if (!w) return;
meow_widget_draw(w);
meow_ui_flush_region(w->x, w->y, w->w, w->h);
}
/* 由 meow_ui_run 每帧调用(主线程):检测实时触摸状态迁移,即时重绘命中按钮的按压/按住/
* 释放,不等 app on_tick/on_tap。仅在触摸状态相对上次渲染发生变化时动作(非全屏、省性能)。 */
void meow_widget_touch_refresh(void)
{
int active = meow_ui_activity_active();
/* §6.1 ①:若当前按住按钮已不属于 active activity(按压期间发生了 Activity 切换,
* 例如点 FULLSCREEN 后进入全屏),静默清引用——不绘制、不闪现(否则会在新页面底上
* 画出旧按钮的 accent 残留)。 */
if (g_live_btn && g_live_btn->owner != active) {
g_live_btn = NULL;
g_live_prev_hold = 0;
}
int down = meow_input_touch_down();
meow_widget *hit = down ? live_btn_hit(meow_input_touch_x(), meow_input_touch_y()) : NULL;
int hold = down && hit && meow_input_touch_hold_ms() >= MEOW_BTN_HOLD_MS;
if (!down) {
/* 释放:把最近按压按钮恢复为常态 */
if (g_live_btn) { live_btn_redraw(g_live_btn); g_live_btn = NULL; }
g_live_prev_hold = 0;
return;
}
if (hit != g_live_btn) {
/* 按下/滑至新按钮:先恢复旧(若手指滑离),再按压新 */
if (g_live_btn) live_btn_redraw(g_live_btn);
g_live_btn = hit;
g_live_prev_hold = hold;
if (hit) live_btn_redraw(hit);
} else if (hit && hold != g_live_prev_hold) {
/* 同一按钮按住达阈值:升级到更强态 */
live_btn_redraw(hit);
g_live_prev_hold = hold;
}
}
meow_widget *meow_btn_create(int x, int y, int w, int h, const char *label)
{
meow_widget *t = (meow_widget *)calloc(1, sizeof *t);
if (!t) return NULL;
t->type = W_BTN; t->x = x; t->y = y; t->w = w; t->h = h;
t->owner = meow_ui_activity_active(); /* §6.1 ①:绑定当前 active Activity */
if (label) { strncpy(t->label, label, sizeof t->label - 1); t->label[sizeof t->label - 1] = 0; }
live_btn_register(t);
return t;
}
@@ -51,6 +145,7 @@ meow_widget *meow_slider_create(int x, int y, int w, int min, int max, int val)
meow_widget *t = (meow_widget *)calloc(1, sizeof *t);
if (!t) return NULL;
t->type = W_SLIDER; t->x = x; t->y = y; t->w = w; t->h = 40;
t->owner = meow_ui_activity_active();
t->min = min; t->max = (max > min) ? max : min + 1; t->val = (val < min) ? min : (val > t->max) ? t->max : val;
return t;
}
@@ -60,6 +155,7 @@ meow_widget *meow_switch_create(int x, int y, int on)
meow_widget *t = (meow_widget *)calloc(1, sizeof *t);
if (!t) return NULL;
t->type = W_SWITCH; t->x = x; t->y = y; t->w = 56; t->h = 32;
t->owner = meow_ui_activity_active();
t->on = on ? 1 : 0;
return t;
}
@@ -70,11 +166,17 @@ meow_widget *meow_list_create(int x, int y, int w, int h, int count,
meow_widget *t = (meow_widget *)calloc(1, sizeof *t);
if (!t) return NULL;
t->type = W_LIST; t->x = x; t->y = y; t->w = w; t->h = h;
t->owner = meow_ui_activity_active();
t->count = count; t->src = src; t->ctx = ctx; t->sel = 0;
return t;
}
void meow_widget_free(meow_widget *w) { if (w) free(w); }
void meow_widget_free(meow_widget *w)
{
if (!w) return;
live_btn_unregister(w);
free(w);
}
static int hit(meow_widget *w, int x, int y)
{
@@ -82,7 +184,13 @@ static int hit(meow_widget *w, int x, int y)
return x >= w->x && x < w->x + w->w && y >= w->y && y < w->y + w->h;
}
int meow_widget_hit(meow_widget *w, int x, int y) { return hit(w, x, y); }
int meow_widget_hit(meow_widget *w, int x, int y)
{
if (!w) return 0;
/* §6.1 ①:页面控件只对自身 Activity 负责,不跨页面复用命中。 */
if (w->owner != meow_ui_activity_active()) return 0;
return hit(w, x, y);
}
int meow_btn_pressed(meow_widget *w) { return w && w->type == W_BTN ? w->pressed : 0; }
void meow_slider_set(meow_widget *w, int val)
@@ -112,22 +220,16 @@ void meow_widget_draw(meow_widget *w)
meow_input_touch_y() >= w->y && meow_input_touch_y() < w->y + w->h;
int press = w->pressed || live;
int hold = live && meow_input_touch_hold_ms() >= MEOW_BTN_HOLD_MS;
if (hold) {
/* 按住:更强态——更深状态层 + 内缩圆角描边环(浅 accent),贴 MD3 强调;
* 动效取舍:状态层按规范即时切换(MD3 ripple 属额外动画,依赖 on_tick 逐帧,
* 非本「加视觉态」范围),不做 1-2 帧插值。 */
uint32_t hbg = blend_color(th->accent, 0x000000, 24);
meow_gfx_fill_round(w->x, w->y, w->w, w->h, 10, hbg);
if (w->w > 6 && w->h > 6)
meow_ui_draw_rect_round(w->x + 1, w->y + 1, w->w - 2, w->h - 2, 9,
blend_color(th->accent, 0xFFFFFF, 45));
} else if (press) {
/* 按下:轻状态层(accent 向黑调 12%,即变深/偏移),释放恢复;MD3 表面按压。 */
meow_gfx_fill_round(w->x, w->y, w->w, w->h, 10,
blend_color(th->accent, 0x000000, 12));
} else {
meow_gfx_fill_round(w->x, w->y, w->w, w->h, 10, th->accent);
}
/* 纯色状态层(无描边环、无分层):一层即满,深浅二态均由 accent 向黑调出。
* 常态=accent、按下=accent+黑12%、按住=accent+黑24%。整体一层不「先底色再套圈」。 */
uint32_t bg;
if (hold)
bg = blend_color(th->accent, 0x000000, 24);
else if (press)
bg = blend_color(th->accent, 0x000000, 12);
else
bg = th->accent;
meow_gfx_fill_round(w->x, w->y, w->w, w->h, 10, bg);
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, 0xFFFFFF);
break;