Skip to content

Commit 24e89aa

Browse files
committed
feat(gui): rewrite widget layout system
1 parent bbb7cbc commit 24e89aa

11 files changed

+797
-741
lines changed

include/LCUI/gui/metrics.h

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ typedef enum LCUI_DensityLevel {
5050
/** 转换成单位为 px 的度量值 */
5151
LCUI_API float LCUIMetrics_Compute(float value, LCUI_StyleType type);
5252

53+
LCUI_API float LCUIMetrics_ComputeStyle(LCUI_Style style);
54+
5355
/** 将矩形中的度量值的单位转换为 px */
5456
LCUI_API void LCUIMetrics_ComputeRectActual(LCUI_Rect *dst, const LCUI_RectF *src);
5557

include/LCUI/gui/widget_base.h

+92-48
Original file line numberDiff line numberDiff line change
@@ -86,35 +86,35 @@ typedef enum LCUI_WidgetTaskType {
8686
LCUI_WTASK_SHADOW,
8787
LCUI_WTASK_BORDER,
8888
LCUI_WTASK_BACKGROUND,
89-
LCUI_WTASK_LAYOUT,
9089
LCUI_WTASK_RESIZE,
9190
LCUI_WTASK_POSITION,
9291
LCUI_WTASK_ZINDEX,
9392
LCUI_WTASK_OPACITY,
93+
LCUI_WTASK_REFLOW,
9494
LCUI_WTASK_USER,
9595
LCUI_WTASK_TOTAL_NUM
9696
} LCUI_WidgetTaskType;
9797

98+
/** See more: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model */
9899
typedef struct LCUI_WidgetBoxModelRec_ {
99-
LCUI_RectF content; /**< 内容框的区域 */
100-
LCUI_RectF padding; /**< 内边距框的区域 */
101-
LCUI_RectF border; /**< 边框盒的区域,包括内边距框和内容框区域 */
102-
LCUI_RectF outer; /**< 外边距框的区域,包括边框盒和外边距框区域 */
103-
LCUI_RectF canvas; /**< 图层的区域,包括边框盒和阴影区域 */
100+
LCUI_RectF content;
101+
LCUI_RectF padding;
102+
LCUI_RectF border;
103+
LCUI_RectF canvas;
104+
LCUI_RectF outer;
104105
} LCUI_WidgetBoxModelRec, *LCUI_WidgetBoxModel;
105106

106107
typedef struct LCUI_WidgetTaskBoxRec_ {
107-
/** update for self */
108-
108+
/** Should update for self? */
109109
LCUI_BOOL for_self;
110110

111-
/** update for children */
111+
/** Should update for children? */
112112
LCUI_BOOL for_children;
113113

114-
/** skip the property synchronization of bound surface */
114+
/** Should skip the property sync of bound surface? */
115115
LCUI_BOOL skip_surface_props_sync;
116116

117-
/** states of tasks */
117+
/** States of tasks */
118118
LCUI_BOOL states[LCUI_WTASK_TOTAL_NUM];
119119
} LCUI_WidgetTaskBoxRec;
120120

@@ -235,39 +235,81 @@ typedef struct LCUI_WidgetAttributeRec_ {
235235
} value;
236236
} LCUI_WidgetAttributeRec, *LCUI_WidgetAttribute;
237237

