void Widget_UpdateMargin(LCUI_Widget w) { int i; LCUI_BoundBox *mbox = &w->computed_style.margin; struct { LCUI_Style sval; float *fval; int key; } pd_map[4] = { { &mbox->top, &w->margin.top, key_margin_top }, { &mbox->right, &w->margin.right, key_margin_right }, { &mbox->bottom, &w->margin.bottom, key_margin_bottom }, { &mbox->left, &w->margin.left, key_margin_left } }; for (i = 0; i < 4; ++i) { LCUI_Style s = &w->style->sheet[pd_map[i].key]; if (!s->is_valid) { pd_map[i].sval->type = LCUI_STYPE_PX; pd_map[i].sval->px = 0.0; *pd_map[i].fval = 0.0; continue; } *pd_map[i].sval = *s; *pd_map[i].fval = LCUIMetrics_Compute(s->value, s->type); } /* 如果有父级部件,则处理 margin-left 和 margin-right 的值 */ if (w->parent) { float width = w->parent->box.content.width; if (Widget_HasAutoStyle(w, key_margin_left)) { if (Widget_HasAutoStyle(w, key_margin_right)) { w->margin.left = (width - w->width) / 2; if (w->margin.left < 0) { w->margin.left = 0; } w->margin.right = w->margin.left; } else { w->margin.left = width - w->width; w->margin.left -= w->margin.right; if (w->margin.left < 0) { w->margin.left = 0; } } } else if (Widget_HasAutoStyle(w, key_margin_right)) { w->margin.right = width - w->width; w->margin.right -= w->margin.left; if (w->margin.right < 0) { w->margin.right = 0; } } } if (w->parent) { if (Widget_HasAutoStyle(w->parent, key_width) || Widget_HasAutoStyle(w->parent, key_height)) { Widget_AddTask(w->parent, LCUI_WTASK_RESIZE); } if (w->computed_style.display != SV_NONE && w->computed_style.position == SV_STATIC) { Widget_UpdateLayout(w->parent); } } Widget_AddTask(w, LCUI_WTASK_POSITION); }
static void Widget_UpdateChildrenSize(LCUI_Widget w) { LinkedListNode *node; for (LinkedList_Each(node, &w->children)) { LCUI_Widget child = node->data; LCUI_StyleSheet s = child->style; if (Widget_HasFillAvailableWidth(child)) { Widget_AddTask(child, LCUI_WTASK_RESIZE); } else if (Widget_HasScaleSize(child)) { Widget_AddTask(child, LCUI_WTASK_RESIZE); } if (Widget_HasAbsolutePosition(child)) { if (s->sheet[key_right].is_valid || s->sheet[key_bottom].is_valid || CheckStyleType(s, key_left, scale) || CheckStyleType(s, key_top, scale)) { Widget_AddTask(child, LCUI_WTASK_POSITION); } } if (Widget_HasAutoStyle(child, key_margin_left) || Widget_HasAutoStyle(child, key_margin_right)) { Widget_AddTask(child, LCUI_WTASK_MARGIN); } if (child->computed_style.vertical_align != SV_TOP) { Widget_AddTask(child, LCUI_WTASK_POSITION); } } }
static void AsyncLoadImage( LCUI_Widget widget, const char *path ) { ImageCache cache; LCUI_Task task = {0}; LCUI_Style *s = &widget->cached_style->sheet[key_background_image]; if( !is_inited ) { RBTree_Init( &images ); RBTree_Init( &refs ); RBTree_OnJudge( &refs, OnCompareWidget ); RBTree_OnJudge( &images, OnComparePath ); RBTree_OnDestroy( &refs, free ); RBTree_OnDestroy( &images, OnDestroyCache ); is_inited = TRUE; } if( s->is_valid && s->type == SVT_STRING ) { cache = RBTree_CustomGetData( &images, s->string ); if( cache ) { DelRef( widget, cache ); } } cache = RBTree_CustomGetData( &images, path ); if( cache ) { AddRef( widget, cache ); Graph_Quote( &widget->computed_style.background.image, &cache->image, NULL ); Widget_AddTask( widget, WTT_BODY ); return; } task.func = ExecLoadImage; task.arg[0] = widget; task.arg[1] = strdup( path ); LCUI_AddTask( &task ); }
/** 计算背景样式 */ void Widget_ComputeBackgroundStyle( LCUI_Widget widget ) { LCUI_Style *s; LCUI_StyleSheet ss = widget->style; LCUI_Background *bg = &widget->computed_style.background; int key = key_background_start + 1; for( ; key < key_background_end; ++key ) { s = &ss->sheet[key]; if( !s->is_valid ) { continue; } switch( key ) { case key_background_color: bg->color = s->color; break; case key_background_image: switch( s->type ) { case SVT_IMAGE: if( !s->image ) { Graph_Init( &bg->image ); break; } Graph_Quote( &bg->image, s->image, NULL ); break; case SVT_STRING: AsyncLoadImage( widget, s->string ); default: break; } break; case key_background_position: bg->position.using_value = TRUE; bg->position.value = s->value; break; case key_background_position_x: bg->position.using_value = FALSE; bg->position.x = *s; break; case key_background_position_y: bg->position.using_value = FALSE; bg->position.y = *s; break; case key_background_size: bg->size.using_value = TRUE; bg->position.value = s->value; break; case key_background_size_width: bg->size.using_value = FALSE; bg->size.w = *s; break; case key_background_size_height: bg->size.using_value = FALSE; bg->size.h = *s; break; default: break; } } Widget_AddTask( widget, WTT_BODY ); }
/** 更新当前任务状态,确保部件的任务能够被处理到 */ void Widget_UpdateTaskStatus( LCUI_Widget widget ) { int i; for( i = 0; i < WTT_TOTAL_NUM; ++i ) { if( widget->task.buffer[i] ) { widget->task.for_self = TRUE; Widget_AddTask( widget, widget->task.buffer[i] ); } } }
void Widget_AddTaskForChildren(LCUI_Widget widget, int task) { LCUI_Widget child; LinkedListNode *node; widget->task.for_children = TRUE; for (LinkedList_Each(node, &widget->children)) { child = node->data; Widget_AddTask(child, task); Widget_AddTaskForChildren(child, task); } }
static void Widget_SendResizeEvent(LCUI_Widget w) { LCUI_WidgetEventRec e; e.target = w; e.data = NULL; e.type = LCUI_WEVENT_RESIZE; e.cancel_bubble = TRUE; Widget_TriggerEvent(w, &e, NULL); Widget_AddTask(w, LCUI_WTASK_REFRESH); Widget_PostSurfaceEvent(w, LCUI_WEVENT_RESIZE, FALSE); }
void Widget_ExecUpdateZIndex(LCUI_Widget w) { int z_index; LinkedList *list; LinkedListNode *cnode, *csnode, *snode; LCUI_Style s = &w->style->sheet[key_z_index]; if (s->is_valid && s->type == LCUI_STYPE_VALUE) { z_index = s->val_int; } else { z_index = 0; } if (!w->parent) { return; } if (w->state == LCUI_WSTATE_NORMAL) { if (w->computed_style.z_index == z_index) { return; } } w->computed_style.z_index = z_index; snode = &w->node_show; list = &w->parent->children_show; LinkedList_Unlink(list, snode); for (LinkedList_Each(cnode, list)) { LCUI_Widget child = cnode->data; LCUI_WidgetStyle *ccs = &child->computed_style; csnode = &child->node_show; if (w->computed_style.z_index < ccs->z_index) { continue; } else if (w->computed_style.z_index == ccs->z_index) { if (w->computed_style.position == ccs->position) { if (w->index < child->index) { continue; } } else if (w->computed_style.position < ccs->position) { continue; } } LinkedList_Link(list, csnode->prev, snode); break; } if (!cnode) { LinkedList_AppendNode(list, snode); } if (w->computed_style.position != SV_STATIC) { Widget_AddTask(w, LCUI_WTASK_REFRESH); } }
static LCUI_BOOL Widget_ComputeStaticSize(LCUI_Widget w, float *width, float *height) { LCUI_WidgetBoxModelRec *box = &w->box; LCUI_WidgetStyle *style = &w->computed_style; /* If it is non-static layout */ if (style->display == SV_NONE || style->position == SV_ABSOLUTE) { return FALSE; } if (Widget_HasParentDependentWidth(w)) { /* Compute the full size of child widget */ Widget_AutoSize(w); /* Recompute the actual size of child widget later */ Widget_AddTask(w, LCUI_WTASK_RESIZE); } /* If its width is a percentage, it ignores the effect of its outer * and inner spacing on the static width. */ if (Widget_CheckStyleType(w, key_width, scale)) { if (style->box_sizing == SV_BORDER_BOX) { *width = box->border.x + box->border.width; } else { *width = box->content.x + box->content.width; *width -= box->content.x - box->border.x; } *width -= box->outer.x - box->border.x; } else if (box->outer.width <= 0) { return FALSE; } else { *width = box->outer.x + box->outer.width; } if (Widget_CheckStyleType(w, key_height, scale)) { if (style->box_sizing == SV_BORDER_BOX) { *height = box->border.y + box->border.height; } else { *height = box->content.y + box->content.height; *height -= box->content.y - box->border.y; } *height -= box->outer.y - box->border.y; } else if (box->outer.height <= 0) { return FALSE; } else { *height = box->outer.y + box->outer.height; } return TRUE; }
void Widget_SetTitleW(LCUI_Widget w, const wchar_t *title) { int len; wchar_t *new_title, *old_title; len = wcslen(title) + 1; new_title = (wchar_t *)malloc(sizeof(wchar_t) * len); if (!new_title) { return; } wcsncpy(new_title, title, len); old_title = w->title; w->title = new_title; if (old_title) { free(old_title); } Widget_AddTask(w, LCUI_WTASK_TITLE); }
LCUI_Widget LCUIWidget_New(const char *type) { ASSIGN(widget, LCUI_Widget); Widget_Init(widget); widget->node.data = widget; widget->node_show.data = widget; widget->node.next = widget->node.prev = NULL; widget->node_show.next = widget->node_show.prev = NULL; if (type) { widget->proto = LCUIWidget_GetPrototype(type); if (widget->proto) { widget->type = widget->proto->name; widget->proto->init(widget); } else { widget->type = strdup2(type); } } Widget_AddTask(widget, LCUI_WTASK_REFRESH_STYLE); return widget; }
static void ExecLoadImage( void *arg1, void *arg2 ) { char *path = arg2; LCUI_Graph image; LCUI_Widget widget = arg1; ImageCache cache; Graph_Init( &image ); if( Graph_LoadImage( path, &image ) != 0 ) { return; } cache = NEW(ImageCacheRec, 1); cache->ref_count = 0; cache->image = image; cache->path = path; RBTree_CustomInsert( &images, path, cache ); AddRef( widget, cache ); Graph_Quote( &widget->computed_style.background.image, &cache->image, NULL ); Widget_AddTask( widget, WTT_BODY ); }
void Widget_Empty(LCUI_Widget w) { LCUI_Widget root = w; while (root->parent) { root = root->parent; } if (root == LCUIWidget.root) { LinkedListNode *next, *node; node = w->children.head.next; while (node) { next = node->next; Widget_AddToTrash(node->data); node = next; } Widget_InvalidateArea(w, NULL, SV_GRAPH_BOX); Widget_AddTask(w, LCUI_WTASK_LAYOUT); } else { Widget_DestroyChildren(w); } }
void Widget_UpdateZIndex(LCUI_Widget w) { Widget_AddTask(w, LCUI_WTASK_ZINDEX); }
void Widget_UpdateSize(LCUI_Widget w) { int box_sizing; LCUI_RectF rect = w->box.canvas; LCUI_Rect2F padding = w->padding; box_sizing = ComputeStyleOption(w, key_box_sizing, SV_CONTENT_BOX); w->computed_style.box_sizing = box_sizing; Widget_ComputePadding(w); Widget_ComputeSize(w); /* 如果左右外间距是 auto 类型的,则需要计算外间距 */ if (w->style->sheet[key_margin_left].is_valid && w->style->sheet[key_margin_left].type == LCUI_STYPE_AUTO) { Widget_UpdateMargin(w); } else if (w->style->sheet[key_margin_right].is_valid && w->style->sheet[key_margin_right].type == LCUI_STYPE_AUTO) { Widget_UpdateMargin(w); } /* 若尺寸无变化则不继续处理 */ if (rect.width == w->box.canvas.width && rect.height == w->box.canvas.height && padding.top == w->padding.top && padding.right == w->padding.right && padding.bottom == w->padding.bottom && padding.left == w->padding.left) { return; } /* 若在变化前后的宽高中至少有一个为 0,则不继续处理 */ if ((w->box.canvas.width <= 0 || w->box.canvas.height <= 0) && (rect.width <= 0 || rect.height <= 0)) { return; } Widget_UpdateLayout(w); /* 如果垂直对齐方式不为顶部对齐 */ if (w->computed_style.vertical_align != SV_TOP) { Widget_UpdatePosition(w); } else if (w->computed_style.position == SV_ABSOLUTE) { /* 如果是绝对定位,且指定了右间距或底间距 */ if (!Widget_HasAutoStyle(w, key_right) || !Widget_HasAutoStyle(w, key_bottom)) { Widget_UpdatePosition(w); } } if (w->parent) { /* Mark old area and new area need to repaint */ Widget_InvalidateArea(w->parent, &rect, SV_PADDING_BOX); rect.width = w->box.canvas.width; rect.height = w->box.canvas.height; Widget_InvalidateArea(w->parent, &rect, SV_PADDING_BOX); /* If its width depends on the parent, there is no need to * repeatedly update the parent size and layout. * See Widget_ComputeStaticSize() for more details. */ if (!Widget_HasParentDependentWidth(w)) { if (Widget_HasAutoStyle(w->parent, key_width) || Widget_HasAutoStyle(w->parent, key_height)) { Widget_AddTask(w->parent, LCUI_WTASK_RESIZE); } if (w->computed_style.display != SV_NONE && w->computed_style.position == SV_STATIC) { Widget_UpdateLayout(w->parent); } } } Widget_SendResizeEvent(w); Widget_UpdateChildrenSize(w); }