|
@@ -15,7 +15,7 @@
|
|
|
*
|
|
*
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
* all copies or substantial portions of the Software.
|
|
* all copies or substantial portions of the Software.
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
@@ -27,11 +27,14 @@
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
#include "LayoutDetails.h"
|
|
#include "LayoutDetails.h"
|
|
|
-#include "../../Include/RmlUi/Core/ComputedValues.h"
|
|
|
|
|
-#include "../../Include/RmlUi/Core/Element.h"
|
|
|
|
|
-#include "../../Include/RmlUi/Core/ElementScroll.h"
|
|
|
|
|
-#include "../../Include/RmlUi/Core/Math.h"
|
|
|
|
|
-#include "../../Include/RmlUi/Core/Profiling.h"
|
|
|
|
|
|
|
+#include "../../../Include/RmlUi/Core/ComputedValues.h"
|
|
|
|
|
+#include "../../../Include/RmlUi/Core/Element.h"
|
|
|
|
|
+#include "../../../Include/RmlUi/Core/ElementScroll.h"
|
|
|
|
|
+#include "../../../Include/RmlUi/Core/ElementText.h"
|
|
|
|
|
+#include "../../../Include/RmlUi/Core/Math.h"
|
|
|
|
|
+#include "../../../Include/RmlUi/Core/Profiling.h"
|
|
|
|
|
+#include "ContainerBox.h"
|
|
|
|
|
+#include "FormattingContext.h"
|
|
|
#include "LayoutEngine.h"
|
|
#include "LayoutEngine.h"
|
|
|
#include <float.h>
|
|
#include <float.h>
|
|
|
|
|
|
|
@@ -40,14 +43,14 @@ namespace Rml {
|
|
|
// Convert width or height of a border box to the width or height of its corresponding content box.
|
|
// Convert width or height of a border box to the width or height of its corresponding content box.
|
|
|
static inline float BorderSizeToContentSize(float border_size, float border_padding_edges_size)
|
|
static inline float BorderSizeToContentSize(float border_size, float border_padding_edges_size)
|
|
|
{
|
|
{
|
|
|
- if (border_size < 0.0f || border_size == FLT_MAX)
|
|
|
|
|
|
|
+ if (border_size < 0.0f || border_size >= FLT_MAX)
|
|
|
return border_size;
|
|
return border_size;
|
|
|
|
|
|
|
|
return Math::Max(0.0f, border_size - border_padding_edges_size);
|
|
return Math::Max(0.0f, border_size - border_padding_edges_size);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Generates the box for an element.
|
|
// Generates the box for an element.
|
|
|
-void LayoutDetails::BuildBox(Box& box, Vector2f containing_block, Element* element, BoxContext box_context, float override_shrink_to_fit_width)
|
|
|
|
|
|
|
+void LayoutDetails::BuildBox(Box& box, Vector2f containing_block, Element* element, BuildBoxMode box_context)
|
|
|
{
|
|
{
|
|
|
if (!element)
|
|
if (!element)
|
|
|
{
|
|
{
|
|
@@ -82,22 +85,19 @@ void LayoutDetails::BuildBox(Box& box, Vector2f containing_block, Element* eleme
|
|
|
|
|
|
|
|
// Calculate the content area and constraints. 'auto' width and height are handled later.
|
|
// Calculate the content area and constraints. 'auto' width and height are handled later.
|
|
|
// For inline non-replaced elements, width and height are ignored, so we can skip the calculations.
|
|
// For inline non-replaced elements, width and height are ignored, so we can skip the calculations.
|
|
|
- if (box_context == BoxContext::Block || box_context == BoxContext::FlexOrTable || replaced_element)
|
|
|
|
|
|
|
+ if (box_context == BuildBoxMode::Block || box_context == BuildBoxMode::UnalignedBlock || replaced_element)
|
|
|
{
|
|
{
|
|
|
- if (content_area.x < 0 && computed.width().type != Style::Width::Auto)
|
|
|
|
|
- content_area.x = ResolveValue(computed.width(), containing_block.x);
|
|
|
|
|
-
|
|
|
|
|
- if (content_area.y < 0 && computed.height().type != Style::Width::Auto)
|
|
|
|
|
- content_area.y = ResolveValue(computed.height(), containing_block.y);
|
|
|
|
|
-
|
|
|
|
|
- min_size = Vector2f(
|
|
|
|
|
- ResolveValue(computed.min_width(), containing_block.x),
|
|
|
|
|
- ResolveValue(computed.min_height(), containing_block.y)
|
|
|
|
|
- );
|
|
|
|
|
- max_size = Vector2f(
|
|
|
|
|
- ResolveValue(computed.max_width(), containing_block.x),
|
|
|
|
|
- ResolveValue(computed.max_height(), containing_block.y)
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ content_area.x = ResolveValueOr(computed.width(), containing_block.x, -1.f);
|
|
|
|
|
+ content_area.y = ResolveValueOr(computed.height(), containing_block.y, -1.f);
|
|
|
|
|
+
|
|
|
|
|
+ min_size = Vector2f{
|
|
|
|
|
+ ResolveValueOr(computed.min_width(), containing_block.x, 0.f),
|
|
|
|
|
+ ResolveValueOr(computed.min_height(), containing_block.y, 0.f),
|
|
|
|
|
+ };
|
|
|
|
|
+ max_size = Vector2f{
|
|
|
|
|
+ ResolveValueOr(computed.max_width(), containing_block.x, FLT_MAX),
|
|
|
|
|
+ ResolveValueOr(computed.max_height(), containing_block.y, FLT_MAX),
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
// Adjust sizes for the given box sizing model.
|
|
// Adjust sizes for the given box sizing model.
|
|
|
if (computed.box_sizing() == Style::BoxSizing::BorderBox)
|
|
if (computed.box_sizing() == Style::BoxSizing::BorderBox)
|
|
@@ -126,27 +126,13 @@ void LayoutDetails::BuildBox(Box& box, Vector2f containing_block, Element* eleme
|
|
|
box.SetContent(content_area);
|
|
box.SetContent(content_area);
|
|
|
|
|
|
|
|
// Evaluate the margins, and width and height if they are auto.
|
|
// Evaluate the margins, and width and height if they are auto.
|
|
|
- BuildBoxSizeAndMargins(box, min_size, max_size, containing_block, element, box_context, replaced_element, override_shrink_to_fit_width);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// Generates the box for an element placed in a block box.
|
|
|
|
|
-void LayoutDetails::BuildBox(Box& box, float& min_height, float& max_height, LayoutBlockBox* containing_box, Element* element, BoxContext box_context,
|
|
|
|
|
- float override_shrink_to_fit_width)
|
|
|
|
|
-{
|
|
|
|
|
- Vector2f containing_block = LayoutDetails::GetContainingBlock(containing_box);
|
|
|
|
|
-
|
|
|
|
|
- BuildBox(box, containing_block, element, box_context, override_shrink_to_fit_width);
|
|
|
|
|
-
|
|
|
|
|
- if (element)
|
|
|
|
|
- GetDefiniteMinMaxHeight(min_height, max_height, element->GetComputedValues(), box, containing_block.y);
|
|
|
|
|
- else
|
|
|
|
|
- min_height = max_height = box.GetSize().y;
|
|
|
|
|
|
|
+ BuildBoxSizeAndMargins(box, min_size, max_size, containing_block, element, box_context, replaced_element);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void LayoutDetails::GetMinMaxWidth(float& min_width, float& max_width, const ComputedValues& computed, const Box& box, float containing_block_width)
|
|
void LayoutDetails::GetMinMaxWidth(float& min_width, float& max_width, const ComputedValues& computed, const Box& box, float containing_block_width)
|
|
|
{
|
|
{
|
|
|
- min_width = ResolveValue(computed.min_width(), containing_block_width);
|
|
|
|
|
- max_width = ResolveValue(computed.max_width(), containing_block_width);
|
|
|
|
|
|
|
+ min_width = ResolveValueOr(computed.min_width(), containing_block_width, 0.f);
|
|
|
|
|
+ max_width = ResolveValueOr(computed.max_width(), containing_block_width, FLT_MAX);
|
|
|
|
|
|
|
|
if (computed.box_sizing() == Style::BoxSizing::BorderBox)
|
|
if (computed.box_sizing() == Style::BoxSizing::BorderBox)
|
|
|
{
|
|
{
|
|
@@ -156,10 +142,11 @@ void LayoutDetails::GetMinMaxWidth(float& min_width, float& max_width, const Com
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void LayoutDetails::GetMinMaxHeight(float& min_height, float& max_height, const ComputedValues& computed, const Box& box, float containing_block_height)
|
|
|
|
|
|
|
+void LayoutDetails::GetMinMaxHeight(float& min_height, float& max_height, const ComputedValues& computed, const Box& box,
|
|
|
|
|
+ float containing_block_height)
|
|
|
{
|
|
{
|
|
|
- min_height = ResolveValue(computed.min_height(), containing_block_height);
|
|
|
|
|
- max_height = ResolveValue(computed.max_height(), containing_block_height);
|
|
|
|
|
|
|
+ min_height = ResolveValueOr(computed.min_height(), containing_block_height, 0.f);
|
|
|
|
|
+ max_height = ResolveValueOr(computed.max_height(), containing_block_height, FLT_MAX);
|
|
|
|
|
|
|
|
if (computed.box_sizing() == Style::BoxSizing::BorderBox)
|
|
if (computed.box_sizing() == Style::BoxSizing::BorderBox)
|
|
|
{
|
|
{
|
|
@@ -169,7 +156,8 @@ void LayoutDetails::GetMinMaxHeight(float& min_height, float& max_height, const
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void LayoutDetails::GetDefiniteMinMaxHeight(float& min_height, float& max_height, const ComputedValues& computed, const Box& box, float containing_block_height)
|
|
|
|
|
|
|
+void LayoutDetails::GetDefiniteMinMaxHeight(float& min_height, float& max_height, const ComputedValues& computed, const Box& box,
|
|
|
|
|
+ float containing_block_height)
|
|
|
{
|
|
{
|
|
|
const float box_height = box.GetSize().y;
|
|
const float box_height = box.GetSize().y;
|
|
|
if (box_height < 0)
|
|
if (box_height < 0)
|
|
@@ -183,47 +171,63 @@ void LayoutDetails::GetDefiniteMinMaxHeight(float& min_height, float& max_height
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Returns the fully-resolved, fixed-width and -height containing block from a block box.
|
|
|
|
|
-Vector2f LayoutDetails::GetContainingBlock(const LayoutBlockBox* containing_box)
|
|
|
|
|
|
|
+ContainingBlock LayoutDetails::GetContainingBlock(ContainerBox* parent_container, const Style::Position position)
|
|
|
{
|
|
{
|
|
|
- RMLUI_ASSERT(containing_box);
|
|
|
|
|
|
|
+ RMLUI_ASSERT(parent_container);
|
|
|
|
|
+ using Style::Position;
|
|
|
|
|
|
|
|
- Vector2f containing_block;
|
|
|
|
|
|
|
+ ContainerBox* container = parent_container;
|
|
|
|
|
+ Box::Area area = Box::CONTENT;
|
|
|
|
|
|
|
|
- containing_block.x = containing_box->GetBox().GetSize(Box::CONTENT).x;
|
|
|
|
|
- if (containing_box->GetElement() != nullptr)
|
|
|
|
|
- containing_block.x -= containing_box->GetElement()->GetElementScroll()->GetScrollbarSize(ElementScroll::VERTICAL);
|
|
|
|
|
-
|
|
|
|
|
- while ((containing_block.y = containing_box->GetBox().GetSize(Box::CONTENT).y) < 0)
|
|
|
|
|
|
|
+ // For absolutely positioned boxes we look for the first positioned ancestor. We deviate from the CSS specs by using
|
|
|
|
|
+ // the same rules for fixed boxes, as that is particularly helpful on handles and other widgets that should not
|
|
|
|
|
+ // scroll with the window. This is a common design pattern in target applications for this library, although this
|
|
|
|
|
+ // behavior may be reconsidered in the future.
|
|
|
|
|
+ if (position == Position::Absolute || position == Position::Fixed)
|
|
|
{
|
|
{
|
|
|
- containing_box = containing_box->GetParent();
|
|
|
|
|
- if (!containing_box)
|
|
|
|
|
- {
|
|
|
|
|
- RMLUI_ERROR;
|
|
|
|
|
- containing_block.y = 0;
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ area = Box::PADDING;
|
|
|
|
|
+
|
|
|
|
|
+ auto EstablishesAbsoluteContainingBlock = [](ContainerBox* container) -> bool {
|
|
|
|
|
+ return container->GetPositionProperty() != Position::Static || container->HasLocalTransformOrPerspective();
|
|
|
|
|
+ };
|
|
|
|
|
+ while (container && container->GetParent() && !EstablishesAbsoluteContainingBlock(container))
|
|
|
|
|
+ container = container->GetParent();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (containing_box)
|
|
|
|
|
|
|
+ const Box* box = container->GetIfBox();
|
|
|
|
|
+ if (!box)
|
|
|
{
|
|
{
|
|
|
- if (Element* element = containing_box->GetElement())
|
|
|
|
|
- containing_block.y -= element->GetElementScroll()->GetScrollbarSize(ElementScroll::HORIZONTAL);
|
|
|
|
|
|
|
+ RMLUI_ERROR;
|
|
|
|
|
+ return {container, {}};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- containing_block.x = Math::Max(0.0f, containing_block.x);
|
|
|
|
|
- containing_block.y = Math::Max(0.0f, containing_block.y);
|
|
|
|
|
|
|
+ Vector2f containing_block = box->GetSize(area);
|
|
|
|
|
|
|
|
- return containing_block;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ if (position == Position::Static || position == Position::Relative)
|
|
|
|
|
+ {
|
|
|
|
|
+ // For static elements we subtract the scrollbar size so that elements normally don't overlap their parent's
|
|
|
|
|
+ // scrollbars. In CSS, this would also be done for absolutely positioned elements, we might want to copy that
|
|
|
|
|
+ // behavior in the future. If so, we would also need to change the element offset behavior, and ideally also
|
|
|
|
|
+ // make positioned boxes contribute to the scrollable area.
|
|
|
|
|
+ if (Element* element = container->GetElement())
|
|
|
|
|
+ {
|
|
|
|
|
+ ElementScroll* element_scroll = element->GetElementScroll();
|
|
|
|
|
+ if (containing_block.x >= 0.f)
|
|
|
|
|
+ containing_block.x = Math::Max(containing_block.x - element_scroll->GetScrollbarSize(ElementScroll::VERTICAL), 0.f);
|
|
|
|
|
+ if (containing_block.y >= 0.f)
|
|
|
|
|
+ containing_block.y = Math::Max(containing_block.y - element_scroll->GetScrollbarSize(ElementScroll::HORIZONTAL), 0.f);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ return {container, containing_block};
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
void LayoutDetails::BuildBoxSizeAndMargins(Box& box, Vector2f min_size, Vector2f max_size, Vector2f containing_block, Element* element,
|
|
void LayoutDetails::BuildBoxSizeAndMargins(Box& box, Vector2f min_size, Vector2f max_size, Vector2f containing_block, Element* element,
|
|
|
- BoxContext box_context, bool replaced_element, float override_shrink_to_fit_width)
|
|
|
|
|
|
|
+ BuildBoxMode box_context, bool replaced_element)
|
|
|
{
|
|
{
|
|
|
const ComputedValues& computed = element->GetComputedValues();
|
|
const ComputedValues& computed = element->GetComputedValues();
|
|
|
|
|
|
|
|
- if (box_context == BoxContext::Inline || box_context == BoxContext::FlexOrTable)
|
|
|
|
|
|
|
+ if (box_context == BuildBoxMode::Inline || box_context == BuildBoxMode::UnalignedBlock)
|
|
|
{
|
|
{
|
|
|
// For inline elements, their calculations are straightforward. No worrying about auto margins and dimensions, etc.
|
|
// For inline elements, their calculations are straightforward. No worrying about auto margins and dimensions, etc.
|
|
|
// Evaluate the margins. Any declared as 'auto' will resolve to 0.
|
|
// Evaluate the margins. Any declared as 'auto' will resolve to 0.
|
|
@@ -235,7 +239,7 @@ void LayoutDetails::BuildBoxSizeAndMargins(Box& box, Vector2f min_size, Vector2f
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
// The element is block, so we need to run the box through the ringer to potentially evaluate auto margins and dimensions.
|
|
// The element is block, so we need to run the box through the ringer to potentially evaluate auto margins and dimensions.
|
|
|
- BuildBoxWidth(box, computed, min_size.x, max_size.x, containing_block, element, replaced_element, override_shrink_to_fit_width);
|
|
|
|
|
|
|
+ BuildBoxWidth(box, computed, min_size.x, max_size.x, containing_block, element, replaced_element);
|
|
|
BuildBoxHeight(box, computed, min_size.y, max_size.y, containing_block.y);
|
|
BuildBoxHeight(box, computed, min_size.y, max_size.y, containing_block.y);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -244,32 +248,36 @@ float LayoutDetails::GetShrinkToFitWidth(Element* element, Vector2f containing_b
|
|
|
{
|
|
{
|
|
|
RMLUI_ASSERT(element);
|
|
RMLUI_ASSERT(element);
|
|
|
|
|
|
|
|
|
|
+ // @performance Can we lay out the elements directly using a fit-content size mode, instead of fetching the
|
|
|
|
|
+ // shrink-to-fit width first? Use a non-definite placeholder for the box content width, and available width as a
|
|
|
|
|
+ // maximum constraint.
|
|
|
Box box;
|
|
Box box;
|
|
|
float min_height, max_height;
|
|
float min_height, max_height;
|
|
|
- LayoutDetails::BuildBox(box, containing_block, element, BoxContext::Block, containing_block.x);
|
|
|
|
|
|
|
+ LayoutDetails::BuildBox(box, containing_block, element, BuildBoxMode::UnalignedBlock);
|
|
|
LayoutDetails::GetDefiniteMinMaxHeight(min_height, max_height, element->GetComputedValues(), box, containing_block.y);
|
|
LayoutDetails::GetDefiniteMinMaxHeight(min_height, max_height, element->GetComputedValues(), box, containing_block.y);
|
|
|
|
|
|
|
|
- // First we need to format the element, then we get the shrink-to-fit width based on the largest line or box.
|
|
|
|
|
- LayoutBlockBox containing_block_box(nullptr, nullptr, Box(containing_block), 0.0f, FLT_MAX);
|
|
|
|
|
|
|
+ // Currently we don't support shrink-to-fit width for flexboxes or tables. Just return a zero-sized width.
|
|
|
|
|
+ const Style::Display display = element->GetDisplay();
|
|
|
|
|
+ if (display == Style::Display::Flex || display == Style::Display::InlineFlex || display == Style::Display::Table ||
|
|
|
|
|
+ display == Style::Display::InlineTable)
|
|
|
|
|
+ return 0.f;
|
|
|
|
|
|
|
|
- // Here we fix the element's width to its containing block so that any content is wrapped at this width.
|
|
|
|
|
- // We can consider to instead set this to infinity and clamp it to the available width later after formatting,
|
|
|
|
|
- // but right now the formatting procedure doesn't work well with such numbers.
|
|
|
|
|
- LayoutBlockBox* block_context_box = containing_block_box.AddBlockElement(element, box, min_height, max_height);
|
|
|
|
|
|
|
+ // Use a large size for the box content width, so that it is practically unconstrained. This makes the formatting
|
|
|
|
|
+ // procedure act as if under a maximum content constraint. Children with percentage sizing values may be scaled
|
|
|
|
|
+ // based on this width (such as 'width' or 'margin'), if so, the layout is considered undefined like in CSS 2.
|
|
|
|
|
+ const float max_content_constraint_width = containing_block.x + 1000.f;
|
|
|
|
|
+ box.SetContent({max_content_constraint_width, box.GetSize().y});
|
|
|
|
|
|
|
|
- // @performance. Some formatting can be simplified, eg. absolute elements do not contribute to the shrink-to-fit width.
|
|
|
|
|
- // Also, children of elements with a fixed width and height don't need to be formatted further.
|
|
|
|
|
- for (int i = 0; i < element->GetNumChildren(); i++)
|
|
|
|
|
- {
|
|
|
|
|
- if (!LayoutEngine::FormatElement(block_context_box, element->GetChild(i)))
|
|
|
|
|
- i = -1;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // First, format the element under the above generated box. Then we ask the resulting box for its shrink-to-fit
|
|
|
|
|
+ // width. For block containers, this is essentially its largest line or child box.
|
|
|
|
|
+ // @performance. Some formatting can be simplified, e.g. absolute elements do not contribute to the shrink-to-fit
|
|
|
|
|
+ // width. Also, children of elements with a fixed width and height don't need to be formatted further.
|
|
|
|
|
+ RootBox root(Math::Max(containing_block, Vector2f(0.f)));
|
|
|
|
|
+ UniquePtr<LayoutBox> layout_box = FormattingContext::FormatIndependent(&root, element, &box, FormattingContextType::Block);
|
|
|
|
|
|
|
|
- // We only do layouting to get the fit-to-shrink width here, and for this purpose we may get
|
|
|
|
|
- // away with not closing the boxes. This is avoided for performance reasons.
|
|
|
|
|
- //block_context_box->Close();
|
|
|
|
|
|
|
+ const float available_width = Math::Max(0.f, containing_block.x - box.GetSizeAcross(Box::HORIZONTAL, Box::MARGIN, Box::PADDING));
|
|
|
|
|
|
|
|
- return Math::Min(containing_block.x, block_context_box->GetShrinkToFitWidth());
|
|
|
|
|
|
|
+ return Math::Min(available_width, layout_box->GetShrinkToFitWidth());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ComputedAxisSize LayoutDetails::BuildComputedHorizontalSize(const ComputedValues& computed)
|
|
ComputedAxisSize LayoutDetails::BuildComputedHorizontalSize(const ComputedValues& computed)
|
|
@@ -294,7 +302,19 @@ void LayoutDetails::GetEdgeSizes(float& margin_a, float& margin_b, float& paddin
|
|
|
padding_border_b = Math::Max(0.0f, ResolveValue(computed_size.padding_b, base_value)) + Math::Max(0.0f, computed_size.border_b);
|
|
padding_border_b = Math::Max(0.0f, ResolveValue(computed_size.padding_b, base_value)) + Math::Max(0.0f, computed_size.border_b);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-Vector2f LayoutDetails::CalculateSizeForReplacedElement(const Vector2f specified_content_size, const Vector2f min_size, const Vector2f max_size, const Vector2f intrinsic_size, const float intrinsic_ratio)
|
|
|
|
|
|
|
+String LayoutDetails::GetDebugElementName(Element* element)
|
|
|
|
|
+{
|
|
|
|
|
+ if (!element)
|
|
|
|
|
+ return "nullptr";
|
|
|
|
|
+ if (!element->GetId().empty())
|
|
|
|
|
+ return '#' + element->GetId();
|
|
|
|
|
+ if (auto element_text = rmlui_dynamic_cast<ElementText*>(element))
|
|
|
|
|
+ return '\"' + StringUtilities::StripWhitespace(element_text->GetText()).substr(0, 20) + '\"';
|
|
|
|
|
+ return element->GetAddress(false, false);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+Vector2f LayoutDetails::CalculateSizeForReplacedElement(const Vector2f specified_content_size, const Vector2f min_size, const Vector2f max_size,
|
|
|
|
|
+ const Vector2f intrinsic_size, const float intrinsic_ratio)
|
|
|
{
|
|
{
|
|
|
// Start with the element's specified width and height. If any of them are auto, use the element's intrinsic
|
|
// Start with the element's specified width and height. If any of them are auto, use the element's intrinsic
|
|
|
// dimensions and ratio to find a suitable content size.
|
|
// dimensions and ratio to find a suitable content size.
|
|
@@ -382,7 +402,8 @@ Vector2f LayoutDetails::CalculateSizeForReplacedElement(const Vector2f specified
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Builds the block-specific width and horizontal margins of a Box.
|
|
// Builds the block-specific width and horizontal margins of a Box.
|
|
|
-void LayoutDetails::BuildBoxWidth(Box& box, const ComputedValues& computed, float min_width, float max_width, Vector2f containing_block, Element* element, bool replaced_element, float override_shrink_to_fit_width)
|
|
|
|
|
|
|
+void LayoutDetails::BuildBoxWidth(Box& box, const ComputedValues& computed, float min_width, float max_width, Vector2f containing_block,
|
|
|
|
|
+ Element* element, bool replaced_element, float override_shrink_to_fit_width)
|
|
|
{
|
|
{
|
|
|
RMLUI_ZoneScoped;
|
|
RMLUI_ZoneScoped;
|
|
|
|
|
|
|
@@ -408,53 +429,46 @@ void LayoutDetails::BuildBoxWidth(Box& box, const ComputedValues& computed, floa
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ const bool absolutely_positioned = (computed.position() == Style::Position::Absolute || computed.position() == Style::Position::Fixed);
|
|
|
|
|
+ const bool inset_auto = (computed.left().type == Style::Left::Auto || computed.right().type == Style::Right::Auto);
|
|
|
const bool width_auto = (content_area.x < 0);
|
|
const bool width_auto = (content_area.x < 0);
|
|
|
|
|
|
|
|
- // If the width is set to auto, we need to calculate the width
|
|
|
|
|
|
|
+ auto GetInsetWidth = [&] {
|
|
|
|
|
+ // For absolutely positioned elements (and only those), the 'left' and 'right' values are part of the box's width constraint.
|
|
|
|
|
+ if (absolutely_positioned)
|
|
|
|
|
+ return ResolveValue(computed.left(), containing_block.x) + ResolveValue(computed.right(), containing_block.x);
|
|
|
|
|
+ return 0.f;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // If the width is set to auto, we need to calculate the width.
|
|
|
if (width_auto)
|
|
if (width_auto)
|
|
|
{
|
|
{
|
|
|
// Apply the shrink-to-fit algorithm here to find the width of the element.
|
|
// Apply the shrink-to-fit algorithm here to find the width of the element.
|
|
|
// See CSS 2.1 section 10.3.7 for when this should be applied.
|
|
// See CSS 2.1 section 10.3.7 for when this should be applied.
|
|
|
const bool shrink_to_fit = !replaced_element &&
|
|
const bool shrink_to_fit = !replaced_element &&
|
|
|
- ((computed.float_() != Style::Float::None) ||
|
|
|
|
|
- ((computed.position() == Style::Position::Absolute || computed.position() == Style::Position::Fixed) &&
|
|
|
|
|
- (computed.left().type == Style::Left::Auto || computed.right().type == Style::Right::Auto)) ||
|
|
|
|
|
|
|
+ ((computed.float_() != Style::Float::None) || (absolutely_positioned && inset_auto) ||
|
|
|
(computed.display() == Style::Display::InlineBlock));
|
|
(computed.display() == Style::Display::InlineBlock));
|
|
|
|
|
|
|
|
- float left = 0.0f, right = 0.0f;
|
|
|
|
|
- // If we are dealing with an absolutely positioned element we need to
|
|
|
|
|
- // consider if the left and right properties are set, since the width can be affected.
|
|
|
|
|
- if (computed.position() == Style::Position::Absolute || computed.position() == Style::Position::Fixed)
|
|
|
|
|
|
|
+ if (!shrink_to_fit)
|
|
|
{
|
|
{
|
|
|
- if (computed.left().type != Style::Left::Auto)
|
|
|
|
|
- left = ResolveValue(computed.left(), containing_block.x);
|
|
|
|
|
- if (computed.right().type != Style::Right::Auto)
|
|
|
|
|
- right = ResolveValue(computed.right(), containing_block.x);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (shrink_to_fit && override_shrink_to_fit_width < 0)
|
|
|
|
|
- {
|
|
|
|
|
- content_area.x = GetShrinkToFitWidth(element, containing_block);
|
|
|
|
|
- override_shrink_to_fit_width = content_area.x;
|
|
|
|
|
|
|
+ // The width is set to whatever remains of the containing block.
|
|
|
|
|
+ content_area.x = containing_block.x - (GetInsetWidth() + box.GetSizeAcross(Box::HORIZONTAL, Box::MARGIN, Box::PADDING));
|
|
|
|
|
+ content_area.x = Math::Max(0.0f, content_area.x);
|
|
|
}
|
|
}
|
|
|
- else if (shrink_to_fit)
|
|
|
|
|
|
|
+ else if (override_shrink_to_fit_width >= 0)
|
|
|
{
|
|
{
|
|
|
content_area.x = override_shrink_to_fit_width;
|
|
content_area.x = override_shrink_to_fit_width;
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
{
|
|
{
|
|
|
- // We resolve any auto margins to 0 and the width is set to whatever is left of the containing block.
|
|
|
|
|
- content_area.x = containing_block.x - (left +
|
|
|
|
|
- box.GetCumulativeEdge(Box::CONTENT, Box::LEFT) +
|
|
|
|
|
- box.GetCumulativeEdge(Box::CONTENT, Box::RIGHT) +
|
|
|
|
|
- right);
|
|
|
|
|
- content_area.x = Math::Max(0.0f, content_area.x);
|
|
|
|
|
|
|
+ content_area.x = GetShrinkToFitWidth(element, containing_block);
|
|
|
|
|
+ override_shrink_to_fit_width = content_area.x;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
// Otherwise, the margins that are set to auto will pick up the remaining width of the containing block.
|
|
// Otherwise, the margins that are set to auto will pick up the remaining width of the containing block.
|
|
|
else if (num_auto_margins > 0)
|
|
else if (num_auto_margins > 0)
|
|
|
{
|
|
{
|
|
|
- const float margin = (containing_block.x - box.GetSizeAcross(Box::HORIZONTAL, Box::MARGIN)) / float(num_auto_margins);
|
|
|
|
|
|
|
+ const float margin = (containing_block.x - (GetInsetWidth() + box.GetSizeAcross(Box::HORIZONTAL, Box::MARGIN))) / float(num_auto_margins);
|
|
|
|
|
|
|
|
if (margins_auto[0])
|
|
if (margins_auto[0])
|
|
|
box.SetEdge(Box::MARGIN, Box::LEFT, margin);
|
|
box.SetEdge(Box::MARGIN, Box::LEFT, margin);
|
|
@@ -471,7 +485,7 @@ void LayoutDetails::BuildBoxWidth(Box& box, const ComputedValues& computed, floa
|
|
|
box.SetContent(content_area);
|
|
box.SetContent(content_area);
|
|
|
|
|
|
|
|
if (num_auto_margins > 0)
|
|
if (num_auto_margins > 0)
|
|
|
- BuildBoxWidth(box, computed, min_width, max_width, containing_block, element, replaced_element, override_shrink_to_fit_width);
|
|
|
|
|
|
|
+ BuildBoxWidth(box, computed, min_width, max_width, containing_block, element, replaced_element, clamped_width);
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
|
box.SetContent(content_area);
|
|
box.SetContent(content_area);
|
|
@@ -504,40 +518,36 @@ void LayoutDetails::BuildBoxHeight(Box& box, const ComputedValues& computed, flo
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ const bool absolutely_positioned = (computed.position() == Style::Position::Absolute || computed.position() == Style::Position::Fixed);
|
|
|
|
|
+ const bool inset_auto = (computed.top().type == Style::Top::Auto || computed.bottom().type == Style::Bottom::Auto);
|
|
|
const bool height_auto = (content_area.y < 0);
|
|
const bool height_auto = (content_area.y < 0);
|
|
|
|
|
|
|
|
- // If the height is set to auto, we need to calculate the height
|
|
|
|
|
|
|
+ auto GetInsetHeight = [&] {
|
|
|
|
|
+ // For absolutely positioned elements (and only those), the 'top' and 'bottom' values are part of the box's height constraint.
|
|
|
|
|
+ if (absolutely_positioned)
|
|
|
|
|
+ return ResolveValue(computed.top(), containing_block_height) + ResolveValue(computed.bottom(), containing_block_height);
|
|
|
|
|
+ return 0.f;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // If the height is set to auto, we need to calculate the height.
|
|
|
if (height_auto)
|
|
if (height_auto)
|
|
|
{
|
|
{
|
|
|
// If the height is set to auto for a box in normal flow, the height is set to -1.
|
|
// If the height is set to auto for a box in normal flow, the height is set to -1.
|
|
|
content_area.y = -1;
|
|
content_area.y = -1;
|
|
|
|
|
|
|
|
- // But if we are dealing with an absolutely positioned element we need to
|
|
|
|
|
- // consider if the top and bottom properties are set, since the height can be affected.
|
|
|
|
|
- if (computed.position() == Style::Position::Absolute || computed.position() == Style::Position::Fixed)
|
|
|
|
|
|
|
+ // But if we are dealing with an absolutely positioned element we need to consider if the top and bottom
|
|
|
|
|
+ // properties are set, since the height can be affected.
|
|
|
|
|
+ if (absolutely_positioned && !inset_auto)
|
|
|
{
|
|
{
|
|
|
- float top = 0.0f, bottom = 0.0f;
|
|
|
|
|
-
|
|
|
|
|
- if (computed.top().type != Style::Top::Auto && computed.bottom().type != Style::Bottom::Auto)
|
|
|
|
|
- {
|
|
|
|
|
- top = ResolveValue(computed.top(), containing_block_height);
|
|
|
|
|
- bottom = ResolveValue(computed.bottom(), containing_block_height);
|
|
|
|
|
-
|
|
|
|
|
- // The height gets resolved to whatever is left of the containing block
|
|
|
|
|
- content_area.y = containing_block_height - (top +
|
|
|
|
|
- box.GetCumulativeEdge(Box::CONTENT, Box::TOP) +
|
|
|
|
|
- box.GetCumulativeEdge(Box::CONTENT, Box::BOTTOM) +
|
|
|
|
|
- bottom);
|
|
|
|
|
- content_area.y = Math::Max(0.0f, content_area.y);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // The height is set to whatever remains of the containing block.
|
|
|
|
|
+ content_area.y = containing_block_height - (GetInsetHeight() + box.GetSizeAcross(Box::VERTICAL, Box::MARGIN, Box::PADDING));
|
|
|
|
|
+ content_area.y = Math::Max(0.0f, content_area.y);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- // Otherwise, the margins that are set to auto will pick up the remaining width of the containing block.
|
|
|
|
|
|
|
+ // Otherwise, the margins that are set to auto will pick up the remaining height of the containing block.
|
|
|
else if (num_auto_margins > 0)
|
|
else if (num_auto_margins > 0)
|
|
|
{
|
|
{
|
|
|
- float margin = 0;
|
|
|
|
|
- if (content_area.y >= 0)
|
|
|
|
|
- margin = (containing_block_height - box.GetSizeAcross(Box::VERTICAL, Box::MARGIN)) / num_auto_margins;
|
|
|
|
|
|
|
+ const float margin = (containing_block_height - (GetInsetHeight() + box.GetSizeAcross(Box::VERTICAL, Box::MARGIN))) / float(num_auto_margins);
|
|
|
|
|
|
|
|
if (margins_auto[0])
|
|
if (margins_auto[0])
|
|
|
box.SetEdge(Box::MARGIN, Box::TOP, margin);
|
|
box.SetEdge(Box::MARGIN, Box::TOP, margin);
|