238-
/** 部件结构 */
239238
typedef struct LCUI_WidgetRec_ {
240-
unsigned hash; /**< 哈希值 */
241-
LCUI_WidgetState state; /**< 状态 */
242-
float x, y; /**< 当前坐标(由 origin 计算而来) */
243-
float origin_x, origin_y; /**< 当前布局下计算出的坐标 */
244-
float width, height; /**< 部件区域大小,包括边框和内边距占用区域 */
245-
size_t index; /**< 部件索引位置 */
246-
char *id; /**< ID */
247-
char *type; /**< 类型 */
248-
strlist_t classes; /**< 类列表 */
249-
strlist_t status; /**< 状态列表 */
250-
wchar_t *title; /**< 标题 */
251-
LCUI_Rect2F padding; /**< 内边距框 */
252-
LCUI_Rect2F margin; /**< 外边距框 */
253-
LCUI_WidgetBoxModelRec box; /**< 部件的各个区域信息 */
254-
LCUI_StyleSheet style; /**< 当前完整样式表 */
255-
LCUI_StyleList custom_style; /**< 自定义样式表 */
256-
LCUI_CachedStyleSheet inherited_style; /**< 通过继承得到的样式表 */
257-
LCUI_WidgetStyle computed_style; /**< 已经计算的样式数据 */
258-
LCUI_Widget parent; /**< 父部件 */
259-
LinkedList children; /**< 子部件 */
260-
LinkedList children_show; /**< 子部件的堆叠顺序记录,由顶到底 */
261-
LCUI_WidgetData data; /**< 私有数据 */
262-
Dict *attributes; /**< 属性记录 */
263-
LCUI_WidgetPrototypeC proto; /**< 原型 */
264-
LCUI_EventTrigger trigger; /**< 事件触发器 */
265-
LCUI_WidgetTaskBoxRec task; /**< 任务记录 */
266-
LCUI_WidgetRules rules; /**< 更新部件时采用的规则 */
267-
LCUI_BOOL event_blocked; /**< 是否阻止自己和子级部件的事件处理 */
268-
LCUI_BOOL disabled; /**< 是否禁用 */
269-
LinkedListNode node; /**< 在部件链表中的结点 */
270-
LinkedListNode node_show; /**< 在部件显示链表中的结点 */
239+
unsigned hash;
240+
LCUI_WidgetState state;
241+
242+
char *id;
243+
char *type;
244+
strlist_t classes;
245+
strlist_t status;
246+
wchar_t *title;
247+
Dict *attributes;
248+
LCUI_BOOL disabled;
249+
LCUI_BOOL event_blocked;
250+
251+
/**
252+
* Coordinates calculated by the layout system
253+
* The position of the rectangular boxes is calculated based on it
254+
*/
255+
float layout_x, layout_y;
256+
257+
/**
258+
* Geometric parameters (readonly)
259+
* their values come from the box.border
260+
*/
261+
float x, y;
262+
float width, height;
263+
264+
LCUI_Rect2F padding;
265+
LCUI_Rect2F margin;
266+
LCUI_WidgetBoxModelRec box;
267+
268+
LCUI_StyleSheet style;
269+
LCUI_StyleList custom_style;
270+
LCUI_CachedStyleSheet inherited_style;
271+
LCUI_WidgetStyle computed_style;
272+
273+
/** Some data bound to the prototype */
274+
LCUI_WidgetData data;
275+
276+
/**
277+
* Prototype chain
278+
* It is used to implement the inheritance of widgets,
279+
* Just like prototype chain in JavaScript
280+
*/
281+
LCUI_WidgetPrototypeC proto;
282+
283+
/**
284+
* Update task context
285+
*/
286+
LCUI_WidgetTaskBoxRec task;
287+
LCUI_WidgetRules rules;
288+
LCUI_EventTrigger trigger;
289+
290+
/** Parent widiget */
291+
LCUI_Widget parent;
292+
293+
/** List of child widgets */
294+
LinkedList children;
295+
296+
/** List of child widgets in descending order by z-index */
297+
LinkedList children_show;
298+
299+
/**
300+
* Position in the parent->children
301+
* this == LinkedList_Get(&this->parent->children, this.index)
302+
*/
303+
size_t index;
304+
305+
/**
306+
* Node in the parent->children
307+
* &this->node == LinkedList_GetNode(&this->parent->children, this.index)
308+
*/
309+
LinkedListNode node;
310+
311+
/** Node in the parent->children_shoa */
312+
LinkedListNode node_show;
271313
} LCUI_WidgetRec;
272314

273315
/* clang-format on */
@@ -383,10 +425,6 @@ LCUI_API float Widget_GetLimitedWidth(LCUI_Widget w, float width);
383425

384426
LCUI_API float Widget_GetLimitedHeight(LCUI_Widget w, float height);
385427

386-
LCUI_API void Widget_AutoSize(LCUI_Widget w);
387-
388-
LCUI_API void Widget_ComputeSizeStyle(LCUI_Widget w);
389-
390428
/** 根据阴影参数获取部件区域的横向偏移距离 */
391429
LCUI_API float Widget_GetBoxShadowOffsetX(LCUI_Widget w);
392430

@@ -458,7 +496,13 @@ LCUI_API float Widget_ComputeMaxContentWidth(LCUI_Widget w);
458496
/** 计算部件的最大可用宽度 */
459497
LCUI_API float Widget_ComputeMaxAvaliableWidth(LCUI_Widget widget);
460498

461-
LCUI_API void Widget_ComputeLimitSize(LCUI_Widget w);
499+
LCUI_API void Widget_UpdateBoxPosition(LCUI_Widget w);
500+
501+
LCUI_API void Widget_UpdateCanvasBox(LCUI_Widget w);
502+
503+
LCUI_API void Widget_UpdateBoxSize(LCUI_Widget w);
504+
505+
LCUI_API void Widget_SetBorderBoxSize(LCUI_Widget w, float width, float height);
462506

463507
LCUI_API size_t LCUIWidget_ClearTrash(void);
464508

