Skip to content

Commit

Permalink
feat(gui): rewrite widget layout system
Browse files Browse the repository at this point in the history
  • Loading branch information
lc-soft committed Feb 16, 2020
1 parent bbb7cbc commit 24e89aa
Show file tree
Hide file tree
Showing 11 changed files with 797 additions and 741 deletions.
2 changes: 2 additions & 0 deletions include/LCUI/gui/metrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ typedef enum LCUI_DensityLevel {
/** 转换成单位为 px 的度量值 */
LCUI_API float LCUIMetrics_Compute(float value, LCUI_StyleType type);

LCUI_API float LCUIMetrics_ComputeStyle(LCUI_Style style);

/** 将矩形中的度量值的单位转换为 px */
LCUI_API void LCUIMetrics_ComputeRectActual(LCUI_Rect *dst, const LCUI_RectF *src);

Expand Down
140 changes: 92 additions & 48 deletions include/LCUI/gui/widget_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,35 +86,35 @@ typedef enum LCUI_WidgetTaskType {
LCUI_WTASK_SHADOW,
LCUI_WTASK_BORDER,
LCUI_WTASK_BACKGROUND,
LCUI_WTASK_LAYOUT,
LCUI_WTASK_RESIZE,
LCUI_WTASK_POSITION,
LCUI_WTASK_ZINDEX,
LCUI_WTASK_OPACITY,
LCUI_WTASK_REFLOW,
LCUI_WTASK_USER,
LCUI_WTASK_TOTAL_NUM
} LCUI_WidgetTaskType;

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

typedef struct LCUI_WidgetTaskBoxRec_ {
/** update for self */

/** Should update for self? */
LCUI_BOOL for_self;

/** update for children */
/** Should update for children? */
LCUI_BOOL for_children;

/** skip the property synchronization of bound surface */
/** Should skip the property sync of bound surface? */
LCUI_BOOL skip_surface_props_sync;

/** states of tasks */
/** States of tasks */
LCUI_BOOL states[LCUI_WTASK_TOTAL_NUM];
} LCUI_WidgetTaskBoxRec;

Expand Down Expand Up @@ -235,39 +235,81 @@ typedef struct LCUI_WidgetAttributeRec_ {
} value;
} LCUI_WidgetAttributeRec, *LCUI_WidgetAttribute;

/** 部件结构 */
typedef struct LCUI_WidgetRec_ {
unsigned hash; /**< 哈希值 */
LCUI_WidgetState state; /**< 状态 */
float x, y; /**< 当前坐标(由 origin 计算而来) */
float origin_x, origin_y; /**< 当前布局下计算出的坐标 */
float width, height; /**< 部件区域大小,包括边框和内边距占用区域 */
size_t index; /**< 部件索引位置 */
char *id; /**< ID */
char *type; /**< 类型 */
strlist_t classes; /**< 类列表 */
strlist_t status; /**< 状态列表 */
wchar_t *title; /**< 标题 */
LCUI_Rect2F padding; /**< 内边距框 */
LCUI_Rect2F margin; /**< 外边距框 */
LCUI_WidgetBoxModelRec box; /**< 部件的各个区域信息 */
LCUI_StyleSheet style; /**< 当前完整样式表 */
LCUI_StyleList custom_style; /**< 自定义样式表 */
LCUI_CachedStyleSheet inherited_style; /**< 通过继承得到的样式表 */
LCUI_WidgetStyle computed_style; /**< 已经计算的样式数据 */
LCUI_Widget parent; /**< 父部件 */
LinkedList children; /**< 子部件 */
LinkedList children_show; /**< 子部件的堆叠顺序记录,由顶到底 */
LCUI_WidgetData data; /**< 私有数据 */
Dict *attributes; /**< 属性记录 */
LCUI_WidgetPrototypeC proto; /**< 原型 */
LCUI_EventTrigger trigger; /**< 事件触发器 */
LCUI_WidgetTaskBoxRec task; /**< 任务记录 */
LCUI_WidgetRules rules; /**< 更新部件时采用的规则 */
LCUI_BOOL event_blocked; /**< 是否阻止自己和子级部件的事件处理 */
LCUI_BOOL disabled; /**< 是否禁用 */
LinkedListNode node; /**< 在部件链表中的结点 */
LinkedListNode node_show; /**< 在部件显示链表中的结点 */
unsigned hash;
LCUI_WidgetState state;

char *id;
char *type;
strlist_t classes;
strlist_t status;
wchar_t *title;
Dict *attributes;
LCUI_BOOL disabled;
LCUI_BOOL event_blocked;

/**
* Coordinates calculated by the layout system
* The position of the rectangular boxes is calculated based on it
*/
float layout_x, layout_y;

/**
* Geometric parameters (readonly)
* their values come from the box.border
*/
float x, y;
float width, height;

LCUI_Rect2F padding;
LCUI_Rect2F margin;
LCUI_WidgetBoxModelRec box;

LCUI_StyleSheet style;
LCUI_StyleList custom_style;
LCUI_CachedStyleSheet inherited_style;
LCUI_WidgetStyle computed_style;

/** Some data bound to the prototype */
LCUI_WidgetData data;

/**
* Prototype chain
* It is used to implement the inheritance of widgets,
* Just like prototype chain in JavaScript
*/
LCUI_WidgetPrototypeC proto;

/**
* Update task context
*/
LCUI_WidgetTaskBoxRec task;
LCUI_WidgetRules rules;
LCUI_EventTrigger trigger;

/** Parent widiget */
LCUI_Widget parent;

/** List of child widgets */
LinkedList children;

/** List of child widgets in descending order by z-index */
LinkedList children_show;

/**
* Position in the parent->children
* this == LinkedList_Get(&this->parent->children, this.index)
*/
size_t index;

/**
* Node in the parent->children
* &this->node == LinkedList_GetNode(&this->parent->children, this.index)
*/
LinkedListNode node;

/** Node in the parent->children_shoa */
LinkedListNode node_show;
} LCUI_WidgetRec;

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

