mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-28 00:48:44 +03:00
Fixes in HTML td height.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include <eepp/core/containers.hpp>
|
||||
#include <eepp/graphics/richtext.hpp>
|
||||
#include <eepp/ui/blocklayouter.hpp>
|
||||
#include <eepp/ui/uihtmltable.hpp>
|
||||
#include <eepp/ui/uihtmlwidget.hpp>
|
||||
#include <eepp/ui/uirichtext.hpp>
|
||||
#include <eepp/ui/uistyle.hpp>
|
||||
@@ -27,6 +28,11 @@ static bool isStretchedFlexItem( UIHTMLWidget* widget ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isTableCellInTableRow( UIWidget* widget ) {
|
||||
return widget && widget->isType( UI_TYPE_HTML_TABLE_CELL ) && widget->getParent() &&
|
||||
widget->getParent()->isType( UI_TYPE_HTML_TABLE_ROW );
|
||||
}
|
||||
|
||||
Float BlockLayouter::getMinIntrinsicWidth() {
|
||||
computeIntrinsicWidths();
|
||||
return mMinIntrinsicWidth;
|
||||
@@ -106,7 +112,8 @@ void BlockLayouter::updateLayout() {
|
||||
{ mContainer->lengthFromValue( *prop ), mContainer->getPixelsSize().getHeight() } );
|
||||
}
|
||||
|
||||
if ( mContainer->getLayoutHeightPolicy() == SizePolicy::Fixed && mContainer->getUIStyle() &&
|
||||
if ( !isTableCellInTableRow( mContainer ) &&
|
||||
mContainer->getLayoutHeightPolicy() == SizePolicy::Fixed && mContainer->getUIStyle() &&
|
||||
( prop = mContainer->getUIStyle()->getProperty( PropertyId::Height ) ) ) {
|
||||
mContainer->setInternalPixelsSize(
|
||||
{ mContainer->getPixelsSize().getWidth(), mContainer->lengthFromValue( *prop ) } );
|
||||
|
||||
@@ -8,6 +8,36 @@ static inline Float sanitizeFloat( Float val ) {
|
||||
return std::isfinite( val ) ? val : 0.f;
|
||||
}
|
||||
|
||||
static Float specifiedHeightPx( UIWidget* widget ) {
|
||||
if ( widget == nullptr || widget->getUIStyle() == nullptr )
|
||||
return 0.f;
|
||||
|
||||
const StyleSheetProperty* prop = widget->getUIStyle()->getProperty( PropertyId::Height );
|
||||
if ( prop == nullptr )
|
||||
return 0.f;
|
||||
|
||||
return sanitizeFloat( widget->lengthFromValue( *prop ) );
|
||||
}
|
||||
|
||||
static Float normalFlowChildrenBottomPx( UIWidget* widget ) {
|
||||
if ( widget == nullptr )
|
||||
return 0.f;
|
||||
|
||||
Float bottom = 0.f;
|
||||
for ( Node* child = widget->getFirstChild(); child; child = child->getNextNode() ) {
|
||||
if ( !child->isWidget() || !child->isVisible() )
|
||||
continue;
|
||||
if ( child->isType( UI_TYPE_HTML_WIDGET ) && child->asType<UIHTMLWidget>()->isOutOfFlow() )
|
||||
continue;
|
||||
|
||||
UIWidget* childWidget = child->asType<UIWidget>();
|
||||
const Float childTop = childWidget->getPixelsPosition().y;
|
||||
bottom = std::max( bottom, childTop + childWidget->getPixelsSize().getHeight() );
|
||||
bottom = std::max( bottom, childTop + normalFlowChildrenBottomPx( childWidget ) );
|
||||
}
|
||||
return sanitizeFloat( bottom );
|
||||
}
|
||||
|
||||
void TableLayouter::setTableLayout( TableLayout layout ) {
|
||||
if ( layout != mTableLayout ) {
|
||||
mTableLayout = layout;
|
||||
@@ -479,9 +509,9 @@ void TableLayouter::updateLayout() {
|
||||
Uint32 colIndex = 0;
|
||||
for ( Uint32 c = 0; c < columnCount; ++c ) {
|
||||
UIHTMLTableCell* cell = mCells[start + c];
|
||||
cell->beginAttributesTransaction();
|
||||
cell->setLayoutWidthPolicy( SizePolicy::Fixed );
|
||||
cell->setLayoutHeightPolicy( SizePolicy::WrapContent );
|
||||
Float cellSpecifiedHeight = specifiedHeightPx( cell );
|
||||
cell->mWidthPolicy = SizePolicy::Fixed;
|
||||
cell->mHeightPolicy = SizePolicy::WrapContent;
|
||||
Uint32 cellColspan = cell->getColSpan();
|
||||
Float cellWidth = 0;
|
||||
for ( Uint32 j = 0; j < cellColspan && ( colIndex + j ) < maxCols; ++j ) {
|
||||
@@ -490,19 +520,30 @@ void TableLayouter::updateLayout() {
|
||||
if ( cellColspan > 1 )
|
||||
cellWidth += ( cellColspan - 1 ) * mCellspacing;
|
||||
cell->setPixelsSize( cellWidth, cell->getPixelsSize().getHeight() );
|
||||
cell->updateLayout();
|
||||
cell->setLayoutHeightPolicy( SizePolicy::Fixed );
|
||||
cell->endAttributesTransaction();
|
||||
rowHeight = std::max( rowHeight, cell->getPixelsSize().getHeight() );
|
||||
cell->UIRichText::updateLayout();
|
||||
cell->mHeightPolicy = SizePolicy::Fixed;
|
||||
// CSS Tables: a single-row table-cell height contributes to the row minimum
|
||||
// height. It must not clamp content that needs more space.
|
||||
Float cellMinHeight =
|
||||
std::max( cell->getPixelsSize().getHeight(), cellSpecifiedHeight );
|
||||
if ( cellSpecifiedHeight > 0.f )
|
||||
cellMinHeight = std::max( cellMinHeight, normalFlowChildrenBottomPx( cell ) );
|
||||
rowHeight = std::max( rowHeight, cellMinHeight );
|
||||
colIndex += cellColspan;
|
||||
}
|
||||
|
||||
UIHTMLTableRow* row = mRows[r];
|
||||
// CSS Tables: row height is also a minimum, not a hard height.
|
||||
if ( row->getLayoutHeightPolicy() == SizePolicy::Fixed )
|
||||
rowHeight = std::max( rowHeight, specifiedHeightPx( row ) );
|
||||
|
||||
Float currentX = mCellspacing;
|
||||
colIndex = 0;
|
||||
for ( Uint32 c = 0; c < columnCount; ++c ) {
|
||||
UIHTMLTableCell* cell = mCells[start + c];
|
||||
cell->beginAttributesTransaction();
|
||||
cell->setPixelsPosition( currentX, 0 );
|
||||
cell->mWidthPolicy = SizePolicy::Fixed;
|
||||
cell->mHeightPolicy = SizePolicy::Fixed;
|
||||
Uint32 cellColspan = cell->getColSpan();
|
||||
Float cellWidth = 0;
|
||||
for ( Uint32 j = 0; j < cellColspan && ( colIndex + j ) < maxCols; ++j ) {
|
||||
@@ -510,19 +551,11 @@ void TableLayouter::updateLayout() {
|
||||
}
|
||||
if ( cellColspan > 1 )
|
||||
cellWidth += ( cellColspan - 1 ) * mCellspacing;
|
||||
cell->setPixelsSize( cellWidth, rowHeight );
|
||||
cell->endAttributesTransaction();
|
||||
cell->setInternalPixelsSize( { cellWidth, rowHeight } );
|
||||
currentX += cellWidth + mCellspacing;
|
||||
colIndex += cellColspan;
|
||||
}
|
||||
|
||||
UIHTMLTableRow* row = mRows[r];
|
||||
// Respect the row's specified height (e.g., <tr style="height: 10px">)
|
||||
if ( row->getLayoutHeightPolicy() == SizePolicy::Fixed ) {
|
||||
Float rowSpecifiedHeight = row->getPropertyHeight();
|
||||
if ( rowSpecifiedHeight > 0.f )
|
||||
rowHeight = std::max( rowHeight, rowSpecifiedHeight );
|
||||
}
|
||||
row->setPixelsSize( containerWidth - paddingH, rowHeight );
|
||||
|
||||
if ( r == 0 && mCells[start]->getParent() &&
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <eepp/ui/uitextspan.hpp>
|
||||
#include <eepp/ui/uithememanager.hpp>
|
||||
#include <eepp/window/engine.hpp>
|
||||
#include <eepp/window/input.hpp>
|
||||
#include <limits>
|
||||
|
||||
using namespace EE;
|
||||
@@ -1869,6 +1870,122 @@ UTEST( UIHTMLTable, basicLayout ) {
|
||||
destroyRichTextScene( sceneNode );
|
||||
}
|
||||
|
||||
UTEST( UIHTMLTable, cellHeightIsMinimumForInlineContentHitTest ) {
|
||||
auto sceneNode = createRichTextScene();
|
||||
ASSERT_TRUE( sceneNode != nullptr );
|
||||
|
||||
String xml = R"xml(
|
||||
<table id="table" layout_width="200dp" layout_height="wrap_content">
|
||||
<tr>
|
||||
<td id="cell" style="height: 10px; line-height: 24px">
|
||||
<a id="link" href="#">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
)xml";
|
||||
|
||||
sceneNode->loadLayoutFromString( xml );
|
||||
sceneNode->update( Time::Zero );
|
||||
|
||||
auto* table = sceneNode->find<UIHTMLTable>( "table" );
|
||||
auto* cell = sceneNode->find<UIHTMLTableCell>( "cell" );
|
||||
auto* link = sceneNode->find<UIAnchorSpan>( "link" );
|
||||
ASSERT_TRUE( table != nullptr );
|
||||
ASSERT_TRUE( cell != nullptr );
|
||||
ASSERT_TRUE( link != nullptr );
|
||||
ASSERT_FALSE( link->getHitBoxes().empty() );
|
||||
|
||||
const Rectf& hitBox = link->getHitBoxes().front();
|
||||
EXPECT_GT( cell->getPixelsSize().getHeight(), 10.f );
|
||||
EXPECT_GE( cell->getPixelsSize().getHeight(), link->getPixelsPosition().y + hitBox.Bottom );
|
||||
|
||||
Vector2f hitPos = link->convertToWorldSpace(
|
||||
{ hitBox.Left + 1.f, eemax( hitBox.Top, hitBox.Bottom - 1.f ) } );
|
||||
Node* hitNode = table->overFind( hitPos );
|
||||
EXPECT_EQ( hitNode, link );
|
||||
|
||||
destroyRichTextScene( sceneNode );
|
||||
}
|
||||
|
||||
UTEST( UIHTMLTable, cellHeightStretchesToRowMinimum ) {
|
||||
auto sceneNode = createRichTextScene();
|
||||
ASSERT_TRUE( sceneNode != nullptr );
|
||||
|
||||
String xml = R"xml(
|
||||
<table id="table" layout_width="200dp" layout_height="wrap_content">
|
||||
<tr>
|
||||
<td id="logo"><img id="img" style="width: 18px; height: 18px; display: block" /></td>
|
||||
<td id="nav" style="height: 10px">Nav</td>
|
||||
</tr>
|
||||
</table>
|
||||
)xml";
|
||||
|
||||
sceneNode->loadLayoutFromString( xml );
|
||||
sceneNode->update( Time::Zero );
|
||||
|
||||
auto* logo = sceneNode->find<UIHTMLTableCell>( "logo" );
|
||||
auto* nav = sceneNode->find<UIHTMLTableCell>( "nav" );
|
||||
ASSERT_TRUE( logo != nullptr );
|
||||
ASSERT_TRUE( nav != nullptr );
|
||||
|
||||
EXPECT_GT( logo->getPixelsSize().getHeight(), 10.f );
|
||||
EXPECT_EQ( nav->getPixelsSize().getHeight(), logo->getPixelsSize().getHeight() );
|
||||
|
||||
destroyRichTextScene( sceneNode );
|
||||
}
|
||||
|
||||
UTEST( UIHTMLTable, tableCellAnchorHoverRelayoutsRichText ) {
|
||||
auto sceneNode = createRichTextScene();
|
||||
ASSERT_TRUE( sceneNode != nullptr );
|
||||
|
||||
String xml = R"xml(
|
||||
<table id="table" layout_width="200dp" layout_height="wrap_content">
|
||||
<style>
|
||||
a { text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
</style>
|
||||
<tr>
|
||||
<td id="logo"><img id="img" style="width: 18px; height: 18px; display: block" /></td>
|
||||
<td id="nav" style="height: 10px"><a id="link" href="#">Nav</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
)xml";
|
||||
|
||||
sceneNode->loadLayoutFromString( xml );
|
||||
sceneNode->update( Time::Zero );
|
||||
|
||||
auto* nav = sceneNode->find<UIHTMLTableCell>( "nav" );
|
||||
auto* link = sceneNode->find<UIAnchorSpan>( "link" );
|
||||
ASSERT_TRUE( nav != nullptr );
|
||||
ASSERT_TRUE( link != nullptr );
|
||||
ASSERT_FALSE( link->getHitBoxes().empty() );
|
||||
auto renderedAnchorStyle = [&nav]() -> Uint32 {
|
||||
for ( const auto& line : nav->getRichText().getLines() ) {
|
||||
for ( const auto& span : line.spans ) {
|
||||
if ( span.type == RichText::RenderSpan::Type::Text && span.text &&
|
||||
span.text->getString() == "Nav" )
|
||||
return span.text->getStyle();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
EXPECT_FALSE( ( renderedAnchorStyle() & Text::Underlined ) != 0 );
|
||||
EXPECT_GT( nav->getPixelsSize().getHeight(), 10.f );
|
||||
|
||||
const Rectf& hitBox = link->getHitBoxes().front();
|
||||
Vector2f hoverPos = link->convertToWorldSpace( { hitBox.Left + 1.f, hitBox.Top + 1.f } );
|
||||
EXPECT_EQ( sceneNode->overFind( hoverPos ), link );
|
||||
|
||||
sceneNode->getEventDispatcher()->getInput()->setMousePos( hoverPos.asInt() );
|
||||
sceneNode->update( Time::Zero );
|
||||
|
||||
EXPECT_TRUE( ( renderedAnchorStyle() & Text::Underlined ) != 0 );
|
||||
EXPECT_GT( nav->getPixelsSize().getHeight(), 10.f );
|
||||
|
||||
destroyRichTextScene( sceneNode );
|
||||
}
|
||||
|
||||
UTEST( UIRichText, WhitespaceCollapseBRTest ) {
|
||||
auto sceneNode = createRichTextScene();
|
||||
ASSERT_TRUE( sceneNode != nullptr );
|
||||
|
||||
Reference in New Issue
Block a user