Files
MeowOS-SDK/sdk/meow_ui_box.c
T
AskaEth 4b094d62a5 sdk: 增量同步主仓新模块(box/anim/scroll/dispatch/listvirt/str/sb_icons/img)
subtree pull sdk 来自主仓 pub/sdk(主仓 commit 68ce46a 等)。新增:
- 布局引擎 meow_ui_box / 动画基座 meow_ui_anim / 滚动物理 meow_ui_scroll
- §6.4 事件分发形式化 / W_LISTVIEW 虚拟化多行 / meow_str 字符串资源 i18n
- sb_icons 动作图标扩充 + topbar 位图图标 / meow_img_downscale
- tools/ 图标生成脚本与 SVG 源
- 对应宿主 test_*.c 与 demo_box_scroll.c

build_sdk.sh 冲突解析:保留公开仓自包含版(vendored meow_font + -Iinclude -I.),
OBJS 手工并入 4 个新模块(meow_ui_box/anim/scroll/str)。保留 vendored meow_font.c/
meow_ipc.h/meow_font.h/meow_notif.h,剔除宿主测试二进制与编译产物。
2026-09-02 09:08:51 +08:00

308 lines
12 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_box.c — 线性布局引擎(§12.1 meow_box)。
*
* meow_box 是**纯布局计算器**(非控件):静态池创建、定长子项数组、arrange 单遍求解,
* 把每个子控件的 x/y/w/h 写进 meow_widget(meow_widget_set_rect);嵌套 sub-box 递归求解。
* 用显式静态栈(深度≤MEOW_BOX_MAX_DEPTH)做嵌套展开,无 C 递归,杜绝栈爆炸。
*
* 求解模型(主轴向):
* - 每个子项占位 = lead(margin) + extent + trail(margin);项间再叠 box->gap。
* - FIXED/WRAP 的 extent 先定;WEIGHT 的 extent=0(待均分)。
* - leftover = content_main - foot;若权重和>0 → 均分 leftover;否则按主轴对齐分配。
* 交叉轴:按 START/CENTER/END/STRETCH 对齐,STRETCH 填满容器交叉向减 margin。
*/
#include "meow_ui.h"
#include "include/meow_ui_box.h"
#include "meow_ui_internal.h" /* meow_widget_rect(只读几何) */
#include <string.h>
#define MEOW_BOX_POOL 16 /* 静态池大小(并发 box 数) */
struct meow_box {
int dir; /* MEOW_BOX_H / MEOW_BOX_V */
int x, y, w, h; /* 容器矩形(绝对坐标) */
int pad_l, pad_t, pad_r, pad_b;
int gap;
int main_align;
int cross_align;
meow_box_item items[MEOW_BOX_MAX_CHILD];
int n;
};
static meow_box g_pool[MEOW_BOX_POOL];
static unsigned char g_used[MEOW_BOX_POOL];
/* ---- 静态池存取 ---- */
meow_box *meow_box_new(meow_box_dir dir)
{
for (int i = 0; i < MEOW_BOX_POOL; i++) {
if (!g_used[i]) {
g_used[i] = 1;
meow_box *b = &g_pool[i];
memset(b, 0, sizeof *b);
b->dir = (dir == MEOW_BOX_H) ? MEOW_BOX_H : MEOW_BOX_V;
return b;
}
}
return NULL;
}
void meow_box_free(meow_box *b)
{
if (!b) return;
for (int i = 0; i < MEOW_BOX_POOL; i++)
if (&g_pool[i] == b) { g_used[i] = 0; return; }
}
void meow_box_set_rect(meow_box *b, int x, int y, int w, int h)
{
if (!b) return;
b->x = x; b->y = y;
b->w = w > 0 ? w : 0; b->h = h > 0 ? h : 0;
}
void meow_box_set_padding(meow_box *b, int l, int t, int r, int btm)
{
if (!b) return;
b->pad_l = l; b->pad_t = t; b->pad_r = r; b->pad_b = btm;
}
void meow_box_set_gap(meow_box *b, int gap)
{
if (!b) return;
b->gap = gap > 0 ? gap : 0;
}
void meow_box_set_align(meow_box *b, int main_align, int cross_align)
{
if (!b) return;
b->main_align = main_align;
b->cross_align = cross_align;
}
/* ---- 子项 ---- */
int meow_box_add(meow_box *b, meow_widget *w, int size, int weight)
{
return meow_box_add_ex(b, w, size, weight, 0, 0, NULL, NULL);
}
int meow_box_add_ex(meow_box *b, meow_widget *w, int size, int weight,
int min, int preferred, meow_box_measure_fn m, void *mctx)
{
if (!b || !w || b->n >= MEOW_BOX_MAX_CHILD) return -1;
if (size != MEOW_BOX_FIXED && size != MEOW_BOX_WEIGHT && size != MEOW_BOX_WRAP) return -1;
if (size == MEOW_BOX_WEIGHT && weight <= 0) return -1;
for (int i = 0; i < b->n; i++)
if (b->items[i].w == w) return -1; /* 同控件去重 */
meow_box_item *it = &b->items[b->n++];
memset(it, 0, sizeof *it);
it->w = w; it->size = size; it->weight = weight > 0 ? weight : 1;
it->min = min > 0 ? min : 0;
it->preferred = preferred > 0 ? preferred : 0;
it->measure = m; it->mctx = mctx;
return 0;
}
int meow_box_add_sub(meow_box *b, meow_box *sub, int size, int weight)
{
if (!b || !sub || b->n >= MEOW_BOX_MAX_CHILD) return -1;
if (size != MEOW_BOX_FIXED && size != MEOW_BOX_WEIGHT && size != MEOW_BOX_WRAP) return -1;
if (size == MEOW_BOX_WEIGHT && weight <= 0) return -1;
for (int i = 0; i < b->n; i++)
if (b->items[i].sub == sub) return -1;
meow_box_item *it = &b->items[b->n++];
memset(it, 0, sizeof *it);
it->sub = sub; it->size = size; it->weight = weight > 0 ? weight : 1;
return 0;
}
/* ---- 查询 ---- */
int meow_box_count(const meow_box *b) { return b ? b->n : 0; }
meow_widget *meow_box_widget_at(const meow_box *b, int idx)
{ return (b && idx >= 0 && idx < b->n) ? b->items[idx].w : NULL; }
meow_box *meow_box_sub_at(const meow_box *b, int idx)
{ return (b && idx >= 0 && idx < b->n) ? b->items[idx].sub : NULL; }
void meow_box_rect(const meow_box *b, int out[4])
{
if (!out) return;
if (!b) { out[0] = out[1] = out[2] = out[3] = 0; return; }
out[0] = b->x; out[1] = b->y; out[2] = b->w; out[3] = b->h;
}
/* ---- 内部:取子项 margin(widget opts;sub 无 margin)---- */
static void item_margin(const meow_box_item *it, meow_box_dir dir,
int *main_before, int *main_after, int *cross)
{
*main_before = *main_after = *cross = 0;
if (!it->w) return;
meow_widget_opts o;
if (meow_widget_opts_get(it->w, &o) != 0) return;
int mx = o.margin_x > 0 ? o.margin_x : 0;
int my = o.margin_y > 0 ? o.margin_y : 0;
if (dir == MEOW_BOX_H) { *main_before = mx; *main_after = mx; *cross = my; }
else { *main_before = my; *main_after = my; *cross = mx; }
}
/* ---- 内部:子项主轴向 extent ----
* FIXED → 控件/嵌套盒当前主轴向尺寸
* WRAP → measure 回调(若有)否则控件当前尺寸;下限 clamp 到 min
* WEIGHT → 0(由均分阶段填充) */
static int item_extent(const meow_box_item *it, int main_is_w)
{
int extent = 0;
if (it->w) {
int r[4]; meow_widget_rect(it->w, r);
extent = main_is_w ? r[2] : r[3];
} else if (it->sub) {
extent = main_is_w ? it->sub->w : it->sub->h;
}
if (it->size == MEOW_BOX_WRAP && it->measure) {
int mn = 0, pf = 0;
it->measure(it->mctx, &mn, &pf);
if (pf > 0) extent = pf;
else if (mn > 0) extent = mn;
if (mn > 0 && extent < mn) extent = mn; /* clamp 到下限 */
}
if (extent < 0) extent = 0;
return extent;
}
/* ---- 单容器求解(一次 pass:算 extent + 位置,写全部子项矩形)----
* 子项为嵌套 meow_box 时只写其矩形(父求结果),后续由 meow_box_arrange 的
* 显式栈递归求解其内部。 */
static void box_layout_one(meow_box *b)
{
int n = b->n;
if (n <= 0) return;
int cx = b->x + b->pad_l;
int cy = b->y + b->pad_t;
int cw = b->w - b->pad_l - b->pad_r; if (cw < 0) cw = 0;
int ch = b->h - b->pad_t - b->pad_b; if (ch < 0) ch = 0;
int main_is_w = (b->dir == MEOW_BOX_H);
int main_size = main_is_w ? cw : ch;
int cross_size = main_is_w ? ch : cw;
int gap = b->gap;
int ext[MEOW_BOX_MAX_CHILD], lead[MEOW_BOX_MAX_CHILD],
trail[MEOW_BOX_MAX_CHILD], wgt[MEOW_BOX_MAX_CHILD];
int nw = 0, wsum = 0;
long long foot = 0;
for (int i = 0; i < n; i++) {
const meow_box_item *it = &b->items[i];
int mb, ma, cr;
item_margin(it, (meow_box_dir)b->dir, &mb, &ma, &cr);
lead[i] = mb; trail[i] = ma; (void)cr;
wgt[i] = (it->size == MEOW_BOX_WEIGHT) ? it->weight : 0;
if (it->size == MEOW_BOX_WEIGHT) {
ext[i] = 0; nw++; wsum += it->weight;
} else {
ext[i] = item_extent(it, main_is_w);
}
foot += (long long)lead[i] + trail[i] + ext[i];
}
foot += (long long)gap * (n - 1);
long long leftover = (long long)main_size - foot;
if (nw > 0 && leftover > 0) {
/* 均分剩余到权重项;余数塞给最后一个权重项铺满精确 */
long long consumed = 0;
for (int i = 0; i < n; i++) {
if (wgt[i] > 0) { ext[i] = (int)(leftover * wgt[i] / wsum); consumed += ext[i]; }
}
for (int i = n - 1; i >= 0; i--) {
if (wgt[i] > 0) { ext[i] += (int)(leftover - consumed); break; }
}
leftover = 0;
}
/* 主轴对齐(仅当无权重、leftover != 0 有意义;有权重时 leftover≈0 → START 等效) */
int off0 = 0;
int intergap = gap;
if (nw == 0) {
if (leftover > 0) {
switch (b->main_align) {
case MEOW_BOX_ALIGN_CENTER: off0 = (int)(leftover / 2); break;
case MEOW_BOX_ALIGN_END: off0 = (int)leftover; break;
case MEOW_BOX_ALIGN_SPACE_BETWEEN: off0 = 0;
intergap = (n > 1) ? gap + (int)(leftover / (n - 1)) : gap; break;
case MEOW_BOX_ALIGN_SPACE_AROUND: off0 = (int)(leftover / (2 * n));
intergap = gap + (int)(leftover / n); break;
case MEOW_BOX_ALIGN_SPACE_EVENLY: off0 = (int)(leftover / (n + 1));
intergap = gap + (int)(leftover / (n + 1)); break;
default: off0 = 0; break; /* START */
}
} else {
off0 = 0; intergap = gap; /* 溢出:从起点排(可越界,交滚动容器) */
}
}
/* 主轴定位 + 交叉轴定位,写子控件矩形(主轴起点 = 内容原点 cx/cy)。
* rel = 相对内容原点的主轴游标;content_rel = 本项内容相对起点;content_pos = 绝对主轴坐标。 */
int main_base = main_is_w ? cx : cy;
int rel = off0;
for (int i = 0; i < n; i++) {
const meow_box_item *it = &b->items[i];
int content_rel = rel + lead[i];
int content_pos = main_base + content_rel;
if (it->w) {
int r[4]; meow_widget_rect(it->w, r);
meow_widget_opts o; int has = (meow_widget_opts_get(it->w, &o) == 0);
int cmx = (has && o.margin_x > 0) ? o.margin_x : 0;
int cmy = (has && o.margin_y > 0) ? o.margin_y : 0;
int cm = main_is_w ? cmy : cmx;
int base = main_is_w ? r[3] : r[2];
int cross = 0, cross_ext = 0;
if (b->cross_align == MEOW_BOX_CROSS_STRETCH) {
cross_ext = cross_size - 2 * cm; if (cross_ext < 0) cross_ext = 0;
cross = cm;
} else {
cross_ext = base;
int pad = cross_size - 2 * cm - cross_ext; if (pad < 0) pad = 0;
if (b->cross_align == MEOW_BOX_CROSS_CENTER) cross = cm + pad / 2;
else if (b->cross_align == MEOW_BOX_CROSS_END) cross = cm + pad;
else cross = cm; /* START */
}
if (main_is_w)
meow_widget_set_rect(it->w, content_pos, cy + cross, ext[i], cross_ext);
else
meow_widget_set_rect(it->w, cx + cross, content_pos, cross_ext, ext[i]);
} else if (it->sub) {
meow_box *sb = it->sub;
int base = main_is_w ? sb->h : sb->w;
int cross = 0, cross_ext = 0;
if (b->cross_align == MEOW_BOX_CROSS_STRETCH) {
cross_ext = cross_size; if (cross_ext < 0) cross_ext = 0;
cross = 0;
} else {
cross_ext = base;
int pad = cross_size - cross_ext; if (pad < 0) pad = 0;
if (b->cross_align == MEOW_BOX_CROSS_CENTER) cross = pad / 2;
else if (b->cross_align == MEOW_BOX_CROSS_END) cross = pad;
else cross = 0;
}
if (main_is_w) { sb->x = content_pos; sb->y = cy + cross; sb->w = ext[i]; sb->h = cross_ext; }
else { sb->x = cx + cross; sb->y = content_pos; sb->w = cross_ext; sb->h = ext[i]; }
}
if (i < n - 1)
rel = content_rel + ext[i] + trail[i] + intergap;
else
rel = content_rel + ext[i] + trail[i];
}
}
/* ---- 求解:单遍,显式静态栈展开嵌套(深度≤MEOW_BOX_MAX_DEPTH)---- */
static meow_box *g_stack[MEOW_BOX_MAX_DEPTH];
void meow_box_arrange(meow_box *b)
{
if (!b) return;
int top = 0;
g_stack[0] = b;
while (top >= 0) {
meow_box *cur = g_stack[top--];
box_layout_one(cur); /* 写全部直接子项矩形(含 sub->rect) */
for (int i = 0; i < cur->n; i++) {
meow_box *sb = cur->items[i].sub;
if (!sb) continue;
if (top + 1 >= MEOW_BOX_MAX_DEPTH) continue; /* 深度上限:跳过(矩形已被父写定) */
g_stack[++top] = sb;
}
}
}