LCUI_API float Widget_GetLimitedHeight(LCUI_Widget w, float height);

LCUI_API void Widget_AutoSize(LCUI_Widget w);

LCUI_API void Widget_ComputeSizeStyle(LCUI_Widget w);

/** 根据阴影参数获取部件区域的横向偏移距离 */
LCUI_API float Widget_GetBoxShadowOffsetX(LCUI_Widget w);

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

LCUI_API void Widget_ComputeLimitSize(LCUI_Widget w);
LCUI_API void Widget_UpdateBoxPosition(LCUI_Widget w);

LCUI_API void Widget_UpdateCanvasBox(LCUI_Widget w);

LCUI_API void Widget_UpdateBoxSize(LCUI_Widget w);

LCUI_API void Widget_SetBorderBoxSize(LCUI_Widget w, float width, float height);

LCUI_API size_t LCUIWidget_ClearTrash(void);

Expand Down
10 changes: 9 additions & 1 deletion include/LCUI/gui/widget_layout.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@
#ifndef LCUI_WIDGET_LAYOUT_H
#define LCUI_WIDGET_LAYOUT_H

LCUI_API void Widget_DoLayout(LCUI_Widget w);
typedef struct LCUI_WidgetLayoutContextRec_ {
int invalid_box;
LCUI_BOOL can_render;
LCUI_BOOL should_add_invalid_area;
LCUI_WidgetBoxModelRec box;
LCUI_Widget container;
} LCUI_WidgetLayoutContextRec, *LCUI_WidgetLayoutContext;

LCUI_API void LCUIWidgetLayout_Reflow(LCUI_WidgetLayoutContext ctx);

#endif
36 changes: 31 additions & 5 deletions src/gui/metrics.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,41 @@ static LCUI_MetricsRec metrics;
float LCUIMetrics_Compute(float value, LCUI_StyleType type)
{
switch (type) {
case LCUI_STYPE_PX: break;
case LCUI_STYPE_DIP: value = value * metrics.density; break;
case LCUI_STYPE_SP: value = value * metrics.scaled_density; break;
case LCUI_STYPE_PT: value = value * metrics.dpi / 72.0f; break;
default: value = 0; break;
case LCUI_STYPE_PX:
break;
case LCUI_STYPE_DIP:
value = value * metrics.density;
break;
case LCUI_STYPE_SP:
value = value * metrics.scaled_density;
break;
case LCUI_STYPE_PT:
value = value * metrics.dpi / 72.0f;
break;
default:
value = 0;
break;
}
return value;
}

float LCUIMetrics_ComputeStyle(LCUI_Style style)
{
switch (style->type) {
case LCUI_STYPE_PX:
return style->val_px;
case LCUI_STYPE_DIP:
return style->val_dip * metrics.density;
case LCUI_STYPE_SP:
return style->val_sp * metrics.scaled_density;
case LCUI_STYPE_PT:
return style->val_pt * metrics.dpi / 72.0f;
default:
break;
}
return 0;
}

int LCUIMetrics_ComputeActual(float value, LCUI_StyleType type)
{
return iround(LCUIMetrics_Compute(value, type) * metrics.scale);
Expand Down
1 change: 1 addition & 0 deletions src/gui/widget/textview.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ static void TextView_UpdateStyle(LCUI_Widget w)
txt->style = style;
txt->tasks[TASK_UPDATE].is_valid = TRUE;
Widget_AddTask(w, LCUI_WTASK_USER);
Widget_AddTask(w, LCUI_WTASK_REFLOW);
}

