Skip to content

Commit c9add70

Browse files
committed
Fix floated auto-width sizing and line-height text placement
Preserve shrink-to-fit widths for floated HTML boxes whose CSS width is auto, instead of letting their own block layout expand them back to the containing block width. This matches the CSS float model and fixes floated list-item menus where hover boxes were much wider than their content. Also separate RichText visual font metrics from CSS line-box metrics. Text runs now keep native font height for drawing while line layout still uses CSS line-height, allowing the expected half-leading offset for vertically centered text in block anchors.
1 parent e984c87 commit c9add70

5 files changed

Lines changed: 104 additions & 18 deletions

File tree

2 Bytes
Loading
-96 Bytes
Loading

src/eepp/graphics/richtext.cpp

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,31 @@ static Float getTextRunLineHeight( const std::shared_ptr<Text>& text, Float line
1414
return lineHeight > 0 ? lineHeight : fontStyle.Font->getLineSpacing( fontStyle.CharacterSize );
1515
}
1616

17-
static Float getTextBaseline( const std::shared_ptr<Text>& text, Float lineHeight ) {
17+
static Float getTextVisualLineHeight( const std::shared_ptr<Text>& text, Float lineHeight ) {
18+
if ( !text || !text->getFontStyleConfig().Font )
19+
return lineHeight;
20+
const auto& fontStyle = text->getFontStyleConfig();
21+
return fontStyle.Font->getLineSpacing( fontStyle.CharacterSize );
22+
}
23+
24+
static Float getTextVisualBaseline( const std::shared_ptr<Text>& text ) {
1825
if ( !text || !text->getFontStyleConfig().Font )
1926
return 0.f;
2027
const auto& fontStyle = text->getFontStyleConfig();
21-
Float fontLineSpacing = fontStyle.Font->getLineSpacing( fontStyle.CharacterSize );
22-
Float usedLineHeight = getTextRunLineHeight( text, lineHeight );
28+
return fontStyle.Font->getAscent( fontStyle.CharacterSize );
29+
}
30+
31+
static Float getTextLineBoxBaselineForHeight( const std::shared_ptr<Text>& text,
32+
Float usedLineHeight ) {
33+
if ( !text || !text->getFontStyleConfig().Font )
34+
return 0.f;
35+
Float fontLineSpacing = getTextVisualLineHeight( text, usedLineHeight );
2336
Float leading = eemax( 0.f, usedLineHeight - fontLineSpacing ) * 0.5f;
24-
return leading + fontStyle.Font->getAscent( fontStyle.CharacterSize );
37+
return leading + getTextVisualBaseline( text );
38+
}
39+
40+
static Float getTextLineBoxBaseline( const std::shared_ptr<Text>& text, Float lineHeight ) {
41+
return getTextLineBoxBaselineForHeight( text, getTextRunLineHeight( text, lineHeight ) );
2542
}
2643

2744
static Float getFontXHeight( const FontStyleConfig& fontStyle ) {
@@ -839,7 +856,7 @@ class RichTextInlineLayouter {
839856
bool isFloat = span.floatType != RichText::InlineFloat::None;
840857

841858
if ( span.type == RichText::RenderSpan::Type::Text ) {
842-
Float baseline = getTextBaseline( span.text, span.lineHeight );
859+
Float baseline = getTextVisualBaseline( span.text );
843860
RichText::BaselineAlignValue baselineAlign = effectiveInlineBaselineAlign(
844861
inlineItems, span.inlinePath, span.baselineAlign );
845862
Float offsetY = getBaselineAlignedOffset(
@@ -1749,34 +1766,38 @@ class RichTextInlineLayouter {
17491766
const std::vector<RichText::InlineItem>& inlineItems ) {
17501767
Float maxAscent = 0.f;
17511768
Float maxDescent = 0.f;
1752-
bool hasParticipatingAncestorLineHeight = false;
1769+
bool hasParticipatingLineHeight = forcedLineHeight > 0.f;
17531770

17541771
for ( const auto& span : line.spans ) {
17551772
if ( span.type == RichText::RenderSpan::Type::Text ) {
17561773
InlineVerticalEdges edges = inlineAncestorLineHeightEdges(
17571774
inlineItems, span.inlinePath, span.size.getHeight() );
1758-
hasParticipatingAncestorLineHeight =
1759-
hasParticipatingAncestorLineHeight || edges.top > 0 || edges.bottom > 0;
1760-
Float baseline = getTextBaseline( span.text, span.lineHeight );
1775+
hasParticipatingLineHeight =
1776+
hasParticipatingLineHeight || edges.top > 0 || edges.bottom > 0;
1777+
Float lineHeight =
1778+
span.lineHeight > 0.f
1779+
? getTextRunLineHeight( span.text, span.lineHeight )
1780+
: eemax( forcedLineHeight,
1781+
getTextRunLineHeight( span.text, span.lineHeight ) );
1782+
Float baseline = getTextLineBoxBaselineForHeight( span.text, lineHeight );
17611783
maxAscent = std::max( maxAscent, baseline + edges.top );
1762-
maxDescent =
1763-
std::max( maxDescent, span.size.getHeight() - baseline + edges.bottom );
1784+
maxDescent = std::max( maxDescent, lineHeight - baseline + edges.bottom );
17641785
} else {
17651786
bool isFloat = span.floatType != RichText::InlineFloat::None;
17661787
Float baseline = span.baseline;
17671788
InlineVerticalEdges edges = inlineAncestorLineHeightEdges(
17681789
inlineItems, span.inlinePath, span.size.getHeight() );
17691790
if ( preserveFloatPositions && isFloat )
17701791
continue;
1771-
hasParticipatingAncestorLineHeight =
1772-
hasParticipatingAncestorLineHeight || edges.top > 0 || edges.bottom > 0;
1792+
hasParticipatingLineHeight =
1793+
hasParticipatingLineHeight || edges.top > 0 || edges.bottom > 0;
17731794
maxAscent = std::max( maxAscent, baseline + edges.top );
17741795
maxDescent =
17751796
std::max( maxDescent, span.size.getHeight() - baseline + edges.bottom );
17761797
}
17771798
}
17781799

1779-
if ( !hasParticipatingAncestorLineHeight )
1800+
if ( !hasParticipatingLineHeight )
17801801
return;
17811802

17821803
line.maxAscent = maxAscent;
@@ -1796,8 +1817,9 @@ class RichTextInlineLayouter {
17961817
renderSpanText->setStyleConfig( renderStyle );
17971818
renderSpanText->setTabWidth( payload.text->getTabWidth() );
17981819

1799-
Float height = getTextRunLineHeight( payload.text, payload.lineHeight );
1800-
Float ascent = getTextBaseline( payload.text, payload.lineHeight );
1820+
Float height = getTextVisualLineHeight( payload.text, payload.lineHeight );
1821+
Float lineHeight = getTextRunLineHeight( payload.text, payload.lineHeight );
1822+
Float ascent = getTextLineBoxBaseline( payload.text, payload.lineHeight );
18011823
Float spanWidth = renderSpanText->getTextWidth();
18021824

18031825
RichText::RenderSpan renderSpan;
@@ -1818,7 +1840,7 @@ class RichTextInlineLayouter {
18181840

18191841
line.spans.push_back( std::move( renderSpan ) );
18201842
line.maxAscent = std::max( line.maxAscent, ascent );
1821-
line.height = std::max( line.height, height );
1843+
line.height = std::max( line.height, lineHeight );
18221844

18231845
curX += spanWidth;
18241846
line.width += spanWidth;

src/eepp/ui/blocklayouter.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ void BlockLayouter::updateLayout() {
102102

103103
bool preserveFloatConstrainedBFCWidth =
104104
widget->establishesBlockFormattingContext() && !rt->getExternalFloatExclusions().empty();
105-
if ( !preserveFloatConstrainedBFCWidth && !parentIsFlex )
105+
bool preserveShrinkToFitFloatWidth = widget->getCSSFloat() != CSSFloat::None &&
106+
widget->getLayoutWidthPolicy() == SizePolicy::MatchParent;
107+
if ( !preserveFloatConstrainedBFCWidth && !preserveShrinkToFitFloatWidth && !parentIsFlex )
106108
setMatchParentIfNeededVerticalGrowth();
107109

108110
const StyleSheetProperty* prop = nullptr;

src/tests/unit_tests/uihtml_float_tests.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,68 @@ UTEST( UIHTMLFloat, rightFloatedInlineSpansAlignAtContainerRightAfterBlock ) {
552552
Engine::destroySingleton();
553553
}
554554

555+
UTEST( UIHTMLFloat, floatedListItemsShrinkToFitBlockAnchors ) {
556+
init_float_test();
557+
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
558+
559+
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( R"html(
560+
<body style="margin:0">
561+
<div id="access" style="background:#000;display:block;float:left;width:940px">
562+
<div id="menu" class="menu" style="font-size:13px;margin-left:12px;width:928px">
563+
<ul id="top" style="list-style:none;margin:0;padding:0">
564+
<li id="home" style="float:left;position:relative">
565+
<a id="home-a" style="display:block;line-height:38px;padding:0 10px">Home</a>
566+
</li>
567+
<li id="about" style="float:left;position:relative">
568+
<a id="about-a" style="display:block;line-height:38px;padding:0 10px">About</a>
569+
</li>
570+
<li id="os2" style="float:left;position:relative">
571+
<a id="os2-a" style="display:block;line-height:38px;padding:0 10px">OS/2 History</a>
572+
<ul id="os2-sub" style="display:none;position:absolute;top:38px;left:0;float:left;width:180px">
573+
<li style="min-width:180px"><a style="display:block;line-height:1em;padding:10px;width:160px">OS/2 Beginnings</a></li>
574+
</ul>
575+
</li>
576+
</ul>
577+
</div>
578+
</div>
579+
</body>
580+
)html" ) );
581+
sceneNode->updateDirtyLayouts();
582+
583+
auto* menu = sceneNode->find<UIWidget>( "menu" );
584+
auto* home = sceneNode->find<UIWidget>( "home" );
585+
auto* about = sceneNode->find<UIWidget>( "about" );
586+
auto* os2 = sceneNode->find<UIWidget>( "os2" );
587+
auto* os2Anchor = sceneNode->find<UIWidget>( "os2-a" );
588+
ASSERT_TRUE( menu != nullptr );
589+
ASSERT_TRUE( home != nullptr );
590+
ASSERT_TRUE( about != nullptr );
591+
ASSERT_TRUE( os2 != nullptr );
592+
ASSERT_TRUE( os2Anchor != nullptr );
593+
594+
EXPECT_EQ( os2->asType<UIHTMLWidget>()->getCSSFloat(), CSSFloat::Left );
595+
EXPECT_LT( os2->getPixelsSize().getWidth(), menu->getPixelsSize().getWidth() * 0.5f );
596+
EXPECT_NEAR( os2->getPixelsSize().getWidth(), os2Anchor->getPixelsSize().getWidth(), 1.f );
597+
EXPECT_NEAR( os2->getPixelsSize().getHeight(), 38.f, 1.f );
598+
EXPECT_NEAR( os2Anchor->getPixelsSize().getHeight(), 38.f, 1.f );
599+
auto* os2AnchorRichText = os2Anchor->asType<UIRichText>()->getRichTextPtr();
600+
ASSERT_TRUE( os2AnchorRichText != nullptr );
601+
ASSERT_EQ( os2AnchorRichText->getLines().size(), (size_t)1 );
602+
ASSERT_EQ( os2AnchorRichText->getLines().front().spans.size(), (size_t)1 );
603+
const auto& os2TextSpan = os2AnchorRichText->getLines().front().spans.front();
604+
EXPECT_GT( os2TextSpan.position.y, 1.f );
605+
EXPECT_LT( os2TextSpan.position.y + os2TextSpan.size.getHeight(),
606+
os2Anchor->getPixelsSize().getHeight() - 1.f );
607+
EXPECT_NEAR( home->getPixelsPosition().y, os2->getPixelsPosition().y, 1.f );
608+
EXPECT_NEAR( about->getPixelsPosition().y, os2->getPixelsPosition().y, 1.f );
609+
EXPECT_GE( about->getPixelsPosition().x,
610+
home->getPixelsPosition().x + home->getPixelsSize().getWidth() - 1.f );
611+
EXPECT_GE( os2->getPixelsPosition().x,
612+
about->getPixelsPosition().x + about->getPixelsSize().getWidth() - 1.f );
613+
614+
Engine::destroySingleton();
615+
}
616+
555617
UTEST( UIHTMLFloat, autoHorizontalMarginsCenterBlockInsideFloat ) {
556618
init_float_test();
557619
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();

0 commit comments

Comments
 (0)