include/LCUI/gui/widget_layout.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@
3131
#ifndef LCUI_WIDGET_LAYOUT_H
3232
#define LCUI_WIDGET_LAYOUT_H
3333

34-
LCUI_API void Widget_DoLayout(LCUI_Widget w);
34+
typedef struct LCUI_WidgetLayoutContextRec_ {
35+
int invalid_box;
36+
LCUI_BOOL can_render;
37+
LCUI_BOOL should_add_invalid_area;
38+
LCUI_WidgetBoxModelRec box;
39+
LCUI_Widget container;
40+
} LCUI_WidgetLayoutContextRec, *LCUI_WidgetLayoutContext;
41+
42+
LCUI_API void LCUIWidgetLayout_Reflow(LCUI_WidgetLayoutContext ctx);
3543

3644
#endif

src/gui/metrics.c

+31-5
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,41 @@ static LCUI_MetricsRec metrics;
3939
float LCUIMetrics_Compute(float value, LCUI_StyleType type)
4040
{
4141
switch (type) {
42-
case LCUI_STYPE_PX: break;
43-
case LCUI_STYPE_DIP: value = value * metrics.density; break;
44-
case LCUI_STYPE_SP: value = value * metrics.scaled_density; break;
45-
case LCUI_STYPE_PT: value = value * metrics.dpi / 72.0f; break;
46-
default: value = 0; break;
42+
case LCUI_STYPE_PX:
43+
break;
44+
case LCUI_STYPE_DIP:
45+
value = value * metrics.density;
46+
break;
47+
case LCUI_STYPE_SP:
48+
value = value * metrics.scaled_density;
49+
break;
50+
case LCUI_STYPE_PT:
51+
value = value * metrics.dpi / 72.0f;
52+
break;
53+
default:
54+
value = 0;
55+
break;
4756
}
4857
return value;
4958
}
5059

60+
float LCUIMetrics_ComputeStyle(LCUI_Style style)
61+
{
62+
switch (style->type) {
63+
case LCUI_STYPE_PX:
64+
return style->val_px;
65+
case LCUI_STYPE_DIP:
66+
return style->val_dip * metrics.density;
67+
case LCUI_STYPE_SP:
68+
return style->val_sp * metrics.scaled_density;
69+
case LCUI_STYPE_PT:
70+
return style->val_pt * metrics.dpi / 72.0f;
71+
default:
72+
break;
73+
}
74+
return 0;
75+
}
76+
5177
int LCUIMetrics_ComputeActual(float value, LCUI_StyleType type)
5278
{
5379
return iround(LCUIMetrics_Compute(value, type) * metrics.scale);

src/gui/widget/textview.c

+1
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ static void TextView_UpdateStyle(LCUI_Widget w)
158158
txt->style = style;
159159
txt->tasks[TASK_UPDATE].is_valid = TRUE;
160160
Widget_AddTask(w, LCUI_WTASK_USER);
161+
Widget_AddTask(w, LCUI_WTASK_REFLOW);
161162
}
162163

163164
static void TextView_UpdateLayerSize(LCUI_Widget w)

src/gui/widget_base.c

