meow_ui.a→3e0ee8f7 launcher→9d0efc2f hello→9609ea6e files→90f5cf07 ncm→be8be221(均已上卡); 真机截图证据 .tmp/meowbug/fix1.png+fix_b2_after.png
468 lines
18 KiB
C
468 lines
18 KiB
C
/* ui/browser.c — 目录浏览页(ACT_BROWSER,根页)view + 控制器。
|
||
*
|
||
* 职责:
|
||
* - 持有目录态(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;**长按=SDK 通用长按事件
|
||
* (§6.3,≥500ms 未抬手且位移≤容忍)** 由 SDK 判定后经 on_long_press 回调带坐标交给本页,
|
||
* App 据此行命中打开同一 BottomSheet(App 层长按计时逻辑已删,交给 SDK);
|
||
* - 空态/错误(无卡/空目录/权限)与底部信息行用 meow_draw_custom_register 注册为非控件绘制物
|
||
* (app_draw_empty / app_draw_footer)——列表视口不含页脚,行不压页脚;invalidate/paint_full
|
||
* 自动重画它们;
|
||
* - 写操作只经 logic(fsx_*),且在卡内校验后执行。
|
||
*
|
||
* 颜色全部读 meow_theme_get() 语义色(§8.1),不硬编码。
|
||
*/
|
||
#include "files_internal.h"
|
||
|
||
#include <errno.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
|
||
typedef struct { meow_view *view; int active; } browser_ctrl;
|
||
static browser_ctrl g_bc;
|
||
|
||
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_press_row; /* 按下行(-1=无);按下高亮 + 拖动识别(长按已交 SDK §6.3) */
|
||
static int g_press_delta; /* 按下后累计位移(>阈值清除按下高亮) */
|
||
static int g_dragging; /* 本次触摸起点是否在列表(拖动滚列表) */
|
||
static int g_drag_last_y; /* 拖拽上一帧 y */
|
||
|
||
static meow_widget *g_tb; /* TopAppBar(chrome) */
|
||
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];
|
||
|
||
/* 预览/操作辅助(前向声明) */
|
||
static void browser_open_sheet(int row);
|
||
void browser_on_long_press(int x, int y); /* 跨 TU(app.c 经 §6.3 回调调用);files_internal.h */
|
||
static void browser_enter_dir(const char *path);
|
||
static void browser_open_file(const char *path);
|
||
static void browser_rename_entry(const fsx_entry *e);
|
||
static void browser_delete_entry(const fsx_entry *e);
|
||
static void browser_props_entry(const fsx_entry *e);
|
||
static void browser_new_folder_in(const char *dirpath);
|
||
static void browser_report_ret(int r);
|
||
|
||
/* 本轮按下状态复位(每次 on_tap / on_touch_down 前清)。 */
|
||
static void browser_drag_reset(void)
|
||
{
|
||
g_press_row = -1;
|
||
g_press_delta = 0;
|
||
g_dragging = 0;
|
||
g_drag_last_y = 0;
|
||
}
|
||
|
||
/* ---- 列表行数据源:逐行填 meow_list_item(图标/名称/大小时间/⋯ ),轻量不 IO(§5.5)---- */
|
||
static int browser_src2(int idx, meow_list_item *out, void *ctx)
|
||
{
|
||
(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 {
|
||
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;
|
||
}
|
||
|
||
/* ---- 空态/错误绘制物(非控件;非空态时无输出)----
|
||
* ⚠️ custom 绘制物无 owner 闸门(不同于 widget,见 meow_ui_draw.c draw_order):
|
||
* 必须显式按「本页是否为当前 active activity」守门,否则切到 viewer 页会跨页泄漏绘制。 */
|
||
static void browser_draw_empty(void *ctx)
|
||
{
|
||
(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 == 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);
|
||
}
|
||
|
||
/* ---- 导航 ---- */
|
||
|
||
static void browser_update_title(void)
|
||
{
|
||
const char *name = fsx_basename(g_cur);
|
||
if (fsx_is_root(g_cur)) snprintf(g_title, sizeof g_title, "SD Card");
|
||
else snprintf(g_title, sizeof g_title, "%s", name);
|
||
if (g_title[0] == '\0') snprintf(g_title, sizeof g_title, "Files");
|
||
if (g_tb) meow_topappbar_set_title(g_tb, g_title);
|
||
}
|
||
|
||
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;
|
||
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(); /* 重画全注册绘制物(行/空态/页脚/topbar) */
|
||
}
|
||
|
||
void browser_enter_dir(const char *path)
|
||
{
|
||
if (!path) return;
|
||
strncpy(g_cur, path, sizeof g_cur - 1);
|
||
g_cur[sizeof g_cur - 1] = '\0';
|
||
browser_reload();
|
||
}
|
||
|
||
int browser_go_up(void)
|
||
{
|
||
if (fsx_is_root(g_cur)) return 0;
|
||
char up[FSX_MAX_PATH];
|
||
if (fsx_dirname(g_cur, up, sizeof up) != 0) return 0;
|
||
browser_enter_dir(up);
|
||
return 1;
|
||
}
|
||
|
||
int browser_at_root(void) { return fsx_is_root(g_cur); }
|
||
int browser_is_active(void) { return g_bc.active; }
|
||
const char *browser_current_dir(void) { return g_cur; }
|
||
int browser_entry_count(void) { return g_n; }
|
||
|
||
/* ---- 行操作 ---- */
|
||
|
||
static void browser_report_ret(int r)
|
||
{
|
||
if (r == 0) return;
|
||
const char *msg = strerror(-r);
|
||
meow_dialog *d = meow_dialog_create(MEOW_DLG_INFO, "Action failed");
|
||
const char *msgs[1];
|
||
msgs[0] = msg ? msg : "error";
|
||
meow_dialog_set_items(d, msgs, 1, -1);
|
||
meow_dialog_show(d);
|
||
meow_dialog_free(d);
|
||
}
|
||
|
||
static void browser_rename_entry(const fsx_entry *e)
|
||
{
|
||
meow_ime_ctx ime;
|
||
memset(&ime, 0, sizeof ime);
|
||
ime.mode = MEOW_IME_ASCII;
|
||
strncpy(ime.buf, e->name, sizeof ime.buf - 1);
|
||
ime.maxlen = 120;
|
||
meow_ui_text_input(&ime);
|
||
char nb[FSX_MAX_NAME];
|
||
if (fsx_sanitize_name(ime.buf, nb, sizeof nb) == 0 && strcmp(ime.buf, e->name) != 0) {
|
||
int r = fsx_rename_into(e->path, nb);
|
||
browser_report_ret(r);
|
||
}
|
||
}
|
||
|
||
static void browser_delete_entry(const fsx_entry *e)
|
||
{
|
||
char m[192];
|
||
snprintf(m, sizeof m, "Really delete \"%s\"%s?",
|
||
e->name, e->is_dir ? " (folder must be empty)" : "");
|
||
meow_dialog *d = meow_dialog_create(MEOW_DLG_CONFIRM, "Delete");
|
||
const char *msgs[1];
|
||
msgs[0] = m;
|
||
meow_dialog_set_items(d, msgs, 1, -1);
|
||
int yes = meow_dialog_confirm(d);
|
||
meow_dialog_free(d);
|
||
if (yes) { int r = fsx_delete(e->path); browser_report_ret(r); }
|
||
}
|
||
|
||
static void browser_props_entry(const fsx_entry *e)
|
||
{
|
||
fsx_entry st;
|
||
if (fsx_stat_path(e->path, &st) != 0) return;
|
||
char size[32], tm[32], l1[64], l2[64], l3[64];
|
||
fsx_fmt_size(st.size, size, sizeof size);
|
||
fsx_fmt_time(st.mtime, tm, sizeof tm);
|
||
snprintf(l1, sizeof l1, "Type: %s", st.is_dir ? "Directory" : "File");
|
||
snprintf(l2, sizeof l2, "Size: %s", st.is_dir ? "-" : size);
|
||
snprintf(l3, sizeof l3, "Modified: %s", tm);
|
||
const char *msgs[3];
|
||
msgs[0] = l1; msgs[1] = l2; msgs[2] = l3;
|
||
meow_dialog *d = meow_dialog_create(MEOW_DLG_INFO, st.name);
|
||
meow_dialog_set_items(d, msgs, 3, -1);
|
||
meow_dialog_show(d);
|
||
meow_dialog_free(d);
|
||
}
|
||
|
||
static void browser_new_folder_in(const char *dirpath)
|
||
{
|
||
meow_ime_ctx ime;
|
||
memset(&ime, 0, sizeof ime);
|
||
ime.mode = MEOW_IME_ASCII;
|
||
ime.maxlen = 120;
|
||
meow_ui_text_input(&ime);
|
||
char nb[FSX_MAX_NAME];
|
||
if (fsx_sanitize_name(ime.buf, nb, sizeof nb) == 0) {
|
||
int r = fsx_mkdir(dirpath, nb);
|
||
browser_report_ret(r);
|
||
if (r == 0 && browser_is_active()) browser_reload();
|
||
}
|
||
}
|
||
|
||
static void browser_new_folder(void) /* 当前目录下新建 */
|
||
{
|
||
browser_new_folder_in(g_cur);
|
||
}
|
||
|
||
/* BottomSheet:按行类型建不同选项清单。 */
|
||
static void browser_open_sheet(int row)
|
||
{
|
||
fsx_entry *e = &g_entries[row];
|
||
char title[128];
|
||
snprintf(title, sizeof title, "%s: %s", e->is_dir ? "Folder" : "File", e->name);
|
||
|
||
meow_sheet *s = meow_sheet_create(title, 0);
|
||
if (e->is_dir) {
|
||
meow_sheet_add_item(s, "Open");
|
||
meow_sheet_add_item(s, "New Folder");
|
||
meow_sheet_add_item(s, "Rename");
|
||
meow_sheet_add_item(s, "Delete");
|
||
meow_sheet_add_item(s, "Properties");
|
||
} else {
|
||
meow_sheet_add_item(s, "Open");
|
||
meow_sheet_add_item(s, "Rename");
|
||
meow_sheet_add_item(s, "Delete");
|
||
meow_sheet_add_item(s, "Properties");
|
||
}
|
||
int op = meow_sheet_open(s);
|
||
meow_sheet_free(s);
|
||
|
||
if (op < 0) return;
|
||
if (op == 0) { /* Open */
|
||
if (e->is_dir) browser_enter_dir(e->path);
|
||
else browser_open_file(e->path);
|
||
} else if (e->is_dir) {
|
||
if (op == 1) browser_new_folder_in(e->path);
|
||
else if (op == 2) browser_rename_entry(e);
|
||
else if (op == 3) browser_delete_entry(e);
|
||
else if (op == 4) browser_props_entry(e);
|
||
} else {
|
||
if (op == 1) browser_rename_entry(e);
|
||
else if (op == 2) browser_delete_entry(e);
|
||
else if (op == 3) browser_props_entry(e);
|
||
}
|
||
if (browser_is_active()) browser_reload(); /* 写操作后重扫(Open 文件时 viewer 前台则跳过) */
|
||
}
|
||
|
||
/* 打开文件:文本→预览页;否则提示。 */
|
||
static void browser_open_file(const char *path)
|
||
{
|
||
if (fsx_is_text_path(path)) {
|
||
if (viewer_open_file(path) == 0) return; /* 已 push 预览页 */
|
||
browser_report_ret(-EIO);
|
||
return;
|
||
}
|
||
meow_dialog *d = meow_dialog_create(MEOW_DLG_INFO, "Preview");
|
||
const char *msgs[1];
|
||
msgs[0] = "This is not a text file.\nNo preview available.";
|
||
meow_dialog_set_items(d, msgs, 1, -1);
|
||
meow_dialog_show(d);
|
||
meow_dialog_free(d);
|
||
}
|
||
|
||
/* ---- 输入分发 ---- */
|
||
|
||
int browser_on_tap(int x, int y)
|
||
{
|
||
if (g_tb) {
|
||
int ah = meow_topappbar_hit(g_tb, x, y);
|
||
if (ah == MEOW_TOPBAR_BACK) { browser_drag_reset(); browser_go_up(); return 1; }
|
||
if (ah >= 0) { browser_drag_reset(); browser_new_folder(); return 1; } /* NEW */
|
||
}
|
||
if (g_err != 0 || g_n == 0) return 0;
|
||
if (meow_widget_hit(g_list, x, y)) {
|
||
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) {
|
||
if (!picker_active()) browser_open_sheet(row); /* §6.2 二期:picker 模式不弹 sheet */
|
||
} else {
|
||
fsx_entry *e = &g_entries[row];
|
||
if (e->is_dir) browser_enter_dir(e->path);
|
||
else if (picker_active()) picker_return_file(e->path); /* picker:点文件=返回路径 */
|
||
else browser_open_file(e->path);
|
||
}
|
||
browser_drag_reset();
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* 触摸拖拽滚动(滚列表不滚页;topbar 不拖)。 */
|
||
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 = meow_listview_hit(g_list, x, y);
|
||
if (row >= 0) {
|
||
g_press_row = row;
|
||
meow_listview_set_sel(g_list, row); /* 按下高亮(§5.8.3 selected) */
|
||
}
|
||
g_dragging = 1;
|
||
g_drag_last_y = y;
|
||
meow_ui_invalidate(r_list.x, r_list.y, r_list.w, r_list.h);
|
||
}
|
||
}
|
||
|
||
void browser_touch_move(int x, int y)
|
||
{
|
||
(void)x;
|
||
if (!g_dragging) return;
|
||
if (g_press_row >= 0) { /* 拖动超阈值:清除按下高亮 */
|
||
int d = y - g_drag_last_y;
|
||
if (d < 0) d = -d;
|
||
g_press_delta += d;
|
||
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 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)
|
||
{
|
||
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 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);
|
||
}
|
||
|
||
/* SDK 通用长按(§6.3,≥阈值未抬手且位移≤容忍)→ 打开该行 BottomSheet。
|
||
* App 层长按计时已删(g_press_ms/g_press_tapped 移除);长按触发后 SDK 已抑制其抬手
|
||
* TAP(不再触发行体/打开动作),故这里无需再吞后续 tap;打开前清按下高亮与拖动态。 */
|
||
void browser_on_long_press(int x, int y)
|
||
{
|
||
if (picker_active()) return; /* §6.2 二期:picker 模式无长按 BottomSheet */
|
||
if (g_err != 0 || g_n == 0) return;
|
||
if (!meow_widget_hit(g_list, x, y)) return;
|
||
int row = meow_listview_hit(g_list, x, y);
|
||
if (row < 0) return;
|
||
g_press_row = -1;
|
||
g_dragging = 0;
|
||
meow_listview_set_sel(g_list, -1); /* 清除按下高亮(sheet 打开在即) */
|
||
browser_open_sheet(row); /* 内部:写操作后自行 reload */
|
||
}
|
||
|
||
/* ---- 页 view 生命周期 ---- */
|
||
|
||
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';
|
||
/* §6.2 二期:picker 模式 topbar 标题「Pick a file」且无 NEW 动作(点文件即选)。 */
|
||
if (picker_active())
|
||
g_tb = app_topbar_create("Pick a file", 1, NULL);
|
||
else
|
||
g_tb = app_topbar_create("Files", 1, "NEW");
|
||
/* 结构化列表:行数据源 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(); /* 扫首个目录(count/scroll/sel 由 reload 写入 listview) */
|
||
}
|
||
|
||
static void browser_page_enter(meow_view *v, void *ctx)
|
||
{
|
||
(void)v;
|
||
((browser_ctrl *)ctx)->active = 1;
|
||
const meow_theme *th = meow_theme_get();
|
||
meow_ui_set_page_bg(th->bg);
|
||
meow_ui_paint_full();
|
||
}
|
||
|
||
static void browser_page_leave(meow_view *v, void *ctx)
|
||
{
|
||
(void)v;
|
||
((browser_ctrl *)ctx)->active = 0;
|
||
}
|
||
|
||
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; }
|
||
}
|
||
|
||
meow_view *browser_view_create(void)
|
||
{
|
||
static const meow_view_hooks h = {
|
||
.on_create = browser_page_create,
|
||
.on_enter = browser_page_enter,
|
||
.on_leave = browser_page_leave,
|
||
.on_destroy = browser_page_destroy,
|
||
};
|
||
return meow_view_create(ACT_BROWSER, MEOW_VIEW_ACTIVITY, &h, &g_bc);
|
||
}
|