// Positions a floating element within this block box. void LayoutBlockBox::PositionFloat(Element* element, float offset) { Vector2f box_position; PositionBox(box_position); space->PositionBox(box_position.y + offset, element); }
// Generates and sets the position for a floating box of a given size within our block box. float LayoutBlockBoxSpace::PositionBox(float cursor, Element* element) { Vector2f element_size = element->GetBox().GetSize(Box::MARGIN); int float_property = element->GetProperty< int >(FLOAT); // Shift the cursor down (if necessary) so it isn't placed any higher than a previously-floated box. for (int i = 0; i < NUM_ANCHOR_EDGES; ++i) { if (!boxes[i].empty()) cursor = Math::Max(cursor, boxes[i].back().offset.y); } // Shift the cursor down past to clear boxes, if necessary. cursor = ClearBoxes(cursor, element->GetProperty< int >(CLEAR)); // Find a place to put this box. Vector2f element_offset; PositionBox(element_offset, cursor, element_size, float_property); // It's been placed, so we can now add it to our list of floating boxes. boxes[float_property == FLOAT_LEFT ? LEFT : RIGHT].push_back(SpaceBox(element_offset, element_size)); // Set our offset and dimensions (if necessary) so they enclose the new box. Vector2f normalised_offset = element_offset - (parent->GetPosition() + parent->GetBox().GetPosition()); offset.x = Math::Min(offset.x, normalised_offset.x); offset.y = Math::Min(offset.y, normalised_offset.y); dimensions.x = Math::Max(dimensions.x, normalised_offset.x + element_size.x); dimensions.y = Math::Max(dimensions.y, normalised_offset.y + element_size.y); // Shift the offset into the correct space relative to the element's offset parent. element_offset += Vector2f(element->GetBox().GetEdge(Box::MARGIN, Box::LEFT), element->GetBox().GetEdge(Box::MARGIN, Box::TOP)); element->SetOffset(element_offset - parent->GetOffsetParent()->GetPosition(), parent->GetOffsetParent()->GetElement()); return element_offset.y + element_size.y; }
// Adds an element to this block box to be handled as an absolutely-positioned element. void LayoutBlockBox::AddAbsoluteElement(Element* element) { ROCKET_ASSERT(context == BLOCK); AbsoluteElement absolute_element; absolute_element.element = element; PositionBox(absolute_element.position, 0); // If we have an open inline-context block box as our last child, then the absolute element must appear after it, // but not actually close the box. if (!block_boxes.empty() && block_boxes.back()->context == INLINE) { LayoutBlockBox* inline_context_box = block_boxes.back(); float last_line_height = inline_context_box->line_boxes.back()->GetDimensions().y; absolute_element.position.y += (inline_context_box->box_cursor + Math::Max(0.0f, last_line_height)); } // Find the positioned parent for this element. LayoutBlockBox* absolute_parent = this; while (absolute_parent != absolute_parent->offset_parent) absolute_parent = absolute_parent->parent; absolute_parent->absolute_elements.push_back(absolute_element); }
// Returns the offset from the top-left corner of this box for the next line. void LayoutBlockBox::PositionLineBox(Vector2f& box_position, float& box_width, bool& _wrap_content, const Vector2f& dimensions) const { Vector2f cursor; PositionBox(cursor); space->PositionBox(box_position, box_width, cursor.y, dimensions); // Also, probably shouldn't check for widths when positioning the box? _wrap_content = wrap_content; }
// Generates the position for a box of a given size within a containing block box. void LayoutBlockBoxSpace::PositionBox(Vector2f& box_position, float& box_width, float cursor, const Vector2f& dimensions) const { box_width = PositionBox(box_position, cursor, dimensions); }
// Generates the position for an arbitrary box within our space layout, floated against either the left or right edge. float LayoutBlockBoxSpace::PositionBox(Vector2f& box_position, float cursor, const Vector2f& dimensions, int float_property) const { float parent_scrollbar_width = parent->GetElement()->GetElementScroll()->GetScrollbarSize(ElementScroll::VERTICAL); float parent_origin = parent->GetPosition().x + parent->GetBox().GetPosition(Box::CONTENT).x; float parent_edge = parent->GetBox().GetSize().x + parent_origin - parent_scrollbar_width; AnchorEdge box_edge = float_property == FLOAT_RIGHT ? RIGHT : LEFT; box_position.y = cursor; box_position.x = box_edge == LEFT ? 0 : (parent->GetBox().GetSize().x - dimensions.x) - parent_scrollbar_width; box_position.x += parent_origin; float next_cursor = FLT_MAX; // First up; we iterate through all boxes that share our edge, pushing ourself to the side of them if we intersect // them. We record the height of the lowest box that gets in our way; in the event we can't be positioned at this // height, we'll reposition ourselves at that height for the next iteration. for (size_t i = 0; i < boxes[box_edge].size(); ++i) { const SpaceBox& fixed_box = boxes[box_edge][i]; // If the fixed box's bottom edge is above our top edge, then we can safely skip it. if (fixed_box.offset.y + fixed_box.dimensions.y <= box_position.y) continue; // If the fixed box's top edge is below our bottom edge, then we can safely skip it. if (fixed_box.offset.y >= box_position.y + dimensions.y) continue; // We're intersecting this box vertically, so the box is pushed to the side if necessary. bool collision = false; if (box_edge == LEFT) { float right_edge = fixed_box.offset.x + fixed_box.dimensions.x; collision = box_position.x < right_edge; if (collision) box_position.x = right_edge; } else { collision = box_position.x + dimensions.x > fixed_box.offset.x; if (collision) box_position.x = fixed_box.offset.x - dimensions.x; } // If there was a collision, then we *might* want to remember the height of this box if it is the earliest- // terminating box we've collided with so far. if (collision) { next_cursor = Math::Min(next_cursor, fixed_box.offset.y + fixed_box.dimensions.y); // Were we pushed out of our containing box? If so, try again at the next cursor position. float normalised_position = box_position.x - parent_origin; if (normalised_position < 0 || normalised_position + dimensions.x > parent->GetBox().GetSize().x) return PositionBox(box_position, next_cursor + 0.00001f, dimensions, float_property); } } // Second; we go through all of the boxes on the other edge, checking for horizontal collisions and determining the // maximum width the box can stretch to, if it is placed at this location. float maximum_box_width = box_edge == LEFT ? parent_edge - box_position.x : box_position.x + dimensions.x; for (size_t i = 0; i < boxes[1 - box_edge].size(); ++i) { const SpaceBox& fixed_box = boxes[1 - box_edge][i]; // If the fixed box's bottom edge is above our top edge, then we can safely skip it. if (fixed_box.offset.y + fixed_box.dimensions.y <= box_position.y) continue; // If the fixed box's top edge is below our bottom edge, then we can safely skip it. if (fixed_box.offset.y >= box_position.y + dimensions.y) continue; // We intersect this box vertically, so check if it intersects horizontally. bool collision = false; if (box_edge == LEFT) { maximum_box_width = Math::Min(maximum_box_width, fixed_box.offset.x - box_position.x); collision = box_position.x + dimensions.x > fixed_box.offset.x; } else { maximum_box_width = Math::Min(maximum_box_width, (box_position.x + dimensions.x) - (fixed_box.offset.x + fixed_box.dimensions.x)); collision = box_position.x < fixed_box.offset.x + fixed_box.dimensions.x; } // If we collided with this box ... d'oh! We'll try again lower down the page, at the highest bottom-edge of // any of the boxes we've been pushed around by so far. if (collision) { next_cursor = Math::Min(next_cursor, fixed_box.offset.y + fixed_box.dimensions.y); return PositionBox(box_position, next_cursor + 0.00001f, dimensions, float_property); } } // Third; we go through all of the boxes (on both sides), checking for vertical collisions. for (int i = 0; i < 2; ++i) { for (size_t j = 0; j < boxes[i].size(); ++j) { const SpaceBox& fixed_box = boxes[i][j]; // If the fixed box's bottom edge is above our top edge, then we can safely skip it. if (fixed_box.offset.y + fixed_box.dimensions.y <= box_position.y) continue; // If the fixed box's top edge is below our bottom edge, then we can safely skip it. if (fixed_box.offset.y >= box_position.y + dimensions.y) continue; // We collide vertically; if we also collide horizontally, then we have to try again further down the // layout. If the fixed box's left edge is to right of our right edge, then we can safely skip it. if (fixed_box.offset.x >= box_position.x + dimensions.x) continue; // If the fixed box's right edge is to the left of our left edge, then we can safely skip it. if (fixed_box.offset.x + fixed_box.dimensions.x <= box_position.x) continue; // D'oh! We hit this box. Ah well; we'll try again lower down the page, at the highest bottom-edge of any // of the boxes we've been pushed around by so far. next_cursor = Math::Min(next_cursor, fixed_box.offset.y + fixed_box.dimensions.y); return PositionBox(box_position, next_cursor + 0.00001f, dimensions, float_property); } } // Looks like we've found a winner! return maximum_box_width; }
// Returns the offset from the top-left corner of this box's offset element the next child block box, of the given // dimensions, will be positioned at. This will include the margins on the new block box. void LayoutBlockBox::PositionBlockBox(Vector2f& box_position, const Box& box, int clear_property) const { PositionBox(box_position, box.GetEdge(Box::MARGIN, Box::TOP), clear_property); box_position.x += box.GetEdge(Box::MARGIN, Box::LEFT); box_position.y += box.GetEdge(Box::MARGIN, Box::TOP); }