+95-2
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ LCUI_Widget LCUIWidget_New(const char *type)
137137
void Widget_ExecDestroy(LCUI_Widget w)
138138
{
139139
if (w->parent) {
140-
Widget_AddTask(w->parent, LCUI_WTASK_LAYOUT);
140+
Widget_AddTask(w->parent, LCUI_WTASK_REFLOW);
141141
Widget_Unlink(w);
142142
}
143143
Widget_DestroyBackground(w);
@@ -183,7 +183,7 @@ void Widget_Destroy(LCUI_Widget w)
183183
node = node->next;
184184
}
185185
if (w->computed_style.position != SV_ABSOLUTE) {
186-
Widget_AddTask(w->parent, LCUI_WTASK_LAYOUT);
186+
Widget_AddTask(w->parent, LCUI_WTASK_REFLOW);
187187
}
188188
Widget_InvalidateArea(w, NULL, SV_GRAPH_BOX);
189189
Widget_AddToTrash(w);
@@ -503,6 +503,7 @@ void Widget_AddState(LCUI_Widget w, LCUI_WidgetState state)
503503
float Widget_ComputeXMetric(LCUI_Widget w, int key)
504504
{
505505
LCUI_Style s = &w->style->sheet[key];
506+
506507
if (s->type == LCUI_STYPE_SCALE) {
507508
if (!w->parent) {
508509
return 0;
@@ -518,6 +519,7 @@ float Widget_ComputeXMetric(LCUI_Widget w, int key)
518519
float Widget_ComputeYMetric(LCUI_Widget w, int key)
519520
{
520521
LCUI_Style s = &w->style->sheet[key];
522+
521523
if (s->type == LCUI_STYPE_SCALE) {
522524
if (!w->parent) {
523525
return 0;
@@ -621,6 +623,97 @@ void Widget_BindProperty(LCUI_Widget w, const char *name, LCUI_Object value)
621623
}
622624
}
623625

626+
void Widget_UpdateBoxPosition(LCUI_Widget w)
627+
{
628+
float x = w->layout_x;
629+
float y = w->layout_y;
630+
631+
switch (w->computed_style.position) {
632+
case SV_ABSOLUTE:
633+
if (!Widget_HasAutoStyle(w, key_left)) {
634+
x = w->computed_style.left;
635+
} else if (!Widget_HasAutoStyle(w, key_right)) {
636+
if (w->parent) {
637+
x = w->parent->box.border.width - w->width;
638+
}
639+
x -= w->computed_style.right;
640+
}
641+
if (!Widget_HasAutoStyle(w, key_top)) {
642+
y = w->computed_style.top;
643+
} else if (!Widget_HasAutoStyle(w, key_bottom)) {
644+
if (w->parent) {
645+
y = w->parent->box.border.height - w->height;
646+
}
647+
y -= w->computed_style.bottom;
648+
}
649+
break;
650+
case SV_RELATIVE:
651+
if (!Widget_HasAutoStyle(w, key_left)) {
652+
x += w->computed_style.left;
653+
} else if (!Widget_HasAutoStyle(w, key_right)) {
654+
x -= w->computed_style.right;
655+
}
656+
if (!Widget_HasAutoStyle(w, key_top)) {
657+
y += w->computed_style.top;
658+
} else if (!Widget_HasAutoStyle(w, key_bottom)) {
659+
y -= w->computed_style.bottom;
660+
}
661+
case SV_STATIC:
662+
default:
663+
break;
664+
}
665+
w->box.outer.x = x;
666+
w->box.outer.y = y;
667+
w->x = x + w->margin.left;
668+
w->y = y + w->margin.top;
669+
w->box.border.x = w->x;
670+
w->box.border.y = w->y;
671+
w->box.padding.x = w->x + w->computed_style.border.left.width;
672+
w->box.padding.y = w->y + w->computed_style.border.top.width;
673+
w->box.content.x = w->box.padding.x + w->padding.left;
674+
w->box.content.y = w->box.padding.y + w->padding.top;
675+
w->box.canvas.x = w->x - Widget_GetBoxShadowOffsetX(w);
676+
w->box.canvas.y = w->y - Widget_GetBoxShadowOffsetY(w);
677+
}
678+
679+
void Widget_UpdateCanvasBox(LCUI_Widget w)
680+
{
681+
w->box.canvas.x = w->box.border.x - Widget_GetBoxShadowOffsetX(w);
682+
w->box.canvas.y = w->box.border.y - Widget_GetBoxShadowOffsetY(w);
683+
w->box.canvas.width = Widget_GetCanvasWidth(w);
684+
w->box.canvas.height = Widget_GetCanvasHeight(w);
685+
}
686+
687+
void Widget_UpdateBoxSize(LCUI_Widget w)
688+
{
689+
const float padding_x = w->padding.left + w->padding.right;
690+
const float padding_y = w->padding.top + w->padding.bottom;
691+
const float border_x = w->computed_style.border.left.width +
692+
w->computed_style.border.right.width;
693+
const float border_y = w->computed_style.border.top.width +
694+
w->computed_style.border.bottom.width;
695+
const float margin_x = w->margin.left + w->margin.right;
696+
const float margin_y = w->margin.top + w->margin.bottom;
697+
698+
w->box.border.width = w->width;
699+
w->box.border.height = w->height;
700+
w->box.padding.width = w->box.border.width - border_x;
701+
w->box.padding.height = w->box.border.height - border_y;
702+
w->box.content.width = w->box.padding.width - padding_x;
703+
w->box.content.height = w->box.padding.height - padding_y;
704+
w->box.outer.width = w->box.border.width + margin_x;
705+
w->box.outer.height = w->box.border.height + margin_y;
706+
w->box.canvas.width = Widget_GetCanvasWidth(w);
707+
w->box.canvas.height = Widget_GetCanvasHeight(w);
708+
}
709+
710+
void Widget_SetBorderBoxSize(LCUI_Widget w, float width, float height)
711+
{
712+
w->width = Widget_GetLimitedWidth(w, width);
713+
w->height = Widget_GetLimitedHeight(w, height);
714+
Widget_UpdateBoxSize(w);
715+
}
716+
624717
void LCUIWidget_InitBase(void)
625718
{
626719
LinkedList_Init(&LCUIWidget.trash);

0 commit comments

Comments
 (0)