static void TextView_UpdateLayerSize(LCUI_Widget w)
Expand Down
97 changes: 95 additions & 2 deletions src/gui/widget_base.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ LCUI_Widget LCUIWidget_New(const char *type)
void Widget_ExecDestroy(LCUI_Widget w)
{
if (w->parent) {
Widget_AddTask(w->parent, LCUI_WTASK_LAYOUT);
Widget_AddTask(w->parent, LCUI_WTASK_REFLOW);
Widget_Unlink(w);
}
Widget_DestroyBackground(w);
Expand Down Expand Up @@ -183,7 +183,7 @@ void Widget_Destroy(LCUI_Widget w)
node = node->next;
}
if (w->computed_style.position != SV_ABSOLUTE) {
Widget_AddTask(w->parent, LCUI_WTASK_LAYOUT);
Widget_AddTask(w->parent, LCUI_WTASK_REFLOW);
}
Widget_InvalidateArea(w, NULL, SV_GRAPH_BOX);
Widget_AddToTrash(w);
Expand Down Expand Up @@ -503,6 +503,7 @@ void Widget_AddState(LCUI_Widget w, LCUI_WidgetState state)
float Widget_ComputeXMetric(LCUI_Widget w, int key)
{
LCUI_Style s = &w->style->sheet[key];

if (s->type == LCUI_STYPE_SCALE) {
if (!w->parent) {
return 0;
Expand All @@ -518,6 +519,7 @@ float Widget_ComputeXMetric(LCUI_Widget w, int key)
float Widget_ComputeYMetric(LCUI_Widget w, int key)
{
LCUI_Style s = &w->style->sheet[key];

if (s->type == LCUI_STYPE_SCALE) {
if (!w->parent) {
return 0;
Expand Down Expand Up @@ -621,6 +623,97 @@ void Widget_BindProperty(LCUI_Widget w, const char *name, LCUI_Object value)
}
}

void Widget_UpdateBoxPosition(LCUI_Widget w)
{
float x = w->layout_x;
float y = w->layout_y;

switch (w->computed_style.position) {
case SV_ABSOLUTE:
if (!Widget_HasAutoStyle(w, key_left)) {
x = w->computed_style.left;
} else if (!Widget_HasAutoStyle(w, key_right)) {
if (w->parent) {
x = w->parent->box.border.width - w->width;
}
x -= w->computed_style.right;
}
if (!Widget_HasAutoStyle(w, key_top)) {
y = w->computed_style.top;
} else if (!Widget_HasAutoStyle(w, key_bottom)) {
if (w->parent) {
y = w->parent->box.border.height - w->height;
}
y -= w->computed_style.bottom;
}
break;
case SV_RELATIVE:
if (!Widget_HasAutoStyle(w, key_left)) {
x += w->computed_style.left;
} else if (!Widget_HasAutoStyle(w, key_right)) {
x -= w->computed_style.right;
}
if (!Widget_HasAutoStyle(w, key_top)) {
y += w->computed_style.top;
} else if (!Widget_HasAutoStyle(w, key_bottom)) {
y -= w->computed_style.bottom;
}
case SV_STATIC:
default:
break;
}
w->box.outer.x = x;
w->box.outer.y = y;
w->x = x + w->margin.left;
w->y = y + w->margin.top;
w->box.border.x = w->x;
w->box.border.y = w->y;
w->box.padding.x = w->x + w->computed_style.border.left.width;
w->box.padding.y = w->y + w->computed_style.border.top.width;
w->box.content.x = w->box.padding.x + w->padding.left;
w->box.content.y = w->box.padding.y + w->padding.top;
w->box.canvas.x = w->x - Widget_GetBoxShadowOffsetX(w);
w->box.canvas.y = w->y - Widget_GetBoxShadowOffsetY(w);
}

void Widget_UpdateCanvasBox(LCUI_Widget w)
{
w->box.canvas.x = w->box.border.x - Widget_GetBoxShadowOffsetX(w);
w->box.canvas.y = w->box.border.y - Widget_GetBoxShadowOffsetY(w);
w->box.canvas.width = Widget_GetCanvasWidth(w);
w->box.canvas.height = Widget_GetCanvasHeight(w);
}

void Widget_UpdateBoxSize(LCUI_Widget w)
{
const float padding_x = w->padding.left + w->padding.right;
const float padding_y = w->padding.top + w->padding.bottom;
const float border_x = w->computed_style.border.left.width +
w->computed_style.border.right.width;
const float border_y = w->computed_style.border.top.width +
w->computed_style.border.bottom.width;
const float margin_x = w->margin.left + w->margin.right;
const float margin_y = w->margin.top + w->margin.bottom;

w->box.border.width = w->width;
w->box.border.height = w->height;
w->box.padding.width = w->box.border.width - border_x;
w->box.padding.height = w->box.border.height - border_y;
w->box.content.width = w->box.padding.width - padding_x;
w->box.content.height = w->box.padding.height - padding_y;
w->box.outer.width = w->box.border.width + margin_x;
w->box.outer.height = w->box.border.height + margin_y;
w->box.canvas.width = Widget_GetCanvasWidth(w);
w->box.canvas.height = Widget_GetCanvasHeight(w);
}

void Widget_SetBorderBoxSize(LCUI_Widget w, float width, float height)
{
w->width = Widget_GetLimitedWidth(w, width);
w->height = Widget_GetLimitedHeight(w, height);
Widget_UpdateBoxSize(w);
}

void LCUIWidget_InitBase(void)
{
LinkedList_Init(&LCUIWidget.trash);
Expand Down
Loading

0 comments on commit 24e89aa

Please sign in to comment.