Files
MeowOS-SDK/include/meow_ui_input.h
T
AskaEth 63ac0fd7dc sdk: 事件分发形式化(§6.4)—— 统一命中/捕获/消费/层间隔离/焦点
根治「焦点/事件穿透」一类反复 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)
2026-09-02 00:57:58 +08:00

119 lines
8.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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 */