Skip to content

Commit 2bc5661

Browse files
authored
fix(layout): use array instead of a merged bounding box to fix issue (#334)
1 parent 3e870bb commit 2bc5661

6 files changed

Lines changed: 78 additions & 40 deletions

File tree

fixtures/html/simple.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
}
6161

6262
main:hover {
63-
background-color: rgba(224, 172, 150, 0.352);
63+
background-color: rgba(150, 156, 224, 0.352);
6464
}
6565

6666
article {

src/client/layout/layout_box.cpp

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ namespace client_layout
1212

1313
LayoutBox::LayoutBox(shared_ptr<dom::Node> node)
1414
: LayoutBoxModelObject(node)
15-
, hit_testable_bounding_box_{glm::vec3(0.0f), glm::vec3(0.0f), glm::mat4(1.0f)}
1615
{
1716
}
1817

@@ -287,21 +286,30 @@ namespace client_layout
287286

288287
bool LayoutBox::mayIntersect(const HitTestResult &r, const HitTestRay &ray, const glm::vec3 &accumulatedOffset) const
289288
{
290-
optional<geometry::BoundingBox> overflowBox = nullopt;
289+
vector<geometry::BoundingBox> overflowBoxes;
291290
if (hasHitTestableOverflow())
292291
{
293292
// TODO(yorkie): handle the hit test for the box with overflow.
294293
}
295294
else
296-
overflowBox = getHitTestableBoundingBox();
295+
overflowBoxes = getHitTestableBoundingBoxes();
297296

298-
if (overflowBox.has_value())
297+
if (overflowBoxes.size() > 0)
299298
{
300-
overflowBox->move(accumulatedOffset);
301-
302-
auto min = overflowBox->minimumWorld;
303-
auto max = overflowBox->maximumWorld;
304-
return ray.intersectsBoxMinMax(min, max);
299+
bool intersects = false;
300+
for (auto box : overflowBoxes)
301+
{
302+
box.move(accumulatedOffset);
303+
304+
auto min = box.minimumWorld;
305+
auto max = box.maximumWorld;
306+
if (ray.intersectsBoxMinMax(min, max))
307+
{
308+
intersects = true;
309+
break;
310+
}
311+
}
312+
return intersects;
305313
}
306314
else
307315
{
@@ -329,6 +337,14 @@ namespace client_layout
329337
return false;
330338
}
331339

340+
void LayoutBox::didComputeLayout(const ConstraintSpace &availableSpace)
341+
{
342+
LayoutBoxModelObject::didComputeLayout(availableSpace);
343+
344+
// Update the hit-testable bounding box.
345+
updateHitTestableBoundingBoxes();
346+
}
347+
332348
void LayoutBox::didComputeLayoutOnce(const ConstraintSpace &availableSpace)
333349
{
334350
LayoutBoxModelObject::didComputeLayoutOnce(availableSpace);
@@ -340,9 +356,6 @@ namespace client_layout
340356
getScrollableArea()
341357
->updateAfterLayout(formattingContext().liveFragment());
342358
}
343-
344-
// Update the hit-testable bounding box.
345-
updateHitTestableBoundingBox();
346359
}
347360

348361
void LayoutBox::updateFromStyle()
@@ -398,31 +411,43 @@ namespace client_layout
398411
return childBoxes;
399412
}
400413

401-
void LayoutBox::updateHitTestableBoundingBox()
414+
void LayoutBox::updateHitTestableBoundingBoxes()
402415
{
403-
auto selfBoundingBox = physicalBorderBoxRect();
404-
glm::vec3 unionMin = selfBoundingBox.minimumWorld;
405-
glm::vec3 unionMax = selfBoundingBox.maximumWorld;
416+
hit_testable_bounding_boxes_.clear();
417+
418+
geometry::BoundingBox mainBoundingBox = physicalBorderBoxRect();
419+
hit_testable_bounding_boxes_.push_back(mainBoundingBox);
420+
421+
glm::vec3 mainMin = mainBoundingBox.minimumWorld;
422+
glm::vec3 mainMax = mainBoundingBox.maximumWorld;
406423

407424
for (const auto &childBox : getChildBoxes())
408425
{
409426
if (!childBox->visible())
410427
continue;
411428

412-
const geometry::BoundingBox &childBoundingBox = childBox->getHitTestableBoundingBox();
413-
unionMin.x = min(unionMin.x, childBoundingBox.minimumWorld.x);
414-
unionMin.y = min(unionMin.y, childBoundingBox.minimumWorld.y);
415-
unionMin.z = min(unionMin.z, childBoundingBox.minimumWorld.z);
416-
unionMax.x = max(unionMax.x, childBoundingBox.maximumWorld.x);
417-
unionMax.y = max(unionMax.y, childBoundingBox.maximumWorld.y);
418-
unionMax.z = max(unionMax.z, childBoundingBox.maximumWorld.z);
429+
for (const auto &childBoundingBox : childBox->getHitTestableBoundingBoxes())
430+
{
431+
// Skip extending if the child box has no size.
432+
if (glm::all(glm::epsilonEqual(childBoundingBox.extendSizeWorld,
433+
glm::vec3(0.0f),
434+
glm::epsilon<float>())))
435+
{
436+
continue;
437+
}
438+
439+
// Check if the child box is fully contained in the main bounding box.
440+
glm::vec3 childMin = childBoundingBox.minimumWorld;
441+
glm::vec3 childMax = childBoundingBox.maximumWorld;
442+
if (childMin.x >= mainMin.x && childMin.y >= mainMin.y && childMin.z >= mainMin.z &&
443+
childMax.x <= mainMax.x && childMax.y <= mainMax.y && childMax.z <= mainMax.z)
444+
{
445+
continue; // Child box is fully contained, skip adding it.
446+
}
447+
hit_testable_bounding_boxes_.push_back(childBoundingBox);
448+
}
419449
}
420-
hit_testable_bounding_box_ = geometry::BoundingBox(unionMin, unionMax, glm::mat4(1.0f));
421450

422-
// Notify the parent box to update its hit-testable bounding box.
423-
if (parent() && parent()->isBox())
424-
{
425-
parentBox()->updateHitTestableBoundingBox();
426-
}
451+
assert(hit_testable_bounding_boxes_.size() > 0);
427452
}
428453
}

