diff --git a/meow_ui.h b/meow_ui.h index bca5b25..bc17e86 100644 --- a/meow_ui.h +++ b/meow_ui.h @@ -175,6 +175,24 @@ void meow_ui_set_fullscreen(int on); * 全屏/隐藏时返回 0。 */ int meow_ui_statusbar_height(void); +/* ---- 状态栏「上收出屏只留提示线」(collapsed,草案/待定案——本批仅加声明)---- + * 供全屏调用:把状态栏内容上移出屏顶、只留一条「提示线」高度(可下拉还原)。 + * 与 meow_ui_set_fullscreen(1) 的关系:fullscreen 隐藏整条状态栏(沉浸,inset=0); + * collapsed 仍视状态栏为「激活」,左缘返回手势(SWIPE_LEFT)与顶部伸缩提示线 + * (meow_ui_swipe_hint)依旧生效,只是其内容上收、仅留提示线——用于「不完全沉浸」, + * 保留一处可下拉拉出状态栏的入口。顶部下滑热区 meow_ui_statusbar_hotzone() 在全屏时缩小。 + * 实现位置:meow_ui_gfx.c(基座让 on 生效:true→只画提示线/内容上移);逐帧过渡动画 + * (上收/恢复插值)为待定案,后续代理可经 meow_ui_statusbar_animate(ms) 补齐。 */ +void meow_ui_statusbar_set_collapsed(int on); /* on=1 上收(内容上移出屏、只留提示线);on=0 恢复 */ +int meow_ui_statusbar_collapsed(void); /* 当前是否处于「上收」目标(1=是) */ +void meow_ui_statusbar_animate(int ms); /* TBD:上收/恢复过渡时长(ms);占位,后续代理实现插值 */ + +/* 顶部下滑热区(下拉展开状态栏/通知)高度(px)。 + * 非全屏=整条状态栏可视高度;全屏时缩小到 MEOW_SBAR_HOTZONE_FS(保留一处下拉入口)。 + * 返回「可用于起始下拉」的热区高度;由输入层/后续代理据此判定下拉起点。 */ +int meow_ui_statusbar_hotzone(void); +#define MEOW_SBAR_HOTZONE_FS 4 /* 全屏时缩小后的顶部热区高度(px,草案值) */ + /* ============================================================ */ /* §7 动画/时间(B1 最小可用) */ /* ============================================================ */ @@ -309,6 +327,27 @@ int meow_notify_has_active(void); /* 1=尚有活动通知,0=无 * const char *meow_notify_urgency_str(meow_notify_urgency u); const char *meow_notify_category_str(meow_notify_category cat); +/* ============================================================ */ +/* §10 字体分层 / 大字库(草案/待定案 —— 本批仅加声明/枚举/宏,不实现) */ +/* ============================================================ */ +/* 字体「档位」概念:launcher 按 app.json 声明的字库档("fonts":<档>)向 app 注入 + * MEOW_FONT_TIER=<0|1|2>;SDK(meow_ui_gfx / 后续 meow_font 大字库加载)据档选库: + * MEOW_FONT_TIER_COMMON=0 默认 4K 常用字库; + * MEOW_FONT_TIER_GBK =1 大字库 GBK; + * MEOW_FONT_TIER_FULL =2 Full(全字符集)。 + * 后续 font 域代理据此在 meow_font.c / gen_font.py 实现大字库生成与按档装载。 + * 既有 meow_font_* 符号(class_of/has_glyph/load_slot/config_multifont…)保留不动; + * 大字库装载以 set_tier/tier 作为入口,或由后续代理按 meow_font_config_multifont 扩展。 */ +#define MEOW_FONT_TIER_COMMON 0 +#define MEOW_FONT_TIER_GBK 1 +#define MEOW_FONT_TIER_FULL 2 + +/* 设当前字体档(0/1/2,越界 clamp 到合法值),返回生效档。 + * 基座在 meow_ui_gfx.c 定义(只存档 + 触发重载请求);真正按档装载大字库由后续 font 代理实现。 */ +int meow_font_set_tier(int tier); +/* 当前字体档(初始 = MEOW_FONT_TIER_COMMON;meow_ui_init 时读 MEOW_FONT_TIER env 对齐)。 */ +int meow_font_tier(void); + #ifdef __cplusplus } #endif diff --git a/meow_ui_gfx.c b/meow_ui_gfx.c index 83a6abe..c429599 100644 --- a/meow_ui_gfx.c +++ b/meow_ui_gfx.c @@ -63,6 +63,24 @@ static int g_dirty_full = 1; static char *g_dump_path = NULL; +/* ---- 状态栏「上收出屏只留提示线」(collapsed,基座)---- + * g_sbar_collapsed = 目标态(1=上收);g_sbar_collapse_h = 当前实际上收偏移 + * (0..MUI_SBAR_H:0=完全展开,MUI_SBAR_H=内容全部上移出屏、仅留提示线)。 + * 基座让 meow_ui_statusbar_set_collapsed(on) 立即生效(true→只画提示线/内容上移); + * 过渡(每帧插值)留待后续代理或经 meow_ui_statusbar_animate(ms) 启用的简易线性逼近。 */ +#define MUI_SBAR_HINT_H 6 /* 上收状态下顶部提示线高(px) */ +static int g_sbar_collapsed = 0; /* 目标:1=上收 */ +static int g_sbar_collapse_h = 0; /* 当前上收偏移(0..MUI_SBAR_H) */ +static int g_sbar_collapse_anim_ms = 0; /* 过渡时长(0=瞬切);meow_ui_statusbar_animate 设置 */ +static int g_sbar_collapse_from_h = 0; /* 本次过渡起点偏移 */ +static unsigned long long g_sbar_collapse_start_ms = 0; /* 本次过渡起始(monotonic ms) */ + +/* ---- 字体档位(MEOW_FONT_TIER,基座存档 + 公共 get/set)---- + * launcher 按 app.json "fonts":<档> 注入 MEOW_FONT_TIER=<0|1|2>;SDK 读入并以 + * meow_font_set_tier/tier 暴露。真正按档装载大字库(GBK/Full)由后续 font 代理在 + * meow_font.c / meow_gfx_load_multifont 挂钩实现——此处仅记录档位不换库。 */ +static int g_font_tier = MEOW_FONT_TIER_COMMON; + /* ---- 帧率调度(A-1/A-2:从 launcher M2.16 迁入,刷新率调度归口 SDK)---- * meow_ui_flush()/present() 的 fps 档位与自适应降档 governor。launcher 不再自带 * 一套帧率门控;launcher 与 app 统一走这里 → 全局观感/刷新率一致。 @@ -499,6 +517,71 @@ static void dump_ppm(void) fclose(f); } +/* ================= 状态栏「上收出屏只留提示线」(collapsed,基座) ================= */ +/* 每帧推进上收/恢复:向目标偏移(collapsed→MUI_SBAR_H、否则→0)线性逼近; + * 无过渡时长(g_sbar_collapse_anim_ms<=0)= 一步到位(基座默认)。动画插值骨架在此, + * 后续代理可换缓动/每帧步进。 */ +static void statusbar_collapse_step(void) +{ + int tgt = g_sbar_collapsed ? MUI_SBAR_H : 0; + if (g_sbar_collapse_h == tgt) return; + if (g_sbar_collapse_anim_ms <= 0) { g_sbar_collapse_h = tgt; return; } + unsigned long long now = now_ms(); + unsigned long long el = now - g_sbar_collapse_start_ms; + int span = g_sbar_collapse_anim_ms; + if ((int)el >= span) { g_sbar_collapse_h = tgt; return; } + int delta = tgt - g_sbar_collapse_from_h; + g_sbar_collapse_h = g_sbar_collapse_from_h + + (int)((long long)delta * (long long)el / (long long)span); +} + +/* 上收中是否需要逐帧重画(动画未达目标位置时置位,强制每帧推进)。 */ +static int statusbar_collapse_in_transition(void) +{ + int tgt = g_sbar_collapsed ? MUI_SBAR_H : 0; + return g_sbar_collapse_h != tgt; +} + +/* 上收状态栏:内容整体上移出屏(不画时钟/电量),仅在顶部画一条「提示线」。 + * 与 meow_ui_swipe_hint(左滑返回进度条,5px,动态)不同:此线是「状态栏可下拉还原」的 + * 常驻提示(主题强调色下边缘)。原状态栏整块清回主题背景,避免残影(app inset 之内)。 */ +static void statusbar_collapsed_render(int h) +{ + (void)h; + const meow_theme *th = meow_theme_get(); + int w = meow_ui_width(); + /* h 越大内容越贴顶;基座:折叠态整块清底 + 顶部提示线(提示线下缘=主题 accent) */ + meow_ui_fill_rect(0, 0, w, MUI_SBAR_H, th->bg); + meow_ui_fill_rect(0, MUI_SBAR_HINT_H - 1, w, 1, th->accent); +} + +/* ---- 公共 API(基座实现;动画插值骨架见 statusbar_collapse_step)---- */ +void meow_ui_statusbar_set_collapsed(int on) +{ + int tgt = on ? 1 : 0; + if (tgt == g_sbar_collapsed) return; + g_sbar_collapsed = tgt; + g_sbar_collapse_from_h = g_sbar_collapse_h; + g_sbar_collapse_start_ms = now_ms(); + dirty_full(); /* 顶栏显隐/上收变化 → 全帧重画 */ +} + +int meow_ui_statusbar_collapsed(void) { return g_sbar_collapsed; } + +void meow_ui_statusbar_animate(int ms) +{ + g_sbar_collapse_anim_ms = (ms > 0) ? ms : 0; /* 0=瞬切(基座默认) */ +} + +int meow_ui_statusbar_hotzone(void) +{ + /* 非全屏=整条状态栏可视;全屏时缩小到 MEOW_SBAR_HOTZONE_FS(保留一处下拉入口)。 + * 上收(collapsed、非全屏)时热区=提示线下缘高度。全屏态由 meow_ui_sys 维护。 */ + if (meow_ui_sys_statusbar_active()) + return g_sbar_collapsed ? MUI_SBAR_HINT_H : MUI_SBAR_H; + return MEOW_SBAR_HOTZONE_FS; +} + /* 顶部状态栏条是否被本次脏区触及(含整帧):触及或数据已变(force)才需要重画顶条。 */ static int statusbar_touched(void) { @@ -519,13 +602,20 @@ static void present(void) if (!g_refresh_always_max) { if (g_last_present_ms && now - g_last_present_ms < (unsigned long long)period) return; } - /* 全局状态栏(A-0):上屏前把状态栏画进 back_buf 顶部 MUI_SBAR_H 行(z 最顶)。 + /* 全局状态栏(A-0)+ 上收(collapsed)基座:上屏前把状态栏画进 back_buf 顶部 MUI_SBAR_H 行。 * 全屏/隐藏时 active()==0 不画(沉浸),app 内容可占满整屏。触发条件: - * 数据已变(1s 轻刷/显隐切换) 或 顶条被 app 内容触及(含整帧清+绘,未 inset 时被顶条覆盖)。 - * 无需每帧重画——app 局部刷新不触及顶条时,back_buf/screen 顶条保持不变,省一次 blit。 */ + * 数据已变(1s 轻刷/显隐切换) / 顶条被 app 内容触及 / 上收动画未到位(逐帧推进)。 + * 展开态仍走 meow_ui_sys_statusbar_render()(meow_ui_sys.c 不改,留给后续代理); + * 上收折叠态(collapse_h>=MUI_SBAR_H)由基座只画提示线(内容上移出屏)。 */ if (meow_ui_sys_statusbar_active() && - (meow_ui_sys_statusbar_force() || statusbar_touched())) - meow_ui_sys_statusbar_render(); + (meow_ui_sys_statusbar_force() || statusbar_touched() || + statusbar_collapse_in_transition())) { + statusbar_collapse_step(); + if (g_sbar_collapse_h >= MUI_SBAR_H) + statusbar_collapsed_render(g_sbar_collapse_h); + else + meow_ui_sys_statusbar_render(); + } if (g_fb_mem) { ioctl(g_fb_fd, FBIO_WAITFORVSYNC, 0); /* 等垂直同步 */ unsigned long long tbg = now_ms(); @@ -575,6 +665,23 @@ unsigned long long meow_ui_frames_presented(void) { return g_frames_presented; } int meow_gfx_is_device(void) { return g_fb_mem != NULL; } int meow_gfx_mfont_ready(void) { return g_mfont_ready; } +/* ---- 字体档位(公共 API,基座定义)---- + * 注意:meow_font_tier()/meow_font_set_tier() 已在 meow_ui_gfx.c 定义(符号唯一), + * 后续 font 代理勿在 meow_font.c 重复定义;可在 meow_font.c 实现「按档装载大字库」并在 + * meow_font_set_tier 的钩子处唤起(见下)。 */ +int meow_font_tier(void) { return g_font_tier; } + +int meow_font_set_tier(int tier) +{ + if (tier < MEOW_FONT_TIER_COMMON) tier = MEOW_FONT_TIER_COMMON; + if (tier > MEOW_FONT_TIER_FULL) tier = MEOW_FONT_TIER_FULL; + if (tier == g_font_tier) return g_font_tier; + g_font_tier = tier; + /* TBD(基座):真正按档装载大字库(meow_font_load_tier 或 config_multifont 扩展)由 + * 后续 font 代理在 meow_font.c 实现;此处仅记录档位 + 标记重载(下一轮 load 读 tier)。 */ + return g_font_tier; +} + /* ================= 初始化/销毁 ================= */ /* 字库加载优先级(M2.x): * 1) 环境变量 MEOW_FONT_PNG/MEOW_FONT_IDX(launcher 注入的「当前生效字体」= 系统 font.file) @@ -584,6 +691,16 @@ int meow_gfx_mfont_ready(void) { return g_mfont_ready; } * 全失败 → g_mfont_ready=0,text_draw 回退 5x7 点阵(系统不死)。 */ static int load_font(void) { + /* 字体档位(MEOW_FONT_TIER env,由 launcher 按 app.json "fonts" 注入): + * 0=默认4K常用 1=大字库GBK 2=Full。基座只读入存档 + 对齐公共 get/set; + * 真正按档装载大字库(GBK/Full 的字库路径/面)由后续 font 代理在 meow_font.c / + * meow_gfx_load_multifont 挂钩实现——此处仍先装载默认(tier0)字库,档位仅记录。 */ + const char *etr = getenv("MEOW_FONT_TIER"); + if (etr && etr[0]) { + g_font_tier = atoi(etr); + if (g_font_tier < MEOW_FONT_TIER_COMMON) g_font_tier = MEOW_FONT_TIER_COMMON; + if (g_font_tier > MEOW_FONT_TIER_FULL) g_font_tier = MEOW_FONT_TIER_FULL; + } const char *ep = getenv("MEOW_FONT_PNG"); const char *ei = getenv("MEOW_FONT_IDX"); if (ep && ep[0] && ei && ei[0] && meow_font_load(ep, ei) == 0) return 1; @@ -677,6 +794,11 @@ int meow_ui_init(const meow_ui_opts *opts) g_init_ok = 1; dirty_full(); meow_ui_sys_init(); /* A-0:系统状态读取器首刷(电量/WiFi/蓝牙/音量),状态栏就绪 */ + /* 全屏预置(基座):launcher 按 app.json "fullscreen" 注入 MEOW_FULLSCREEN=1,SDK 据此 + * 预先设全屏(隐藏整条状态栏/沉浸,statusbar_active()==0、statusbar_height()==0)。 + * 未注入/非"1"=非全屏(默认)。放在 sys_init 后使首帧即为全屏态。 */ + const char *efs = getenv("MEOW_FULLSCREEN"); + if (efs && efs[0] == '1') meow_ui_set_fullscreen(1); return 0; }