feat(sdk): 结构化列表控件 W_LISTVIEW (agent-186) — MEOW_APP_SDK §5.5 A②落地: 图标(内置folder/file符号或img)+标题+副标题+右侧操作(arrow/switch/check/more), 单选/多选/禁态, 行右1/3操作区命中, 视口裁剪, 全语义色; meow_ui_img_scaled 弱引用防二进制膨胀; files App 自绘列表完整迁移(长按留App层)
meow_ui.a ece0ecdb→4335c33a; hello 07b1ee37→07c0c5a5; files 6f6b0a6d→22b6b606 宿主: widgets(含4条listview新断言)/p1p2p3/ime_fix/test_fsx 全 PASS
This commit is contained in:
1 parent
3f57d7fd4f
commit
243148e440
8 files changed
+667
-155
No files matched your search
@@ -33,8 +33,10 @@ cd patch_v2/files_app && ./build_files.sh
|
||||
```
|
||||
|
||||
构建脚本仿 `hello_app/build_hello.sh`:`zig-mipsel-cc -Os -static -Wall -Wextra -I../sdk -I.`
|
||||
链 `../sdk/meow_ui.a "$SYSROOT/lib/libz.a" -lpthread`。**零触及 `patch_v2/sdk/`**(未新增/改任何 SDK 文件);
|
||||
`-Wall -Wextra` 0 warning。
|
||||
链 `../sdk/meow_ui.a "$SYSROOT/lib/libz.a" -lpthread`。本应用是 **§5.5 结构化列表(`meow_listview`)的
|
||||
首个消费方**(`patch_v2/sdk/meow_ui_widget.c` 新增 `W_LISTVIEW`:图标+标题+副标题+右侧操作+选中);
|
||||
`-Wall -Wextra` 0 warning。行滚动/选中/命中由 SDK 收口,App 只驱动 scroll/sel 并分发(行体=进目录/
|
||||
开预览;右侧 `⋯` 或长按→BottomSheet)。
|
||||
|
||||
## 部署
|
||||
|
||||
@@ -66,11 +68,14 @@ gcc -O2 -Wall -Wextra -DFSX_SD_ROOT='"/tmp/meowfsx_fsx_test"' -Ilogic \
|
||||
- 左缘左滑/返回钮:预览页→回目录;浏览器→上级目录;卡根再左滑=退出 App。
|
||||
- 物理键不映射(APPS.md §3.4.1):息屏/音量/媒体语义归系统/SDK。
|
||||
|
||||
## 已知限制 / SDK 能力缺口(未改 SDK,如需请走 agent 立项)
|
||||
## 已知限制 / SDK 能力说明
|
||||
|
||||
1. **SDK `meow_lazycolumn` 仅渲染单行 label**(`meow_list_source` 填文本),无法表达「图标+名称+大小/时间+右侧操作钮」
|
||||
的行布局。本 App 用 `meow_widget_custom_create` 自绘虚拟化列表满足该行版式;`fsx_scan_dir` 仍以有界数组缓存
|
||||
(`FSX_MAX_ENTRIES=1024`)承载数据,列表虚拟化只画可见行(`fsx_visible_range`)——大目录不一次全读进内存。
|
||||
1. **结构化列表(`meow_listview`)已落地**:`ui/browser.c` 的自绘 custom 列表已迁移到 SDK `W_LISTVIEW`
|
||||
(行数据源 `browser_src2` 填 `meow_list_item`)。图标(目录/文件内置符号)、名称、大小/时间副标题、
|
||||
右侧 `⋯` 操作钮、虚拟化、空态/页脚、禁用行不响应均由 SDK 收口;`fsx_scan_dir` 仍以有界数组缓存
|
||||
(`FSX_MAX_ENTRIES=1024`),列表虚拟化只画可见行——大目录不一次全读进内存。
|
||||
- **long-press 由 App 层保持**:SDK listview 无内建长按;`browser_tick` 的「按下→≥500ms 未拖动→
|
||||
BottomSheet」逻辑不依赖控件渲染,保留在 App 层(触摸命中用 `meow_listview_hit` 行号)。
|
||||
2. **中文字形**:SDK 默认 `common` 字库(≈4k)不含 CJK 全量;exFAT 中文文件名/中文文本预览在 common 档会显示缺字豆腐。
|
||||
预览页不声明 `fonts`(app.json 未加 `"fonts":"full"`/`"gbk"`),如需中文预览可在 app.json 声明大字号档。
|
||||
3. **预览仅文本**:按扩展名白名单 + 首 512 字节 NUL 嗅探判文本;二进制/图片无预览(提示「not a text file」)。
|
||||
|
||||
Binary file not shown.
+98
-144
@@ -1,12 +1,15 @@
|
||||
/* ui/browser.c — 目录浏览页(ACT_BROWSER,根页)view + 控制器。
|
||||
*
|
||||
* 职责:
|
||||
* - 持有目录态(g_cur 当前目录 / g_entries 缓存快照 / g_err 空态码 / g_scroll);
|
||||
* - 内容区用 meow_widget_custom_create 自绘「虚拟化列表」:只画可见行(fsx_visible_range),
|
||||
* 行=图标(目录/文件)+名称+大小/时间+右侧 ⋯ 操作钮;不一次全读进内存(entries 有界缓存);
|
||||
* - 空态/错误(无卡/空目录/权限)由 render_cb 内 app_draw_empty 画;
|
||||
* - 触摸:行 body=进目录/开预览;右侧操作钮或长按 → BottomSheet(删除/重命名/新建/属性);
|
||||
* - 滑动:LEFT=返回上级(App 层处理);UP/DOWN=列表滚动(本页);
|
||||
* - 持有目录态(g_cur 当前目录 / g_entries 缓存快照 / g_err 空态码);
|
||||
* - 内容区用 SDK 结构化列表(meow_listview,§5.5 W_LISTVIEW)虚拟化渲染
|
||||
* 「图标 + 名称 + 大小/时间 + 右侧 ⋯ 操作钮」行:行数据源 browser_src2 逐行填充
|
||||
* meow_list_item(icon_kind=目录/文件符号、副标题=大小/时间、right=⋯)。滚动/选中命中由
|
||||
* SDK 收口(视口裁剪、局部 dirty、disabled 不响应),App 只在 on_tap/touch_move 驱动
|
||||
* scroll/sel 并分发:行体=进目录/开预览;右侧 ⋯ 或长按 → BottomSheet(删除/重命名/新建/属性);
|
||||
* - 空态/错误(无卡/空目录/权限)与底部信息行用 meow_draw_custom_register 注册为非控件绘制物
|
||||
* (app_draw_empty / app_draw_footer)——列表视口不含页脚,行不压页脚;invalidate/paint_full
|
||||
* 自动重画它们;
|
||||
* - 写操作只经 logic(fsx_*),且在卡内校验后执行。
|
||||
*
|
||||
* 颜色全部读 meow_theme_get() 语义色(§8.1),不硬编码。
|
||||
@@ -25,8 +28,6 @@ static fsx_entry g_entries[FSX_MAX_ENTRIES];
|
||||
static int g_n; /* 当前目录项数 */
|
||||
static char g_cur[FSX_MAX_PATH];
|
||||
static int g_err; /* fsx_scan_dir 返回(0=ok,<0=空态码) */
|
||||
static int g_scroll; /* 列表滚动(px) */
|
||||
static int g_sel; /* 当前选中行(-1=无) */
|
||||
static int g_press_row; /* 按下行(-1=无);长按检测 */
|
||||
static uint64_t g_press_ms;
|
||||
static int g_press_tapped; /* 长按已消费本按 */
|
||||
@@ -35,8 +36,11 @@ static int g_dragging; /* 本次触摸起点是否在列表(拖
|
||||
static int g_drag_last_y; /* 拖拽上一帧 y */
|
||||
|
||||
static meow_widget *g_tb; /* TopAppBar(chrome) */
|
||||
static meow_widget *g_list; /* 内容区虚拟化列表(custom widget) */
|
||||
static app_rect_t r_content;
|
||||
static meow_widget *g_list; /* 内容区结构化列表(meow_listview,W_LISTVIEW) */
|
||||
static app_rect_t r_content; /* 顶部栏之下 → 屏底(含页脚带) */
|
||||
static app_rect_t r_list; /* 列表视口 = 内容区去掉页脚带(行不压页脚) */
|
||||
static int g_empty_id; /* 空态/错误绘制物(非控件) */
|
||||
static int g_footer_id; /* 底部信息行绘制物(非控件) */
|
||||
static char g_title[FSX_MAX_PATH];
|
||||
|
||||
/* 预览/操作辅助(前向声明) */
|
||||
@@ -59,123 +63,65 @@ static void browser_drag_reset(void)
|
||||
g_drag_last_y = 0;
|
||||
}
|
||||
|
||||
/* ---- 通用文本绘制:宽度限 maxw 内截断(超出加 "...")---- */
|
||||
static void browser_text_trunc(int x, int y, const char *s, int maxw, int size, uint32_t col)
|
||||
/* ---- 列表行数据源:逐行填 meow_list_item(图标/名称/大小时间/⋯ ),轻量不 IO(§5.5)---- */
|
||||
static int browser_src2(int idx, meow_list_item *out, void *ctx)
|
||||
{
|
||||
int full = meow_ui_text_width(s, size);
|
||||
if (full <= maxw) { meow_ui_text(x, y, s, size, col); return; }
|
||||
const char *dots = "...";
|
||||
int dw = meow_ui_text_width(dots, size);
|
||||
char tmp[FSX_MAX_NAME + 4];
|
||||
int i;
|
||||
for (i = 0; s[i] && i < FSX_MAX_NAME - 4; i++) {
|
||||
tmp[i] = s[i];
|
||||
tmp[i + 1] = '\0';
|
||||
if (meow_ui_text_width(tmp, size) > maxw - dw) break;
|
||||
}
|
||||
if (i >= FSX_MAX_NAME - 4) i = FSX_MAX_NAME - 4;
|
||||
memcpy(tmp + i, dots, 4);
|
||||
meow_ui_text(x, y, tmp, size, col);
|
||||
}
|
||||
|
||||
/* ---- 列表行图标:目录=filled 矩形+页签;文件=空心矩形+折角 ---- */
|
||||
static void browser_draw_icon(const meow_theme *th, int x, int y, int is_dir, int sel)
|
||||
{
|
||||
uint32_t col = sel ? 0xFFFFFF : (is_dir ? th->primary : th->onSurfaceVariant);
|
||||
if (is_dir) {
|
||||
meow_ui_fill_rect(x, y + 6, 26, 18, col); /* 主体 */
|
||||
meow_ui_fill_rect(x, y + 2, 12, 6, col); /* 页签 */
|
||||
(void)ctx;
|
||||
if (idx < 0 || idx >= g_n) return 0;
|
||||
fsx_entry *e = &g_entries[idx];
|
||||
static char sub[FSX_MAX_NAME + 48]; /* 行内瞬时字符串(绘制即刻用,重复填充无碍) */
|
||||
char sz[32], tm[32];
|
||||
out->icon_kind = e->is_dir ? MEOW_LIST_ICON_FOLDER : MEOW_LIST_ICON_FILE;
|
||||
out->title = e->name;
|
||||
if (e->is_dir) {
|
||||
fsx_fmt_time(e->mtime, tm, sizeof tm);
|
||||
snprintf(sub, sizeof sub, "Folder %s", tm);
|
||||
} else {
|
||||
meow_ui_draw_rect(x + 4, y + 4, 18, 22, col); /* 页体 */
|
||||
meow_ui_fill_rect(x + 4, y + 4, 7, 4, col); /* 文件名横条 */
|
||||
fsx_fmt_size(e->size, sz, sizeof sz);
|
||||
fsx_fmt_time(e->mtime, tm, sizeof tm);
|
||||
snprintf(sub, sizeof sub, "%s %s", sz, tm);
|
||||
}
|
||||
out->subtitle = sub;
|
||||
out->right = MEOW_LIST_RIGHT_MORE; /* 右侧 ⋯ 详情钮(行右 1/3 判操作) */
|
||||
out->on = 0;
|
||||
out->enabled = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 行区视口高(底部留页脚,行不压到页脚)。 */
|
||||
static int browser_vp_h(void) { return r_content.h - APP_FOOTER_H; }
|
||||
|
||||
/* ---- 列表 render_cb:虚拟化画可见行 + 空态 + 页脚 ---- */
|
||||
static void browser_render(meow_widget *w, const meow_theme *th, void *ctx)
|
||||
/* ---- 空态/错误绘制物(非控件;非空态时无输出)----
|
||||
* ⚠️ custom 绘制物无 owner 闸门(不同于 widget,见 meow_ui_draw.c draw_order):
|
||||
* 必须显式按「本页是否为当前 active activity」守门,否则切到 viewer 页会跨页泄漏绘制。 */
|
||||
static void browser_draw_empty(void *ctx)
|
||||
{
|
||||
(void)w; (void)ctx;
|
||||
/* 空态/错误 */
|
||||
if (g_err != 0) {
|
||||
if (g_err == FSX_ERR_NOENT)
|
||||
app_draw_empty(r_content, "NO STORAGE CARD", "insert a TF card / mount sd_0");
|
||||
else if (g_err == FSX_ERR_EACCES)
|
||||
app_draw_empty(r_content, "ACCESS DENIED", "cannot read this folder");
|
||||
else
|
||||
app_draw_empty(r_content, "CANNOT OPEN", "unexpected error");
|
||||
return;
|
||||
}
|
||||
if (g_n == 0) {
|
||||
app_draw_empty(r_content, "EMPTY FOLDER", g_cur);
|
||||
app_draw_footer(r_content, "0 items");
|
||||
return;
|
||||
}
|
||||
|
||||
int vp = browser_vp_h();
|
||||
int first, last;
|
||||
fsx_visible_range(g_n, APP_ROW_H, g_scroll, vp, &first, &last);
|
||||
int thh = meow_ui_text_height(th->font_body);
|
||||
int namew = r_content.w - APP_PAD - APP_ICON_W - APP_PAD - APP_OP_W - APP_PAD; /* 名称区 */
|
||||
char size[32], tm[32];
|
||||
|
||||
for (int i = first; i < last; i++) {
|
||||
fsx_entry *e = &g_entries[i];
|
||||
int ry = r_content.y + (i * APP_ROW_H - g_scroll);
|
||||
if (ry + APP_ROW_H <= r_content.y) continue;
|
||||
if (ry >= r_content.y + vp) break;
|
||||
|
||||
int hilite = (i == g_sel || i == g_press_row);
|
||||
if (hilite) meow_ui_fill_rect(r_content.x, ry, r_content.w, APP_ROW_H, th->primary);
|
||||
|
||||
browser_draw_icon(th, r_content.x + APP_PAD, ry + (APP_ROW_H - 24) / 2, e->is_dir, hilite);
|
||||
|
||||
uint32_t namecol = hilite ? 0xFFFFFF : th->onSurface;
|
||||
uint32_t subcol = hilite ? 0xFFFFFF : th->dim;
|
||||
browser_text_trunc(r_content.x + APP_PAD + APP_ICON_W + APP_PAD, ry + 6,
|
||||
e->name, namew, th->font_body, namecol);
|
||||
|
||||
if (e->is_dir) {
|
||||
fsx_fmt_time(e->mtime, tm, sizeof tm);
|
||||
snprintf(size, sizeof size, "Folder %s", tm);
|
||||
} else {
|
||||
fsx_fmt_size(e->size, size, sizeof size);
|
||||
fsx_fmt_time(e->mtime, tm, sizeof tm);
|
||||
snprintf(size, sizeof size, "%s %s", size, tm);
|
||||
}
|
||||
browser_text_trunc(r_content.x + APP_PAD + APP_ICON_W + APP_PAD, ry + 6 + thh + 2,
|
||||
size, namew, 20, subcol);
|
||||
|
||||
/* 右侧 ⋯ 操作钮(三个点) */
|
||||
int ox = r_content.x + r_content.w - APP_OP_W / 2 - 1;
|
||||
int oy = ry + APP_ROW_H / 2 - 8;
|
||||
for (int d = 0; d < 3; d++)
|
||||
meow_ui_fill_rect(ox, oy + d * 6, 4, 4, hilite ? 0xFFFFFF : th->onSurfaceVariant);
|
||||
|
||||
/* 分隔线 */
|
||||
int sy = ry + APP_ROW_H - 1;
|
||||
if (sy >= r_content.y && sy < r_content.y + r_content.h)
|
||||
meow_ui_fill_rect(r_content.x, sy, r_content.w, 1, th->outlineVariant);
|
||||
}
|
||||
(void)ctx;
|
||||
if (meow_ui_activity_active() != ACT_BROWSER) return; /* 非本页 active 不画(防跨页泄漏) */
|
||||
if (g_err == 0 && g_n != 0) return;
|
||||
if (g_err == FSX_ERR_NOENT)
|
||||
app_draw_empty(r_list, "NO STORAGE CARD", "insert a TF card / mount sd_0");
|
||||
else if (g_err == FSX_ERR_EACCES)
|
||||
app_draw_empty(r_list, "ACCESS DENIED", "cannot read this folder");
|
||||
else if (g_err)
|
||||
app_draw_empty(r_list, "CANNOT OPEN", "unexpected error");
|
||||
else
|
||||
app_draw_empty(r_list, "EMPTY FOLDER", g_cur);
|
||||
}
|
||||
|
||||
/* ---- 底部信息行绘制物(非控件):项数/路径/截断提示 ---- */
|
||||
static void browser_draw_footer(void *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
if (meow_ui_activity_active() != ACT_BROWSER) return; /* 非本页 active 不画(防跨页泄漏) */
|
||||
if (g_err != 0) return;
|
||||
char foot[96];
|
||||
if (g_n >= FSX_MAX_ENTRIES)
|
||||
if (g_n == 0)
|
||||
snprintf(foot, sizeof foot, "0 items");
|
||||
else if (g_n >= FSX_MAX_ENTRIES)
|
||||
snprintf(foot, sizeof foot, "%d items (showing first %d)", g_n, FSX_MAX_ENTRIES);
|
||||
else
|
||||
snprintf(foot, sizeof foot, "%d items %s", g_n, g_cur);
|
||||
app_draw_footer(r_content, foot);
|
||||
}
|
||||
|
||||
/* 列表 hit_cb:落点是否在内容区(行判定在 on_tap 里做)。 */
|
||||
static int browser_hit(meow_widget *w, int x, int y, void *ctx)
|
||||
{
|
||||
(void)w; (void)ctx;
|
||||
return x >= r_content.x && x < r_content.x + r_content.w &&
|
||||
y >= r_content.y && y < r_content.y + r_content.h;
|
||||
}
|
||||
|
||||
/* ---- 导航 ---- */
|
||||
|
||||
static void browser_update_title(void)
|
||||
@@ -192,12 +138,15 @@ void browser_reload(void)
|
||||
int r = fsx_scan_dir(g_cur, g_entries, FSX_MAX_ENTRIES, &g_n);
|
||||
g_err = (r < 0) ? r : 0;
|
||||
if (g_err != 0) g_n = 0;
|
||||
g_scroll = 0;
|
||||
g_sel = -1;
|
||||
if (g_list) {
|
||||
meow_listview_set_count(g_list, g_n); /* 行数动态更新(§5.5) */
|
||||
meow_listview_set_scroll(g_list, 0);
|
||||
meow_listview_set_sel(g_list, -1);
|
||||
}
|
||||
browser_update_title();
|
||||
const meow_theme *th = meow_theme_get();
|
||||
meow_ui_set_page_bg(th->bg);
|
||||
meow_ui_paint_full();
|
||||
meow_ui_paint_full(); /* 重画全注册绘制物(行/空态/页脚/topbar) */
|
||||
}
|
||||
|
||||
void browser_enter_dir(const char *path)
|
||||
@@ -371,10 +320,9 @@ int browser_on_tap(int x, int y)
|
||||
}
|
||||
if (g_err != 0 || g_n == 0) return 0;
|
||||
if (meow_widget_hit(g_list, x, y)) {
|
||||
int op = (x >= r_content.x + r_content.w - APP_OP_W);
|
||||
int row = (y - r_content.y + g_scroll) / APP_ROW_H;
|
||||
if (row < 0 || row >= g_n) { browser_drag_reset(); return 1; }
|
||||
g_sel = row;
|
||||
int row = meow_listview_hit(g_list, x, y); /* 行号(含滚动换算;禁用行 -1) */
|
||||
if (row < 0) { browser_drag_reset(); return 1; } /* 命中列表但无有效行(空隙) */
|
||||
int op = meow_listview_hit_op(g_list, x, y); /* 行右 1/3 = 操作区 */
|
||||
if (op) browser_open_sheet(row);
|
||||
else {
|
||||
fsx_entry *e = &g_entries[row];
|
||||
@@ -393,14 +341,15 @@ void browser_touch_down(int x, int y)
|
||||
browser_drag_reset();
|
||||
if (g_err != 0 || g_n == 0) return;
|
||||
if (meow_widget_hit(g_list, x, y)) {
|
||||
int row = (y - r_content.y + g_scroll) / APP_ROW_H;
|
||||
if (row >= 0 && row < g_n) {
|
||||
int row = meow_listview_hit(g_list, x, y);
|
||||
if (row >= 0) {
|
||||
g_press_row = row;
|
||||
g_press_ms = meow_ui_time_ms();
|
||||
meow_listview_set_sel(g_list, row); /* 按下高亮(§5.8.3 selected) */
|
||||
}
|
||||
g_dragging = 1;
|
||||
g_drag_last_y = y;
|
||||
meow_ui_invalidate(r_content.x, r_content.y, r_content.w, r_content.h);
|
||||
meow_ui_invalidate(r_list.x, r_list.y, r_list.w, r_list.h);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,19 +361,18 @@ void browser_touch_move(int x, int y)
|
||||
int d = y - g_drag_last_y;
|
||||
if (d < 0) d = -d;
|
||||
if (d > 0) g_press_delta += d;
|
||||
if (g_press_delta > 10) g_press_row = -1;
|
||||
if (g_press_delta > 10) {
|
||||
g_press_row = -1;
|
||||
meow_listview_set_sel(g_list, -1); /* 拖动取消按下高亮 */
|
||||
}
|
||||
}
|
||||
int dy = y - g_drag_last_y;
|
||||
g_drag_last_y = y;
|
||||
if (dy == 0) return;
|
||||
int ns = g_scroll - dy;
|
||||
int ms = fsx_max_scroll(g_n, APP_ROW_H, browser_vp_h());
|
||||
if (ns < 0) ns = 0;
|
||||
if (ns > ms) ns = ms;
|
||||
if (ns != g_scroll) {
|
||||
g_scroll = ns;
|
||||
meow_ui_invalidate(r_content.x, r_content.y, r_content.w, r_content.h);
|
||||
}
|
||||
int before = meow_listview_scroll(g_list);
|
||||
meow_listview_scroll_by(g_list, -dy); /* set_scroll 内部 clamp 到 [0,max] */
|
||||
if (meow_listview_scroll(g_list) != before)
|
||||
meow_ui_invalidate(r_list.x, r_list.y, r_list.w, r_list.h);
|
||||
}
|
||||
|
||||
void browser_swipe(int dir, int dist)
|
||||
@@ -432,14 +380,10 @@ void browser_swipe(int dir, int dist)
|
||||
if (dir != MEOW_SWIPE_UP && dir != MEOW_SWIPE_DOWN) return;
|
||||
if (g_err != 0 || g_n == 0) return;
|
||||
int del = (dir == MEOW_SWIPE_UP) ? dist : -dist; /* 上滑→显示更低内容 */
|
||||
int ns = g_scroll + del;
|
||||
int ms = fsx_max_scroll(g_n, APP_ROW_H, browser_vp_h());
|
||||
if (ns < 0) ns = 0;
|
||||
if (ns > ms) ns = ms;
|
||||
if (ns != g_scroll) {
|
||||
g_scroll = ns;
|
||||
meow_ui_invalidate(r_content.x, r_content.y, r_content.w, r_content.h);
|
||||
}
|
||||
int before = meow_listview_scroll(g_list);
|
||||
meow_listview_scroll_by(g_list, del);
|
||||
if (meow_listview_scroll(g_list) != before)
|
||||
meow_ui_invalidate(r_list.x, r_list.y, r_list.w, r_list.h);
|
||||
}
|
||||
|
||||
/* on_tick:长按(≥500ms 未拖动)→ 打开该行 BottomSheet。 */
|
||||
@@ -460,14 +404,22 @@ static void browser_page_create(meow_view *v, void *ctx)
|
||||
(void)v;
|
||||
((browser_ctrl *)ctx)->view = v;
|
||||
r_content = app_content_rect();
|
||||
r_list = AR(r_content.x, r_content.y, r_content.w, r_content.h - APP_FOOTER_H);
|
||||
if (r_list.h < 0) r_list.h = 0;
|
||||
strncpy(g_cur, FSX_SD_ROOT, sizeof g_cur - 1);
|
||||
g_cur[sizeof g_cur - 1] = '\0';
|
||||
g_tb = app_topbar_create("Files", 1, "NEW");
|
||||
g_list = meow_widget_custom_create(r_content.x, r_content.y, r_content.w, r_content.h,
|
||||
browser_render, browser_hit, NULL);
|
||||
g_sel = -1;
|
||||
/* 结构化列表:行数据源 browser_src2;单选模式(行高亮用 selected,§5.8.3) */
|
||||
g_list = meow_listview_create(r_list.x, r_list.y, r_list.w, r_list.h, 0,
|
||||
browser_src2, NULL, MEOW_LIST_SEL_SINGLE);
|
||||
meow_listview_set_item_h(g_list, APP_ROW_H);
|
||||
/* 空态/页脚注册为非控件绘制物(列表视口不含页脚;invalidate/paint_full 自动重画) */
|
||||
g_empty_id = meow_draw_custom_register(1, r_list.x, r_list.y, r_list.w, r_list.h,
|
||||
browser_draw_empty, NULL);
|
||||
g_footer_id = meow_draw_custom_register(1, r_content.x, r_content.y + r_content.h - APP_FOOTER_H,
|
||||
r_content.w, APP_FOOTER_H, browser_draw_footer, NULL);
|
||||
g_press_row = -1;
|
||||
browser_reload();
|
||||
browser_reload(); /* 扫首个目录(count/scroll/sel 由 reload 写入 listview) */
|
||||
}
|
||||
|
||||
static void browser_page_enter(meow_view *v, void *ctx)
|
||||
@@ -488,6 +440,8 @@ static void browser_page_leave(meow_view *v, void *ctx)
|
||||
static void browser_page_destroy(meow_view *v, void *ctx)
|
||||
{
|
||||
(void)v; (void)ctx;
|
||||
if (g_empty_id) { meow_draw_unregister(g_empty_id); g_empty_id = 0; }
|
||||
if (g_footer_id) { meow_draw_unregister(g_footer_id); g_footer_id = 0; }
|
||||
if (g_list) { meow_widget_free(g_list); g_list = NULL; }
|
||||
if (g_tb) { meow_widget_free(g_tb); g_tb = NULL; }
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user