根治「焦点/事件穿透」一类反复 bug。此前命中/捕获/消费/层间隔离各处 ad-hoc; 现上收为单一仲裁者(meow_ui_input.c),公共 API 只追加、不删改既有签名。 分发模型: - 触摸沿 z 序自顶向下命中(meow_draw_hit_topdown,owner==active+enable 过滤); - 命中即消费;模态层永远先审(meow_ui_modal_begin/end/active/depth); - down 捕获(meow_ui_gesture_begin/owner/end):命中目标捕获手势直到 up,防滑出误触下层; - 模态存在时下层完全收不到事件——hit_topdown 恒返回模态哨兵(背板消费,不透传); - meow_ui_run 在 TOUCH_DOWN 自动 begin、TAP/SWIPE/LONG_PRESS/NOTIF_PULL 自动 end。 焦点模型: - 唯一持有者(meow_ui_focus_acquire/release/owner/tap); - 点非 focus 区域→释放并通知(meow_ui_focus_tap,list 焦点残留根治); - 滚动容器(list/listview)tap_at 命中行自动 acquire,点外由 run 的 focus_tap 释放; - lost 回调(meow_ui_focus_set_lost_cb)通知转移。 迁移(行为修复,正常交互语义不变): - dialog/sheet → meow_ui_modal_begin/end(模态期下层收不到;叠黑由既有整帧快照+卡片仅重绘保证); - list/listview tap → meow_ui_focus_acquire; - 注入/叠加让位(overlay_input_suppressed)与模态层隔离互补,语义不变。 验证:新穿透矩阵 test_meow_ui_dispatch(M1 模态点下层不触发 / M2 弹层叠开两层层隔离 / M3 捕获手势滑出不误触 / M4 list 焦点点外释放)ALL PASS; 全量回归 widgets/ime_fix/pinyin/overlay_suppress/notif_gesture/sdk_fixes/sb_collapse/ p1p2p3_repro/box/anim/scroll/sdk_input_keys 全 PASS;交叉 -Wall -Wextra 0 warning。 meow_ui.a 新 md5:a9fcfb6eeb07ddbdd48039df4f8f18ce(主会话统一重链,本仓未重链 App)
119 lines
8.2 KiB
C
119 lines
8.2 KiB
C
/*
|
||
* meow_ui_input.h — SDK 输入层 / 生命周期(§6,B1)+ 通用长按(§6.3,2026-08-31)。
|
||
*
|
||
* 原 meow_ui.h 拆分的一部分(input 模块)。物理键为系统语义(SDK 不下发 on_key 映射为确认/exit),
|
||
* App 只响应触摸/滑动;事件循环阻塞到 meow_ui_request_exit。
|
||
*/
|
||
#ifndef MEOW_UI_INPUT_H
|
||
#define MEOW_UI_INPUT_H
|
||
|
||
#include "meow_ui_types.h"
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/* ============================================================ */
|
||
/* §6 输入层 / 生命周期(B1) */
|
||
/* ============================================================ */
|
||
typedef struct meow_ui_events {
|
||
void (*on_key)(int keycode, int is_down); /* M2.24:物理键为系统语义,SDK 不下发;回调保留(应用不映射键为确认/exit) */
|
||
void (*on_tap)(int x, int y); /* 触摸抬起落点 */
|
||
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 */
|
||
void meow_ui_request_exit(void); /* 请求退出(相当于返回/退出键) */
|
||
|
||
/* 顶部左滑进度条(视觉反馈):画一条 5px 高、从 x=0 到 x 的横条(主题 accent 色)。
|
||
* x<=0 时清掉此前画的条(填回背景色)。不发 flush,由调用方 meow_ui_flush_region 上屏。 */
|
||
void meow_ui_swipe_hint(int x);
|
||
|
||
/* ============================================================ */
|
||
/* §6.3 通用长按(2026-08-31) */
|
||
/* ============================================================ */
|
||
/* 控件按下 ≥ 阈值(默认 500ms,可配)未抬手、且按下后位移 ≤ 容忍(超移动/抬手取消)
|
||
* → 触发一次长按回调;坐标 = 触发时刻的按下点(App 据此 meow_widget_hit / meow_listview_hit
|
||
* 判定命中哪个控件/行,与 on_tap 同一套命中流程)。
|
||
*
|
||
* **opt-in**:注册回调(meow_ui_set_long_press_cb)才启用长按判定;未注册时行为与之前完全一致
|
||
* (按压不触发长按、抬手照常 TAP/SWIPE)——hello 等不使用长按的 App 零回归。
|
||
*
|
||
* 与 pressed 状态机共存:按压视觉态(MEOW_WS_PRESSED / 按住加深)不变;短按(未达阈值或
|
||
* 抬手先于阈值)仍正常触发 on_tap 点击;长按已触发后其抬手**不再产生 TAP/SWIPE**(SDK 抑制
|
||
* 点击,App 无需自行吞掉后续 tap)。
|
||
*
|
||
* 注册经独立 set 接口:**不做进 meow_ui_events**——避免给既有 App(hello/files)按位置初始化
|
||
* meow_ui_events 的写法在 -Wextra 下引入「缺省成员」告警(_missing-field-initializers),
|
||
* 保持 SDK 头向后兼容。 */
|
||
typedef void (*meow_long_press_fn)(int x, int y);
|
||
void meow_ui_set_long_press_cb(meow_long_press_fn cb); /* 注册/更换回调;NULL=取消 */
|
||
void meow_ui_set_long_press_ms(int ms); /* 长按阈值(ms);<=0 恢复默认 500 */
|
||
int meow_ui_long_press_ms(void); /* 当前阈值(默认 500) */
|
||
|
||
/* ============================================================ */
|
||
/* §6.4 事件分发形式化(2026-09-02)—— 统一「命中测试 / 捕获 / 消费 / 层间隔离 / 焦点」
|
||
*
|
||
* 背景:此前命中/捕获/消费/层间隔离各处 ad-hoc 补丁(模式层穿透、弹层叠黑、list 焦点残留、
|
||
* 同坐标二次消费)。本节把三层模型的形式化落地为**单一仲裁者**(SDK 内部实现,见
|
||
* devdocs/plan/SDK_VIEW_FRAMEWORK_DESIGN.md §A.7 命中分流 ③②① + 本 SDK 内部拓扑):
|
||
*
|
||
* 触摸沿 z 序**自顶向下**命中:模态层永远先审;某一层命中即消费(返回 consumed 才继续下探)。
|
||
* 某一次 touch-down 命中后,捕获直到 up;期间 move/up 只发给捕获目标(防滑出误触发下层)。
|
||
* 模态层存在时下层**完全收不到**事件——未命中面板的事件由模态背板消费,不透传。
|
||
*
|
||
* 兼容:**只追加、不删改既有签名**;未调用本节 API 的 App 行为与之前完全一致(命中/捕获仍
|
||
* 由 App 在 on_tap/on_touch_* 里自理)。hello/files/ncm/reader 等既有 App 只把 SDK 提供的
|
||
* 语义用于收敛 bug,不改正常交互。
|
||
*/
|
||
/* ---- 模态作用域(modal scope)----
|
||
* 一组自顶向下优先的叠层。dialog/sheet/App 自绘模态在「打开时 begin、关闭时 end」。
|
||
* 作用域存在(meow_ui_modal_active / depth>0)时,下层(view/widget)完全收不到触摸;
|
||
* 未命中面板的事件由背板消费(meow_ui_hit_topdown 恒返回模态哨兵)。支持嵌套(弹层叠开,
|
||
* depth 递增)。begin 返回作用域句柄;end 销栈(幂等,NULL 忽略)。 */
|
||
typedef struct meow_ui_modal meow_ui_modal;
|
||
meow_ui_modal *meow_ui_modal_begin(void); /* 打开模态层入栈;返回句柄(NULL=栈满/上限) */
|
||
void meow_ui_modal_end(meow_ui_modal *m); /* 关闭:弹到 m(含 m,一次性关上嵌套) */
|
||
int meow_ui_modal_active(void); /* 当前是否有任一模态作用域打开(depth>0) */
|
||
int meow_ui_modal_depth(void); /* 模态栈深(弹层叠开计数) */
|
||
|
||
/* ---- 自上而下命中 ---- */
|
||
/* 沿 z 序(模态最高优先,其次注册控件 z 降序;均含 owner==active+enable 过滤)返回第一个
|
||
* 命中绘制物(meow_widget* 或 meow_view*);无命中返回 NULL。模态存在时恒返回模态哨兵
|
||
* (背板消费,不透传)。供 gesture_begin/focus_tap/App 自绘模态命中统一使用。 */
|
||
void *meow_ui_hit_topdown(int x, int y);
|
||
/* meow_widget_hit 的公共别名(owner==active + enable + 矩形/自定义命中回调一致)。 */
|
||
int meow_ui_widget_hit(meow_widget *w, int x, int y);
|
||
|
||
/* ---- 手势捕获(gesture capture)----
|
||
* touch-down 命中目标捕获整个手势直到 up。捕获期 meow_ui_gesture_owner 恒返回捕获目标;
|
||
* 后续 move/up 只发给它(防滑出后误触发下层)。begin 返回捕获目标(未命中=NULL 不捕获);
|
||
* end/抬手释放。 */
|
||
void *meow_ui_gesture_begin(int x, int y); /* 命中+捕获;返回捕获目标(模态存在时=模态哨兵) */
|
||
void *meow_ui_gesture_owner(void); /* 当前手势捕获目标(无=NULL) */
|
||
void meow_ui_gesture_end(void); /* 手势结束(抬手/TAP/SWIPE 边沿):释放捕获 */
|
||
|
||
/* ---- 唯一焦点(focus,root 根治 list 焦点残留 / 滚动容器间转移)----
|
||
* 焦点持有者唯一。点非焦点区域 → 释放焦点并触发 lost 回调;滚动容器(list/scroll)在
|
||
* 获得焦点时 acquire,点外由 meow_ui_focus_tap 统一释放——「命中非 focus 区域 → 释放并通知」。
|
||
* 转移规则:某滚动容器 acquire 后,随后的纵向 swipe / 拖动滚该容器;任何落在该容器外的
|
||
* 触摸(hit != focus owner)释放焦点,回退到页面级滚动。 */
|
||
typedef void (*meow_ui_focus_lost_fn)(void *owner, void *ctx); /* 焦点被释放/转移时回调 */
|
||
void meow_ui_focus_set_lost_cb(meow_ui_focus_lost_fn cb, void *ctx); /* 注册/更换焦点丢失回调 */
|
||
void *meow_ui_focus_owner(void); /* 当前唯一焦点持有者(无=NULL) */
|
||
int meow_ui_focus_acquire(void *owner); /* 设为唯一焦点(覆盖旧焦点并触发其 lost) */
|
||
int meow_ui_focus_release(void *owner); /* owner==当前焦点才释放+通知(NULL=任意) */
|
||
void meow_ui_focus_tap(int x, int y); /* TAP 落点:命中焦点持有者→保位;否则释放+通知 */
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
#endif /* MEOW_UI_INPUT_H */
|