src/client/layout/layout_box.hpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ namespace client_layout
198198

199199
protected:
200200
virtual bool hitTestChildren(HitTestResult &, const HitTestRay &, const glm::vec3 &accumulatedOffset, HitTestPhase);
201+
virtual void didComputeLayout(const ConstraintSpace &) override;
201202
virtual void didComputeLayoutOnce(const ConstraintSpace &) override;
202203

203204
void updateFromStyle() override;
@@ -213,11 +214,11 @@ namespace client_layout
213214
{
214215
return overflow_ != nullptr && overflow_->visualOverflow;
215216
}
216-
inline geometry::BoundingBox getHitTestableBoundingBox() const
217+
inline const std::vector<geometry::BoundingBox> &getHitTestableBoundingBoxes() const
217218
{
218-
return hit_testable_bounding_box_;
219+
return hit_testable_bounding_boxes_;
219220
}
220-
void updateHitTestableBoundingBox();
221+
void updateHitTestableBoundingBoxes();
221222

222223
glm::vec3 computeSize() const;
223224
vector<std::shared_ptr<LayoutBox>> getChildBoxes() const;
@@ -229,6 +230,6 @@ namespace client_layout
229230

230231
private:
231232
std::shared_ptr<BoxOverflowModel> overflow_;
232-
geometry::BoundingBox hit_testable_bounding_box_;
233+
std::vector<geometry::BoundingBox> hit_testable_bounding_boxes_;
233234
};
234235
}

src/client/layout/layout_object.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -949,11 +949,15 @@ namespace client_layout
949949
}
950950
}
951951

952-
void LayoutObject::willComputeLayout(const ConstraintSpace &avilableSpace)
952+
void LayoutObject::willComputeLayout(const ConstraintSpace &availableSpace)
953953
{
954954
}
955955

956-
void LayoutObject::didComputeLayoutOnce(const ConstraintSpace &avilableSpace)
956+
void LayoutObject::didComputeLayout(const ConstraintSpace &availableSpace)
957+
{
958+
}
959+
960+
void LayoutObject::didComputeLayoutOnce(const ConstraintSpace &availableSpace)
957961
{
958962
if (hasSceneComponent<WebContent>())
959963
{

src/client/layout/layout_object.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@ namespace client_layout
456456
virtual void sizeDidChange(const Fragment &);
457457

458458
virtual void willComputeLayout(const ConstraintSpace &);
459+
virtual void didComputeLayout(const ConstraintSpace &);
459460
virtual void didComputeLayoutOnce(const ConstraintSpace &);
460461

461462
private:

src/client/layout/layout_view.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,16 @@ namespace client_layout
4848
{
4949
}
5050

51-
bool LayoutView::computeLayout(const ConstraintSpace &avilableSpace)
51+
bool LayoutView::computeLayout(const ConstraintSpace &availableSpace)
5252
{
5353
function<void(LayoutObject &, const LayoutObject &)> traverseChildNode =
5454
[&traverseChildNode](LayoutObject &object, const LayoutObject &parent)
5555
{
5656
if (object.isNone())
5757
return;
58-
object.didComputeLayoutOnce(parent.fragment());
58+
59+
const auto &space = parent.fragment();
60+
object.didComputeLayoutOnce(space);
5961

6062
// traverse the children of the block or inline object.
6163
if (object.isLayoutBlock())
@@ -70,20 +72,25 @@ namespace client_layout
7072
for (shared_ptr<LayoutObject> child : inlineObject->childrenRef())
7173
traverseChildNode(*child, *inlineObject); // Just traverse the inline's children but don't compute layout.
7274
}
75+
76+
// Finally, call `didComputeLayout` for the object itself.
77+
object.didComputeLayout(space);
7378
};
7479

7580
// TODO(yorkie): support the lifecycle `willComputeLayout`?
7681

7782
// Use taffy to compute the layout.
78-
bool r = LayoutBlockFlow::computeLayout(avilableSpace);
79-
didComputeLayoutOnce(avilableSpace);
83+
bool r = LayoutBlockFlow::computeLayout(availableSpace);
84+
didComputeLayoutOnce(availableSpace);
8085

8186
// Traverse the children of the view and call `didComputeLayout` for each child.
8287
// This lifecycle `didComputeLayout` is used to setup for the next layout computation such as setting the content
8388
// size for text and replaced elements.
8489
for (shared_ptr<LayoutObject> child : childrenRef())
8590
traverseChildNode(*child, *this);
8691

92+
// Finally, call `didComputeLayout` for the view itself.
93+
didComputeLayout(availableSpace);
8794
return r;
8895
}
8996

0 commit comments

Comments
 (0)