summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Drake <tlsa@netsurf-browser.org>2011-04-22 21:57:18 +0000
committerMichael Drake <tlsa@netsurf-browser.org>2011-04-22 21:57:18 +0000
commit8d2bb4bd3a5eacbc355fd7ce98c8865f0bcb44e0 (patch)
treeee2a58bdda328f447c756f19f5b753874a4afe32
parent9fef803d1a1386d75c6e087f8e0cf5cc52874219 (diff)
downloadnetsurf-8d2bb4bd3a5eacbc355fd7ce98c8865f0bcb44e0.tar.gz
netsurf-8d2bb4bd3a5eacbc355fd7ce98c8865f0bcb44e0.tar.bz2
Change how blocks are positioned vertically (esp. margin handling).
svn path=/trunk/netsurf/; revision=12235
-rw-r--r--render/box.h3
-rw-r--r--render/layout.c277
2 files changed, 219 insertions, 61 deletions
diff --git a/render/box.h b/render/box.h
index 4c87d1c52..7a63203e8 100644
--- a/render/box.h
+++ b/render/box.h
@@ -124,7 +124,8 @@ typedef enum {
PRE_STRIP = 1 << 3, /* PRE tag needing leading newline stripped */
CLONE = 1 << 4, /* continuation of previous box from wrapping */
MEASURED = 1 << 5, /* text box width has been measured */
- HAS_HEIGHT = 1 << 6 /* box has height */
+ HAS_HEIGHT = 1 << 6, /* box has height (perhaps due to children) */
+ MAKE_HEIGHT = 1 << 7 /* box causes its own height */
} box_flags;
/* Sides of a box */
diff --git a/render/layout.c b/render/layout.c
index d99fa0056..99bdc8b4f 100644
--- a/render/layout.c
+++ b/render/layout.c
@@ -69,6 +69,8 @@ static bool layout_block_context(struct box *block, int viewport_height,
struct content *content);
static void layout_minmax_block(struct box *block,
const struct font_functions *font_func);
+static struct box* layout_next_margin_block(struct box *box, struct box *block,
+ int viewport_height, int *max_pos_margin, int *max_neg_margin);
static bool layout_block_object(struct box *block);
static void layout_get_object_dimensions(struct box *box,
int *width, int *height,
@@ -208,7 +210,8 @@ bool layout_block_context(struct box *block, int viewport_height,
int max_neg_margin = 0;
int y = 0;
int lm, rm;
- struct box *margin_box;
+ struct box *margin_collapse = NULL;
+ bool in_margin = false;
css_fixed gadget_size;
css_unit gadget_unit; /* Checkbox / radio buttons */
@@ -281,7 +284,7 @@ bool layout_block_context(struct box *block, int viewport_height,
gadget_unit, block->style));
}
- box = margin_box = block->children;
+ box = block->children;
/* set current coordinates to top-left of the block */
cx = 0;
y = cy = block->padding[TOP];
@@ -314,7 +317,6 @@ bool layout_block_context(struct box *block, int viewport_height,
while (box) {
assert(box->type == BOX_BLOCK || box->type == BOX_TABLE ||
box->type == BOX_INLINE_CONTAINER);
- assert(margin_box);
/* Tables are laid out before being positioned, because the
* position depends on the width which is calculated in
@@ -336,6 +338,16 @@ bool layout_block_context(struct box *block, int viewport_height,
goto advance_to_next_box;
}
+ /* If we don't know which box the current margin collapses
+ * through to, find out. Update the pos/neg margin values. */
+ if (margin_collapse == NULL) {
+ margin_collapse = layout_next_margin_block(box, block,
+ viewport_height,
+ &max_pos_margin, &max_neg_margin);
+ /* We have a margin that has not yet been applied. */
+ in_margin = true;
+ }
+
/* Clearance. */
y = 0;
if (box->style && css_computed_clear(box->style) !=
@@ -343,19 +355,6 @@ bool layout_block_context(struct box *block, int viewport_height,
y = layout_clear(block->float_children,
css_computed_clear(box->style));
- /* Get top margin */
- if (box->style) {
- layout_find_dimensions(box->parent->width,
- viewport_height, box, box->style,
- NULL, NULL, NULL, NULL,
- box->margin, NULL, NULL);
- }
-
- if (max_pos_margin < box->margin[TOP])
- max_pos_margin = box->margin[TOP];
- else if (max_neg_margin < -box->margin[TOP])
- max_neg_margin = -box->margin[TOP];
-
/* Blocks establishing a block formatting context get minimum
* left and right margins to avoid any floats. */
lm = rm = 0;
@@ -405,8 +404,9 @@ bool layout_block_context(struct box *block, int viewport_height,
* diminished due to floats. */
int x0, x1, top;
struct box *left, *right;
- top = cy > y ? cy : y;
- top += max_pos_margin - max_neg_margin;
+ top = cy + max_pos_margin -
+ max_neg_margin;
+ top = (top > y) ? top : y;
x0 = cx;
x1 = cx + box->parent->width -
box->parent->padding[LEFT] -
@@ -436,18 +436,34 @@ bool layout_block_context(struct box *block, int viewport_height,
cx += box->x;
/* Position box: vertical. */
- if (box->type != BOX_BLOCK || y ||
- box->border[TOP].width || box->padding[TOP]) {
- margin_box->y += max_pos_margin - max_neg_margin;
- cy += max_pos_margin - max_neg_margin;
- max_pos_margin = max_neg_margin = 0;
- margin_box = 0;
+ if (box->border[TOP].width) {
box->y += box->border[TOP].width;
cy += box->border[TOP].width;
- if (cy < y) {
- box->y += y - cy;
- cy = y;
- }
+ }
+
+ /* Vertical margin */
+ if (((box->type == BOX_BLOCK &&
+ (box->flags & HAS_HEIGHT)) ||
+ box->type == BOX_TABLE ||
+ (box->type == BOX_INLINE_CONTAINER &&
+ box != box->parent->children) ||
+ margin_collapse == box) &&
+ in_margin == true) {
+ /* Margin goes above this box. */
+ cy += max_pos_margin - max_neg_margin;
+ box->y += max_pos_margin - max_neg_margin;
+
+ /* Current margin has been applied. */
+ in_margin = false;
+ max_pos_margin = max_neg_margin = 0;
+ }
+
+ /* Handle clearance */
+ if (box->type != BOX_INLINE_CONTAINER &&
+ (y > 0) && (cy < y)) {
+ /* box clears something*/
+ box->y += y - cy;
+ cy = y;
}
/* Unless the box has an overflow style of visible, the box
@@ -455,15 +471,12 @@ bool layout_block_context(struct box *block, int viewport_height,
if (box->type == BOX_BLOCK && box->style &&
css_computed_overflow(box->style) !=
CSS_OVERFLOW_VISIBLE) {
- cy += max_pos_margin - max_neg_margin;
- box->y += max_pos_margin - max_neg_margin;
layout_block_context(box, viewport_height, content);
- if (box->type == BOX_BLOCK || box->object)
- cy += box->padding[TOP];
+ cy += box->padding[TOP];
- if (box->type == BOX_BLOCK && box->height == AUTO) {
+ if (box->height == AUTO) {
box->height = 0;
layout_block_add_scrollbar(box, BOTTOM);
}
@@ -471,23 +484,13 @@ bool layout_block_context(struct box *block, int viewport_height,
cx -= box->x;
cy += box->height + box->padding[BOTTOM] +
box->border[BOTTOM].width;
- max_pos_margin = max_neg_margin = 0;
- if (max_pos_margin < box->margin[BOTTOM])
- max_pos_margin = box->margin[BOTTOM];
- else if (max_neg_margin < -box->margin[BOTTOM])
- max_neg_margin = -box->margin[BOTTOM];
y = box->y + box->padding[TOP] + box->height +
box->padding[BOTTOM] +
box->border[BOTTOM].width;
+
/* Skip children, because they are done in the new
* block context */
goto advance_to_next_box;
- } else if (box->type == BOX_BLOCK && (box->flags & HAS_HEIGHT)) {
- /* This block doesn't establish a new block formatting
- * context */
- cy += max_pos_margin - max_neg_margin;
- box->y += max_pos_margin - max_neg_margin;
- max_pos_margin = max_neg_margin = 0;
}
#ifdef LAYOUT_DEBUG
@@ -546,14 +549,17 @@ bool layout_block_context(struct box *block, int viewport_height,
/* Advance to next box. */
if (box->type == BOX_BLOCK && !box->object && box->children) {
/* Down into children. */
+
+ if (box == margin_collapse) {
+ /* Current margin collapsed though to this box.
+ * Unset margin_collapse. */
+ margin_collapse = NULL;
+ }
+
y = box->padding[TOP];
box = box->children;
box->y = y;
cy += y;
- if (!margin_box) {
- max_pos_margin = max_neg_margin = 0;
- margin_box = box;
- }
continue;
} else if (box->type == BOX_BLOCK || box->object)
cy += box->padding[TOP];
@@ -565,23 +571,42 @@ bool layout_block_context(struct box *block, int viewport_height,
cy += box->height + box->padding[BOTTOM] +
box->border[BOTTOM].width;
- max_pos_margin = max_neg_margin = 0;
- if (max_pos_margin < box->margin[BOTTOM])
- max_pos_margin = box->margin[BOTTOM];
- else if (max_neg_margin < -box->margin[BOTTOM])
- max_neg_margin = -box->margin[BOTTOM];
cx -= box->x;
y = box->y + box->padding[TOP] + box->height +
box->padding[BOTTOM] +
box->border[BOTTOM].width;
+
advance_to_next_box:
if (!box->next) {
/* No more siblings:
* up to first ancestor with a sibling. */
+
do {
+ if (box == margin_collapse) {
+ /* Current margin collapsed though to
+ * this box. Unset margin_collapse. */
+ margin_collapse = NULL;
+ }
+
+ /* Apply bottom margin */
+ if (max_pos_margin < box->margin[BOTTOM])
+ max_pos_margin = box->margin[BOTTOM];
+ else if (max_neg_margin < -box->margin[BOTTOM])
+ max_neg_margin = -box->margin[BOTTOM];
+
box = box->parent;
if (box == block)
break;
+
+ /* Margin is invalidated if this is a box
+ * margins can't collapse through. */
+ if (box->type == BOX_BLOCK &&
+ box->flags & MAKE_HEIGHT) {
+ margin_collapse = NULL;
+ in_margin = false;
+ max_pos_margin = max_neg_margin = 0;
+ }
+
if (box->height == AUTO) {
box->height = y - box->padding[TOP];
@@ -607,22 +632,31 @@ bool layout_block_context(struct box *block, int viewport_height,
cy += box->padding[BOTTOM] +
box->border[BOTTOM].width;
- if (max_pos_margin < box->margin[BOTTOM])
- max_pos_margin = box->margin[BOTTOM];
- else if (max_neg_margin < -box->margin[BOTTOM])
- max_neg_margin = -box->margin[BOTTOM];
cx -= box->x;
y = box->y + box->padding[TOP] + box->height +
box->padding[BOTTOM] +
box->border[BOTTOM].width;
- } while (box != block && !box->next);
+
+ } while (box->next == NULL);
if (box == block)
break;
}
+
/* To next sibling. */
+
+ if (box == margin_collapse) {
+ /* Current margin collapsed though to this box.
+ * Unset margin_collapse. */
+ margin_collapse = NULL;
+ }
+
+ if (max_pos_margin < box->margin[BOTTOM])
+ max_pos_margin = box->margin[BOTTOM];
+ else if (max_neg_margin < -box->margin[BOTTOM])
+ max_neg_margin = -box->margin[BOTTOM];
+
box = box->next;
box->y = y;
- margin_box = box;
}
/* Account for bottom margin of last contained block */
@@ -737,11 +771,17 @@ void layout_minmax_block(struct box *block,
case BOX_INLINE_CONTAINER:
layout_minmax_inline_container(child,
&child_has_height, font_func);
+ if (child_has_height &&
+ child ==
+ child->parent->children) {
+ block->flags |= MAKE_HEIGHT;
+ }
break;
case BOX_TABLE:
layout_minmax_table(child, font_func);
/* todo: fix for zero height tables */
child_has_height = true;
+ child->flags |= MAKE_HEIGHT;
break;
default:
assert(0);
@@ -779,8 +819,10 @@ void layout_minmax_block(struct box *block,
min = max = FIXTOINT(nscss_len2px(width, wunit, block->style));
}
- if (height > 0 && hunit != CSS_UNIT_PCT)
+ if (height > 0 && hunit != CSS_UNIT_PCT) {
+ block->flags |= MAKE_HEIGHT;
block->flags |= HAS_HEIGHT;
+ }
/* add margins, border, padding to min, max widths */
/* Note: we don't know available width here so percentage margin
@@ -824,6 +866,121 @@ void layout_minmax_block(struct box *block,
/**
+ * Find next block that current margin collapses to.
+ *
+ * \param box box to start tree-order search from (top margin is included)
+ * \param block box responsible for current block fromatting context
+ * \param viewport_height height of viewport in px
+ * \param max_pos_margin updated to to maximum positive margin encountered
+ * \param max_neg_margin updated to to maximum negative margin encountered
+ * \return next box that current margin collapses to, or NULL if none.
+ */
+
+struct box* layout_next_margin_block(struct box *box, struct box *block,
+ int viewport_height, int *max_pos_margin, int *max_neg_margin)
+{
+ assert(block != NULL);
+
+ while (box != NULL) {
+
+ if (box->type == BOX_INLINE_CONTAINER || (box->style &&
+ (css_computed_position(box->style) !=
+ CSS_POSITION_ABSOLUTE &&
+ css_computed_position(box->style) !=
+ CSS_POSITION_FIXED))) {
+ /* Not positioned */
+
+ /* Get margins */
+ if (box->style) {
+ layout_find_dimensions(box->parent->width,
+ viewport_height, box,
+ box->style,
+ NULL, NULL, NULL, NULL,
+ box->margin, box->padding,
+ box->border);
+
+ /* Apply top margin */
+ if (*max_pos_margin < box->margin[TOP])
+ *max_pos_margin = box->margin[TOP];
+ else if (*max_neg_margin < -box->margin[TOP])
+ *max_neg_margin = -box->margin[TOP];
+ }
+
+ /* Check whether box is the box current margin collapses
+ * to */
+ if (box->flags & MAKE_HEIGHT ||
+ box->border[TOP].width ||
+ box->padding[TOP] ||
+ (box->style &&
+ css_computed_overflow(box->style) !=
+ CSS_OVERFLOW_VISIBLE) ||
+ (box->type == BOX_INLINE_CONTAINER &&
+ box != box->parent->children)) {
+ /* Collapse to this box; return it */
+ return box;
+ }
+ }
+
+
+ /* Find next box */
+ if (box->type == BOX_BLOCK && !box->object && box->children &&
+ box->style &&
+ css_computed_overflow(box->style) ==
+ CSS_OVERFLOW_VISIBLE) {
+ /* Down into children. */
+ box = box->children;
+ } else {
+ if (!box->next) {
+ /* No more siblings:
+ * Go up to first ancestor with a sibling. */
+ do {
+ /* Apply bottom margin */
+ if (*max_pos_margin <
+ box->margin[BOTTOM])
+ *max_pos_margin =
+ box->margin[BOTTOM];
+ else if (*max_neg_margin <
+ -box->margin[BOTTOM])
+ *max_neg_margin =
+ -box->margin[BOTTOM];
+
+ box = box->parent;
+ } while (box != block && !box->next);
+
+ if (box == block) {
+ /* Margins don't collapse with stuff
+ * outside the block formatting context
+ */
+ return block;
+ }
+ }
+
+ /* Apply bottom margin */
+ if (*max_pos_margin < box->margin[BOTTOM])
+ *max_pos_margin = box->margin[BOTTOM];
+ else if (*max_neg_margin < -box->margin[BOTTOM])
+ *max_neg_margin = -box->margin[BOTTOM];
+
+ /* To next sibling. */
+ box = box->next;
+
+ /* Get margins */
+ if (box->style) {
+ layout_find_dimensions(box->parent->width,
+ viewport_height, box,
+ box->style,
+ NULL, NULL, NULL, NULL,
+ box->margin, box->padding,
+ box->border);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
* Layout a block which contains an object.
*
* \param block box of type BLOCK, INLINE_BLOCK, TABLE, or TABLE_CELL