/* * 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 #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; } } }