|
|
@@ -93,9 +93,9 @@ static float GetScrollOffsetDelta(ScrollAlignment alignment, float begin_offset,
|
|
|
|
|
|
Element::Element(const String& tag) :
|
|
|
local_stacking_context(false), local_stacking_context_forced(false), stacking_context_dirty(false), computed_values_are_default_initialized(true),
|
|
|
- visible(true), offset_fixed(false), absolute_offset_dirty(true), dirty_definition(false), dirty_child_definitions(false), dirty_animation(false),
|
|
|
- dirty_transition(false), dirty_transform(false), dirty_perspective(false), tag(tag), relative_offset_base(0, 0), relative_offset_position(0, 0),
|
|
|
- absolute_offset(0, 0), scroll_offset(0, 0)
|
|
|
+ visible(true), offset_fixed(false), absolute_offset_dirty(true), rounded_main_padding_size_dirty(true), dirty_definition(false),
|
|
|
+ dirty_child_definitions(false), dirty_animation(false), dirty_transition(false), dirty_transform(false), dirty_perspective(false), tag(tag),
|
|
|
+ relative_offset_base(0, 0), relative_offset_position(0, 0), absolute_offset(0, 0), scroll_offset(0, 0)
|
|
|
{
|
|
|
RMLUI_ASSERT(tag == StringUtilities::ToLower(tag));
|
|
|
parent = nullptr;
|
|
|
@@ -206,10 +206,7 @@ void Element::Render()
|
|
|
RMLUI_ZoneText(name.c_str(), name.size());
|
|
|
#endif
|
|
|
|
|
|
- // TODO: This is a work-around for the dirty offset not being properly updated when used by containing block children. This results
|
|
|
- // in scrolling not working properly. We don't care about the return value, the call is only used to force the absolute offset to update.
|
|
|
- if (absolute_offset_dirty)
|
|
|
- GetAbsoluteOffset(BoxArea::Border);
|
|
|
+ UpdateAbsoluteOffsetAndRenderBoxData();
|
|
|
|
|
|
// Rebuild our stacking context if necessary.
|
|
|
if (stacking_context_dirty)
|
|
|
@@ -382,28 +379,51 @@ Vector2f Element::GetRelativeOffset(BoxArea area)
|
|
|
|
|
|
Vector2f Element::GetAbsoluteOffset(BoxArea area)
|
|
|
{
|
|
|
- if (absolute_offset_dirty)
|
|
|
+ UpdateAbsoluteOffsetAndRenderBoxData();
|
|
|
+ return area == BoxArea::Border ? absolute_offset : absolute_offset + GetBox().GetPosition(area);
|
|
|
+}
|
|
|
+
|
|
|
+void Element::UpdateAbsoluteOffsetAndRenderBoxData()
|
|
|
+{
|
|
|
+ if (absolute_offset_dirty || rounded_main_padding_size_dirty)
|
|
|
{
|
|
|
absolute_offset_dirty = false;
|
|
|
+ rounded_main_padding_size_dirty = false;
|
|
|
|
|
|
+ Vector2f offset_from_ancestors;
|
|
|
if (offset_parent)
|
|
|
- absolute_offset = offset_parent->GetAbsoluteOffset(BoxArea::Border) + relative_offset_base + relative_offset_position;
|
|
|
- else
|
|
|
- absolute_offset = relative_offset_base + relative_offset_position;
|
|
|
+ offset_from_ancestors = offset_parent->GetAbsoluteOffset(BoxArea::Border);
|
|
|
|
|
|
if (!offset_fixed)
|
|
|
{
|
|
|
// Add any parent scrolling onto our position as well.
|
|
|
if (offset_parent)
|
|
|
- absolute_offset -= offset_parent->scroll_offset;
|
|
|
+ offset_from_ancestors -= offset_parent->scroll_offset;
|
|
|
|
|
|
// Finally, there may be relatively positioned elements between ourself and our containing block, add their relative offsets as well.
|
|
|
for (Element* ancestor = parent; ancestor && ancestor != offset_parent; ancestor = ancestor->parent)
|
|
|
- absolute_offset += ancestor->relative_offset_position;
|
|
|
+ offset_from_ancestors += ancestor->relative_offset_position;
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- return absolute_offset + GetBox().GetPosition(area);
|
|
|
+ const Vector2f relative_offset = relative_offset_base + relative_offset_position;
|
|
|
+ absolute_offset = relative_offset + offset_from_ancestors;
|
|
|
+
|
|
|
+ // Next, we find the rounded size of the box so that elements can be placed border-to-border next to each other
|
|
|
+ // without any gaps. To achieve this, we have to adjust their rounded/rendered sizes based on their position, in
|
|
|
+ // such a way that the bottom-right of this element exactly matches the top-left of the next element. The order
|
|
|
+ // of floating-point operations matter here, we want to replicate the operations in the layout engine as close
|
|
|
+ // as possible to avoid any gaps.
|
|
|
+ const Vector2f main_padding_size = main_box.GetSize(BoxArea::Padding);
|
|
|
+ const Vector2f bottom_right_absolute_offset = (relative_offset + main_padding_size) + offset_from_ancestors;
|
|
|
+ const Vector2f new_rounded_main_padding_size = bottom_right_absolute_offset.Round() - absolute_offset.Round();
|
|
|
+ if (new_rounded_main_padding_size != rounded_main_padding_size)
|
|
|
+ {
|
|
|
+ rounded_main_padding_size = new_rounded_main_padding_size;
|
|
|
+ meta->background_border.DirtyBackground();
|
|
|
+ meta->background_border.DirtyBorder();
|
|
|
+ meta->effects.DirtyEffectsData();
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
void Element::SetClipArea(BoxArea _clip_area)
|
|
|
@@ -430,11 +450,20 @@ void Element::SetBox(const Box& box)
|
|
|
{
|
|
|
if (box != main_box || additional_boxes.size() > 0)
|
|
|
{
|
|
|
+#ifdef RMLUI_DEBUG
|
|
|
+ for (const BoxEdge edge : {BoxEdge::Top, BoxEdge::Right, BoxEdge::Bottom, BoxEdge::Left})
|
|
|
+ {
|
|
|
+ const float border_width = box.GetEdge(BoxArea::Border, edge);
|
|
|
+ if (border_width != Math::Round(border_width))
|
|
|
+ Log::Message(Log::LT_WARNING, "Expected integer border width but got %g px on element: %s", border_width, GetAddress().c_str());
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
main_box = box;
|
|
|
additional_boxes.clear();
|
|
|
|
|
|
OnResize();
|
|
|
-
|
|
|
+ rounded_main_padding_size_dirty = true;
|
|
|
meta->background_border.DirtyBackground();
|
|
|
meta->background_border.DirtyBorder();
|
|
|
meta->effects.DirtyEffectsData();
|
|
|
@@ -444,9 +473,7 @@ void Element::SetBox(const Box& box)
|
|
|
void Element::AddBox(const Box& box, Vector2f offset)
|
|
|
{
|
|
|
additional_boxes.emplace_back(PositionedBox{box, offset});
|
|
|
-
|
|
|
OnResize();
|
|
|
-
|
|
|
meta->background_border.DirtyBackground();
|
|
|
meta->background_border.DirtyBorder();
|
|
|
meta->effects.DirtyEffectsData();
|
|
|
@@ -461,18 +488,57 @@ const Box& Element::GetBox(int index, Vector2f& offset)
|
|
|
{
|
|
|
offset = Vector2f(0);
|
|
|
|
|
|
- if (index < 1)
|
|
|
- return main_box;
|
|
|
-
|
|
|
const int additional_box_index = index - 1;
|
|
|
- if (additional_box_index >= (int)additional_boxes.size())
|
|
|
+ if (index < 1 || additional_box_index >= (int)additional_boxes.size())
|
|
|
return main_box;
|
|
|
|
|
|
offset = additional_boxes[additional_box_index].offset;
|
|
|
-
|
|
|
return additional_boxes[additional_box_index].box;
|
|
|
}
|
|
|
|
|
|
+RenderBox Element::GetRenderBox(BoxArea fill_area, int index)
|
|
|
+{
|
|
|
+ RMLUI_ASSERTMSG(fill_area >= BoxArea::Border && fill_area <= BoxArea::Content,
|
|
|
+ "Render box can only be generated with fill area of border, padding or content.");
|
|
|
+
|
|
|
+ UpdateAbsoluteOffsetAndRenderBoxData();
|
|
|
+
|
|
|
+ struct BoxReference {
|
|
|
+ const Box& box;
|
|
|
+ Vector2f padding_size;
|
|
|
+ Vector2f offset;
|
|
|
+ };
|
|
|
+ auto GetBoxAndOffset = [this, index]() {
|
|
|
+ const int additional_box_index = index - 1;
|
|
|
+ if (index < 1 || additional_box_index >= (int)additional_boxes.size())
|
|
|
+ return BoxReference{main_box, rounded_main_padding_size, {}};
|
|
|
+ const PositionedBox& positioned_box = additional_boxes[additional_box_index];
|
|
|
+ return BoxReference{positioned_box.box, positioned_box.box.GetSize(BoxArea::Padding), positioned_box.offset.Round()};
|
|
|
+ };
|
|
|
+
|
|
|
+ BoxReference box = GetBoxAndOffset();
|
|
|
+
|
|
|
+ EdgeSizes edge_sizes = {};
|
|
|
+ for (int area = (int)BoxArea::Border; area < (int)fill_area; area++)
|
|
|
+ {
|
|
|
+ edge_sizes[0] += box.box.GetEdge(BoxArea(area), BoxEdge::Top);
|
|
|
+ edge_sizes[1] += box.box.GetEdge(BoxArea(area), BoxEdge::Right);
|
|
|
+ edge_sizes[2] += box.box.GetEdge(BoxArea(area), BoxEdge::Bottom);
|
|
|
+ edge_sizes[3] += box.box.GetEdge(BoxArea(area), BoxEdge::Left);
|
|
|
+ }
|
|
|
+ Vector2f inner_size;
|
|
|
+ switch (fill_area)
|
|
|
+ {
|
|
|
+ case BoxArea::Border: inner_size = box.padding_size + box.box.GetFrameSize(BoxArea::Border); break;
|
|
|
+ case BoxArea::Padding: inner_size = box.padding_size; break;
|
|
|
+ case BoxArea::Content: inner_size = box.padding_size - box.box.GetFrameSize(BoxArea::Padding); break;
|
|
|
+ case BoxArea::Margin:
|
|
|
+ case BoxArea::Auto: RMLUI_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ return RenderBox{inner_size, box.offset, edge_sizes, meta->computed_values.border_radius()};
|
|
|
+}
|
|
|
+
|
|
|
int Element::GetNumBoxes()
|
|
|
{
|
|
|
return 1 + (int)additional_boxes.size();
|