System/Libraries/Css+Html: Handle display: inline-block
This commit is contained in:
parent
49666d7ea9
commit
1cf0007b0b
3 changed files with 186 additions and 87 deletions
|
@ -1,3 +1,8 @@
|
||||||
|
#define CSS_DISPLAY_NONE 0
|
||||||
|
#define CSS_DISPLAY_BLOCK 1
|
||||||
|
#define CSS_DISPLAY_INLINE 2
|
||||||
|
#define CSS_DISPLAY_INLINE_BLOCK 3
|
||||||
|
|
||||||
#define CSS_TEXT_ALIGN_CENTER 1
|
#define CSS_TEXT_ALIGN_CENTER 1
|
||||||
#define CSS_TEXT_ALIGN_RIGHT 2
|
#define CSS_TEXT_ALIGN_RIGHT 2
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,14 @@ class @html_lazyload_image
|
||||||
@html_lazyload_image* next;
|
@html_lazyload_image* next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class @renderer_reflow_state
|
||||||
|
{
|
||||||
|
I32 x1;
|
||||||
|
I32 x2;
|
||||||
|
I32 y1;
|
||||||
|
Widget* parent;
|
||||||
|
};
|
||||||
|
|
||||||
class @html_renderer
|
class @html_renderer
|
||||||
{
|
{
|
||||||
CTask* task;
|
CTask* task;
|
||||||
|
@ -79,6 +87,8 @@ class @html_renderer
|
||||||
Context2D* link_pointer;
|
Context2D* link_pointer;
|
||||||
U64 link_callback;
|
U64 link_callback;
|
||||||
U64 form_submit_callback;
|
U64 form_submit_callback;
|
||||||
|
@renderer_reflow_state state[256];
|
||||||
|
I64 state_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define HtmlRenderer @html_renderer
|
#define HtmlRenderer @html_renderer
|
||||||
|
@ -384,8 +394,15 @@ Bool @apply_css_rules_to_node(@html_dom_node* node, HtmlRenderer* renderer)
|
||||||
if (!StrICmp(values->@(0), "none")) {
|
if (!StrICmp(values->@(0), "none")) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
if (StrICmp(values->@(0), "block")) {
|
if (!StrICmp(values->@(0), "block")) {
|
||||||
node->display_block = FALSE;
|
node->display = CSS_DISPLAY_BLOCK;
|
||||||
|
} else if (!StrICmp(values->@(0), "inline")) {
|
||||||
|
node->display = CSS_DISPLAY_INLINE;
|
||||||
|
} else if (!StrICmp(values->@(0), "inline-block")) {
|
||||||
|
node->display = CSS_DISPLAY_INLINE_BLOCK;
|
||||||
|
} else {
|
||||||
|
// FIXME: unimplemented; default to inline
|
||||||
|
node->display = CSS_DISPLAY_INLINE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -907,6 +924,7 @@ U0 @render_node_text(@html_dom_node* node, HtmlRenderer* renderer)
|
||||||
|
|
||||||
fragment_widget->width = fragment_widget->ctx->width;
|
fragment_widget->width = fragment_widget->ctx->width;
|
||||||
fragment_widget->height = fragment_widget->ctx->height;
|
fragment_widget->height = fragment_widget->ctx->height;
|
||||||
|
fragment_widget->fast_copy = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -972,9 +990,8 @@ U0 @render_node_list(@html_dom_node* node, HtmlRenderer* renderer)
|
||||||
Context2DWidget* block_widget;
|
Context2DWidget* block_widget;
|
||||||
Context2DWidget* img_widget;
|
Context2DWidget* img_widget;
|
||||||
|
|
||||||
// FIXME: Resolve if display: block is set
|
|
||||||
if (block_level_element_tag_names->contains(node->tagName)) {
|
if (block_level_element_tag_names->contains(node->tagName)) {
|
||||||
node->display_block = TRUE;
|
node->display = CSS_DISPLAY_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StrICmp(node->tagName, "InternalTextNode"))
|
if (StrICmp(node->tagName, "InternalTextNode"))
|
||||||
|
@ -988,14 +1005,20 @@ U0 @render_node_list(@html_dom_node* node, HtmlRenderer* renderer)
|
||||||
if (renderer->debug && StrICmp(node->tagName, "InternalTextNode")) {
|
if (renderer->debug && StrICmp(node->tagName, "InternalTextNode")) {
|
||||||
for (i = 0; i < renderer->indent; i++)
|
for (i = 0; i < renderer->indent; i++)
|
||||||
" ";
|
" ";
|
||||||
"<%s> textAlign: %d, width: %d, height: %d, bg: #0x%06x, color: #0x%06x, fontFamily: '%s', fontSize: %d, fontWeight: %d, display_block: %d\n",
|
"<%s> textAlign: %d, width: %d, height: %d, bg: #0x%06x, color: #0x%06x, fontFamily: '%s', fontSize: %d, fontWeight: %d, display: %d\n",
|
||||||
node->tagName, node->textAlign, node->width, node->height, node->backgroundColor, node->color, node->fontFamily, node->fontSize, node->fontWeight, node->display_block;
|
node->tagName, node->textAlign, node->width, node->height, node->backgroundColor, node->color, node->fontFamily, node->fontSize, node->fontWeight, node->display;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert a block widget for the element's opening tag
|
// Insert a block widget for the element's opening tag
|
||||||
if (node->display_block) {
|
if (node->display == CSS_DISPLAY_BLOCK || node->display == CSS_DISPLAY_INLINE_BLOCK) {
|
||||||
block_widget = Gui.CreateWidget(renderer->win, WIDGET_TYPE_CONTEXT2D,
|
block_widget = Gui.CreateWidget(renderer->win, WIDGET_TYPE_CONTEXT2D,
|
||||||
U64_MAX, U64_MAX, 0, 0);
|
U64_MAX, U64_MAX, 0, 0);
|
||||||
|
if (node->width > 0 && node->height > 0) {
|
||||||
|
block_widget->ctx = NewContext2D(node->width, node->height)->fill(node->backgroundColor);
|
||||||
|
block_widget->width = block_widget->ctx->width;
|
||||||
|
block_widget->height = block_widget->ctx->height;
|
||||||
|
block_widget->fast_copy = TRUE;
|
||||||
|
}
|
||||||
block_widget->data = node;
|
block_widget->data = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1072,123 +1095,194 @@ U0 @render_node_list(@html_dom_node* node, HtmlRenderer* renderer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert a block widget for the element's closing tag
|
// Insert a block widget for the element's closing tag
|
||||||
if (node->display_block || !StrICmp(node->tagName, "br")) {
|
if (node->display == CSS_DISPLAY_BLOCK || node->display == CSS_DISPLAY_INLINE_BLOCK || !StrICmp(node->tagName, "br")) {
|
||||||
block_widget = Gui.CreateWidget(renderer->win, WIDGET_TYPE_CONTEXT2D,
|
block_widget = Gui.CreateWidget(renderer->win, WIDGET_TYPE_CONTEXT2D,
|
||||||
U64_MAX, U64_MAX, 0, 0);
|
U64_MAX, U64_MAX, 0, 0);
|
||||||
|
if (node->display == CSS_DISPLAY_BLOCK || node->display == CSS_DISPLAY_INLINE_BLOCK) {
|
||||||
|
block_widget->width = -0xbeef;
|
||||||
|
block_widget->height = -0xcafe;
|
||||||
|
}
|
||||||
block_widget->data = node;
|
block_widget->data = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
--renderer->indent;
|
--renderer->indent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
U0 @reflow_push_state(HtmlRenderer* renderer, I32 x1, I32 x2, I32 y1, Widget* parent)
|
||||||
|
{
|
||||||
|
++renderer->state_index;
|
||||||
|
@renderer_reflow_state* state = &renderer->state[renderer->state_index];
|
||||||
|
state->x1 = x1;
|
||||||
|
state->x2 = x2;
|
||||||
|
state->y1 = y1;
|
||||||
|
state->parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
U0 @reflow_pop_state(HtmlRenderer* renderer, I32* x1, I32* x2, I32* y1, U64* parent)
|
||||||
|
{
|
||||||
|
@renderer_reflow_state* state = &renderer->state[renderer->state_index];
|
||||||
|
if (x1)
|
||||||
|
*x1 = state->x1;
|
||||||
|
if (x2)
|
||||||
|
*x2 = state->x2;
|
||||||
|
if (y1)
|
||||||
|
*y1 = state->y1;
|
||||||
|
if (parent)
|
||||||
|
*parent = state->parent;
|
||||||
|
--renderer->state_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool @reflow_widget_is_closing_tag(Widget* widget)
|
||||||
|
{
|
||||||
|
if (!widget)
|
||||||
|
return FALSE;
|
||||||
|
return (widget->width == -0xbeef && widget->height == -0xcafe);
|
||||||
|
}
|
||||||
|
|
||||||
|
I64 @reflow_cumulative_width(@window_widgets_list* wl_item, I64 line_break_width)
|
||||||
|
{
|
||||||
|
@html_dom_node* node = NULL;
|
||||||
|
I64 width = wl_item->widget->width;
|
||||||
|
wl_item = wl_item->next;
|
||||||
|
while (wl_item && wl_item->widget && wl_item->widget->data && width + wl_item->widget->width < line_break_width) {
|
||||||
|
node = wl_item->widget->data;
|
||||||
|
if (node->display == CSS_DISPLAY_BLOCK || !StrICmp(node->tagName, "br") || @reflow_widget_is_closing_tag(wl_item->widget)) {
|
||||||
|
wl_item = NULL;
|
||||||
|
} else {
|
||||||
|
width += wl_item->widget->width;
|
||||||
|
wl_item = wl_item->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
U0 @reflow_node_list(HtmlRenderer* renderer)
|
U0 @reflow_node_list(HtmlRenderer* renderer)
|
||||||
{
|
{
|
||||||
if (!renderer)
|
if (!renderer || !renderer->win || !renderer->widgets_base)
|
||||||
return;
|
return;
|
||||||
if (!renderer->widgets_base)
|
|
||||||
return;
|
|
||||||
|
|
||||||
renderer->render_x = 0;
|
|
||||||
renderer->render_y = 0;
|
|
||||||
renderer->max_line_height = RENDERER_DEFAULT_MAX_LINE_HEIGHT;
|
|
||||||
|
|
||||||
VerticalScrollBarWidget* vscroll = renderer->vertical_scroll_widget;
|
|
||||||
|
|
||||||
renderer->background_widget->ctx = renderer->background_ctx;
|
renderer->background_widget->ctx = renderer->background_ctx;
|
||||||
|
renderer->state_index = -1;
|
||||||
|
|
||||||
|
I64 x1 = 0;
|
||||||
|
I64 y1 = 0;
|
||||||
|
I64 x2 = renderer->win->width;
|
||||||
|
I64 indent_x = 0;
|
||||||
|
Widget* parent = NULL;
|
||||||
|
Widget* widget = NULL;
|
||||||
|
|
||||||
|
@reflow_push_state(renderer, x1, x2, y1, parent);
|
||||||
|
|
||||||
|
renderer->render_x = x1;
|
||||||
|
renderer->render_y = y1;
|
||||||
|
renderer->max_line_height = RENDERER_DEFAULT_MAX_LINE_HEIGHT;
|
||||||
|
|
||||||
|
@html_dom_node* node = NULL;
|
||||||
|
@window_widgets_list* wl_item = renderer->widgets_base->next;
|
||||||
|
|
||||||
|
VerticalScrollBarWidget* vscroll = renderer->vertical_scroll_widget;
|
||||||
I64 origin_y = renderer->background_widget->y;
|
I64 origin_y = renderer->background_widget->y;
|
||||||
I64 offset_y = origin_y;
|
I64 offset_y = origin_y;
|
||||||
I64 line_break_max = 0;
|
|
||||||
I64 minimum_vscroll_value = 0;
|
I64 minimum_vscroll_value = 0;
|
||||||
if (vscroll && vscroll->max && vscroll->value) {
|
if (vscroll && vscroll->max && vscroll->value) {
|
||||||
minimum_vscroll_value = ToI64((vscroll->max * 1.0) / (vscroll->height * 1.0) * (16 * 1.0));
|
minimum_vscroll_value = ToI64((vscroll->max * 1.0) / (vscroll->height * 1.0) * (16 * 1.0));
|
||||||
offset_y -= vscroll->value - minimum_vscroll_value;
|
offset_y -= vscroll->value - minimum_vscroll_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@html_dom_node* node;
|
while (wl_item) {
|
||||||
@html_dom_node* cumulative_node;
|
widget = wl_item->widget;
|
||||||
I64 cumulative_widgets_width;
|
if (!widget)
|
||||||
@window_widgets_list* widget_list_item = renderer->widgets_base->next;
|
goto reflow_next_wl_item;
|
||||||
@window_widgets_list* cumulative_list_item;
|
|
||||||
Widget* widget;
|
|
||||||
|
|
||||||
while (widget_list_item) {
|
|
||||||
widget = widget_list_item->widget;
|
|
||||||
node = widget->data;
|
node = widget->data;
|
||||||
|
if (!node)
|
||||||
|
goto reflow_next_wl_item;
|
||||||
|
|
||||||
if (node) {
|
switch (node->display) {
|
||||||
|
case CSS_DISPLAY_BLOCK:
|
||||||
if (!StrICmp(node->tagName, "br")) {
|
case CSS_DISPLAY_INLINE_BLOCK:
|
||||||
renderer->render_x = 0;
|
if (@reflow_widget_is_closing_tag(widget)) {
|
||||||
renderer->render_y += renderer->max_line_height;
|
if (node->display == CSS_DISPLAY_INLINE_BLOCK)
|
||||||
renderer->max_line_height = RENDERER_DEFAULT_MAX_LINE_HEIGHT;
|
renderer->render_x = x2;
|
||||||
goto reflow_next_list_item;
|
@reflow_pop_state(renderer, &x1, &x2, &y1, &parent);
|
||||||
}
|
if (node->display == CSS_DISPLAY_BLOCK) {
|
||||||
|
renderer->render_x = x1;
|
||||||
if (node->display_block) {
|
|
||||||
if (renderer->render_x) {
|
|
||||||
renderer->render_x = 0;
|
|
||||||
renderer->render_y += renderer->max_line_height;
|
renderer->render_y += renderer->max_line_height;
|
||||||
renderer->max_line_height = RENDERER_DEFAULT_MAX_LINE_HEIGHT;
|
renderer->max_line_height = RENDERER_DEFAULT_MAX_LINE_HEIGHT;
|
||||||
}
|
while (renderer->render_y < y1) {
|
||||||
goto reflow_next_list_item;
|
++renderer->render_y;
|
||||||
}
|
|
||||||
|
|
||||||
if (!renderer->render_x && node->textAlign) {
|
|
||||||
// Begin calculating x offset for text alignment
|
|
||||||
cumulative_widgets_width = widget->width;
|
|
||||||
cumulative_list_item = widget_list_item->next;
|
|
||||||
while (cumulative_list_item && cumulative_list_item->widget && cumulative_list_item->widget->data) {
|
|
||||||
cumulative_node = cumulative_list_item->widget->data;
|
|
||||||
if (cumulative_node->display_block || !StrICmp(cumulative_node->tagName, "br")) {
|
|
||||||
cumulative_list_item = NULL;
|
|
||||||
} else {
|
|
||||||
cumulative_widgets_width += cumulative_list_item->widget->width;
|
|
||||||
cumulative_list_item = cumulative_list_item->next;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (node->textAlign) {
|
} else {
|
||||||
case CSS_TEXT_ALIGN_CENTER:
|
widget->x = renderer->render_x;
|
||||||
renderer->render_x = (renderer->win->width / 2) - (cumulative_widgets_width / 2);
|
widget->y = renderer->render_y + offset_y;
|
||||||
break;
|
@reflow_push_state(renderer, x1, x2, renderer->render_y + widget->height, parent);
|
||||||
case CSS_TEXT_ALIGN_RIGHT:
|
parent = widget;
|
||||||
renderer->render_x = -8 + renderer->win->width - cumulative_widgets_width;
|
if (node->display == CSS_DISPLAY_INLINE_BLOCK) {
|
||||||
break;
|
x1 = renderer->render_x;
|
||||||
default:
|
}
|
||||||
"ERROR: Invalid node->textAlign value: %d\n", node->textAlign;
|
if (node->display == CSS_DISPLAY_BLOCK) {
|
||||||
PressAKey;
|
if (renderer->render_x > x1) {
|
||||||
break;
|
renderer->render_y += renderer->max_line_height;
|
||||||
|
}
|
||||||
|
renderer->render_x = x1;
|
||||||
|
renderer->max_line_height = RENDERER_DEFAULT_MAX_LINE_HEIGHT;
|
||||||
|
}
|
||||||
|
if (widget->width > 0) {
|
||||||
|
x2 = widget->x + widget->width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
goto reflow_next_wl_item;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
line_break_max = 8;
|
if (!StrICmp(node->tagName, "br")) {
|
||||||
if (!node->textAlign) {
|
renderer->render_x = x1;
|
||||||
line_break_max += widget->width + 16;
|
renderer->render_y += renderer->max_line_height;
|
||||||
}
|
renderer->max_line_height = RENDERER_DEFAULT_MAX_LINE_HEIGHT;
|
||||||
|
goto reflow_next_wl_item;
|
||||||
|
}
|
||||||
|
|
||||||
widget->x = renderer->render_x;
|
if (renderer->render_x == x1 && node->textAlign) {
|
||||||
widget->y = renderer->render_y + offset_y;
|
indent_x = @reflow_cumulative_width(wl_item, x2 - x1);
|
||||||
|
switch (node->textAlign) {
|
||||||
if (@self_or_ancestor_matches_tag_name(node, "a")) {
|
case CSS_TEXT_ALIGN_CENTER:
|
||||||
widget->pointer = renderer->link_pointer;
|
renderer->render_x = ((x2 - x1) / 2) - (indent_x / 2);
|
||||||
Gui.Widget.SetCallback(widget, "clicked", renderer->link_callback);
|
break;
|
||||||
}
|
case CSS_TEXT_ALIGN_RIGHT:
|
||||||
|
renderer->render_x = -8 + x2 - indent_x;
|
||||||
renderer->render_x += widget->width;
|
break;
|
||||||
renderer->max_line_height = Max(renderer->max_line_height, widget->height);
|
default:
|
||||||
|
// Invalid node->textAlign value; default to text-align: left;
|
||||||
if (renderer->render_x > renderer->win->width - line_break_max) {
|
break;
|
||||||
renderer->render_x = 0;
|
|
||||||
renderer->render_y += renderer->max_line_height;
|
|
||||||
renderer->max_line_height = RENDERER_DEFAULT_MAX_LINE_HEIGHT;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reflow_next_list_item:
|
|
||||||
|
|
||||||
widget_list_item = widget_list_item->next;
|
if (renderer->render_x > x2 - widget->width) {
|
||||||
|
renderer->render_x = x1;
|
||||||
|
renderer->render_y += renderer->max_line_height;
|
||||||
|
renderer->max_line_height = RENDERER_DEFAULT_MAX_LINE_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
widget->x = renderer->render_x;
|
||||||
|
widget->y = renderer->render_y + offset_y;
|
||||||
|
|
||||||
|
if (@self_or_ancestor_matches_tag_name(node, "a")) {
|
||||||
|
widget->pointer = renderer->link_pointer;
|
||||||
|
Gui.Widget.SetCallback(widget, "clicked", renderer->link_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer->render_x += widget->width;
|
||||||
|
renderer->max_line_height = Max(renderer->max_line_height, widget->height);
|
||||||
|
|
||||||
|
reflow_next_wl_item:
|
||||||
|
wl_item = wl_item->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (renderer->render_x) {
|
if (renderer->render_x) {
|
||||||
renderer->render_x = 0;
|
renderer->render_x = x1;
|
||||||
renderer->render_y += renderer->max_line_height;
|
renderer->render_y += renderer->max_line_height;
|
||||||
renderer->max_line_height = RENDERER_DEFAULT_MAX_LINE_HEIGHT;
|
renderer->max_line_height = RENDERER_DEFAULT_MAX_LINE_HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ class @html_dom_node : JsonElement
|
||||||
I64 fontSize;
|
I64 fontSize;
|
||||||
I64 fontWeight;
|
I64 fontWeight;
|
||||||
Bool italic;
|
Bool italic;
|
||||||
Bool display_block;
|
I64 display;
|
||||||
};
|
};
|
||||||
|
|
||||||
class @html_input_buffer
|
class @html_input_buffer
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue