mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
Fixes for absolute positioning and some minor details.
This commit is contained in:
54
bin/unit_tests/assets/html/absolute_position.html
Normal file
54
bin/unit_tests/assets/html/absolute_position.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<style type="text/css">
|
||||
body {
|
||||
background: #000000;
|
||||
color: #FFFFFF;
|
||||
font-family: Verdana, Helvetica, Arial, sans-serif;
|
||||
font-size: 8pt;
|
||||
font-weight:normal;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#main {
|
||||
width: 400px;
|
||||
position:absolute;
|
||||
left:50%;
|
||||
margin-left: -200px;
|
||||
margin-top: 120px;
|
||||
border: 0px;
|
||||
}
|
||||
.box {
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
background-color: #333333;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main">
|
||||
<div class="box">
|
||||
<div class="titlebox">File Upload</div>
|
||||
<div class="login_inbox">
|
||||
<div class="loginbox">
|
||||
<div class="mini_titlebox">[LOGIN]</div>
|
||||
<form method="post" action="?s=1">
|
||||
<p>Username: <input name="Nombre" type="text" size="12" /></p>
|
||||
<p>Password: <input name="Password" type="password" size="12" /></p>
|
||||
<p><input type="submit" name="Submit" value="Entrar" /></p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="titlebox">Download Files</div>
|
||||
<div class="inbox">
|
||||
<a href="upload/">ENTER</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -18,8 +18,6 @@ class EE_API UIHTMLTable : public UIHTMLWidget {
|
||||
|
||||
virtual bool isType( const Uint32& type ) const;
|
||||
|
||||
virtual void updateLayout();
|
||||
|
||||
virtual Float getMinIntrinsicWidth() const;
|
||||
|
||||
virtual Float getMaxIntrinsicWidth() const;
|
||||
|
||||
@@ -61,6 +61,10 @@ class EE_API UIHTMLWidget : public UILayout {
|
||||
protected:
|
||||
CSSDisplay mDisplay{ CSSDisplay::Block };
|
||||
CSSPosition mPosition{ CSSPosition::Static };
|
||||
std::string mTopEq{ "auto" };
|
||||
std::string mRightEq{ "auto" };
|
||||
std::string mBottomEq{ "auto" };
|
||||
std::string mLeftEq{ "auto" };
|
||||
Rectf mOffsets{ 0, 0, 0, 0 };
|
||||
int mZIndex{ 0 };
|
||||
UILayouter* mLayouter{ nullptr };
|
||||
|
||||
@@ -24,6 +24,9 @@ class EE_API UILayout : public UIWidget {
|
||||
bool isLayoutDirty() const { return mDirtyLayout; }
|
||||
|
||||
void onAutoSizeChild( UIWidget* child );
|
||||
|
||||
void setLayoutDirty();
|
||||
|
||||
protected:
|
||||
friend class UISceneNode;
|
||||
friend class UILayouter;
|
||||
@@ -51,8 +54,6 @@ class EE_API UILayout : public UIWidget {
|
||||
|
||||
virtual void updateLayoutWrappingContents();
|
||||
|
||||
void setLayoutDirty();
|
||||
|
||||
bool setMatchParentIfNeededVerticalGrowth();
|
||||
};
|
||||
|
||||
|
||||
@@ -129,8 +129,6 @@ class EE_API UIRichText : public UIHTMLWidget {
|
||||
|
||||
String getSelectionString() const;
|
||||
|
||||
virtual void updateLayout();
|
||||
|
||||
virtual RichText* getRichTextPtr() { return &mRichText; }
|
||||
|
||||
protected:
|
||||
|
||||
@@ -431,10 +431,18 @@ void StyleSheetSpecification::registerDefaultProperties() {
|
||||
registerProperty( "list-style-type", "none", true ).setType( PropertyType::String );
|
||||
registerProperty( "list-style-position", "outside", true ).setType( PropertyType::String );
|
||||
registerProperty( "list-style-image", "none" ).setType( PropertyType::String );
|
||||
registerProperty( "top", "auto" ).setType( PropertyType::NumberLength );
|
||||
registerProperty( "right", "auto" ).setType( PropertyType::NumberLength );
|
||||
registerProperty( "bottom", "auto" ).setType( PropertyType::NumberLength );
|
||||
registerProperty( "left", "auto" ).setType( PropertyType::NumberLength );
|
||||
registerProperty( "top", "auto" )
|
||||
.setType( PropertyType::NumberLength )
|
||||
.setRelativeTarget( PropertyRelativeTarget::ContainingBlockHeight );
|
||||
registerProperty( "right", "auto" )
|
||||
.setType( PropertyType::NumberLength )
|
||||
.setRelativeTarget( PropertyRelativeTarget::ContainingBlockWidth );
|
||||
registerProperty( "bottom", "auto" )
|
||||
.setType( PropertyType::NumberLength )
|
||||
.setRelativeTarget( PropertyRelativeTarget::ContainingBlockHeight );
|
||||
registerProperty( "left", "auto" )
|
||||
.setType( PropertyType::NumberLength )
|
||||
.setRelativeTarget( PropertyRelativeTarget::ContainingBlockWidth );
|
||||
registerProperty( "z-index", "auto" ).setType( PropertyType::NumberInt );
|
||||
|
||||
registerProperty( "inner-widget-orientation", "widgeticontextbox" )
|
||||
|
||||
@@ -85,16 +85,6 @@ Float UIHTMLTable::getMaxIntrinsicWidth() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UIHTMLTable::updateLayout() {
|
||||
UILayouter* layouter = const_cast<UIHTMLTable*>( this )->getLayouter();
|
||||
if ( layouter )
|
||||
getLayouter()->updateLayout();
|
||||
else
|
||||
UIHTMLWidget::updateLayout();
|
||||
|
||||
mDirtyLayout = false;
|
||||
}
|
||||
|
||||
Uint32 UIHTMLTable::onMessage( const NodeMessage* Msg ) {
|
||||
switch ( Msg->getMsg() ) {
|
||||
case NodeMessage::LayoutAttributeChange: {
|
||||
|
||||
@@ -60,6 +60,10 @@ void UIHTMLWidget::setCSSPosition( CSSPosition position ) {
|
||||
void UIHTMLWidget::setOffsets( const Rectf& offsets ) {
|
||||
if ( mOffsets != offsets ) {
|
||||
mOffsets = offsets;
|
||||
mTopEq = String::fromFloat( offsets.Top, "dp" );
|
||||
mLeftEq = String::fromFloat( offsets.Left, "dp" );
|
||||
mRightEq = String::fromFloat( offsets.Right, "dp" );
|
||||
mBottomEq = String::fromFloat( offsets.Bottom, "dp" );
|
||||
notifyLayoutAttrChange();
|
||||
}
|
||||
}
|
||||
@@ -79,13 +83,13 @@ std::string UIHTMLWidget::getPropertyString( const PropertyDefinition* propertyD
|
||||
case PropertyId::Position:
|
||||
return CSSPositionHelper::toString( mPosition );
|
||||
case PropertyId::Top:
|
||||
return String::fromFloat( mOffsets.Top, "dp" );
|
||||
return mTopEq;
|
||||
case PropertyId::Right:
|
||||
return String::fromFloat( mOffsets.Right, "dp" );
|
||||
return mRightEq;
|
||||
case PropertyId::Bottom:
|
||||
return String::fromFloat( mOffsets.Bottom, "dp" );
|
||||
return mBottomEq;
|
||||
case PropertyId::Left:
|
||||
return String::fromFloat( mOffsets.Left, "dp" );
|
||||
return mLeftEq;
|
||||
case PropertyId::ZIndex:
|
||||
return String::toString( mZIndex );
|
||||
default:
|
||||
@@ -111,34 +115,22 @@ bool UIHTMLWidget::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
return true;
|
||||
}
|
||||
case PropertyId::Top: {
|
||||
if ( attribute.asString() == "auto" )
|
||||
mOffsets.Top = 0;
|
||||
else
|
||||
mOffsets.Top = lengthFromValueAsDp( attribute );
|
||||
mTopEq = attribute.asString();
|
||||
notifyLayoutAttrChange();
|
||||
return true;
|
||||
}
|
||||
case PropertyId::Right: {
|
||||
if ( attribute.asString() == "auto" )
|
||||
mOffsets.Right = 0;
|
||||
else
|
||||
mOffsets.Right = lengthFromValueAsDp( attribute );
|
||||
mRightEq = attribute.asString();
|
||||
notifyLayoutAttrChange();
|
||||
return true;
|
||||
}
|
||||
case PropertyId::Bottom: {
|
||||
if ( attribute.asString() == "auto" )
|
||||
mOffsets.Bottom = 0;
|
||||
else
|
||||
mOffsets.Bottom = lengthFromValueAsDp( attribute );
|
||||
mBottomEq = attribute.asString();
|
||||
notifyLayoutAttrChange();
|
||||
return true;
|
||||
}
|
||||
case PropertyId::Left: {
|
||||
if ( attribute.asString() == "auto" )
|
||||
mOffsets.Left = 0;
|
||||
else
|
||||
mOffsets.Left = lengthFromValueAsDp( attribute );
|
||||
mLeftEq = attribute.asString();
|
||||
notifyLayoutAttrChange();
|
||||
return true;
|
||||
}
|
||||
@@ -148,7 +140,6 @@ bool UIHTMLWidget::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
|
||||
return UILayout::applyProperty( attribute );
|
||||
}
|
||||
|
||||
void UIHTMLWidget::updateLayout() {
|
||||
if ( getLayouter() )
|
||||
getLayouter()->updateLayout();
|
||||
@@ -156,6 +147,8 @@ void UIHTMLWidget::updateLayout() {
|
||||
UILayout::updateLayout();
|
||||
|
||||
positionOutOfFlowChildren();
|
||||
|
||||
mDirtyLayout = false;
|
||||
}
|
||||
|
||||
UIWidget* UIHTMLWidget::getContainingBlock() {
|
||||
@@ -195,11 +188,22 @@ void UIHTMLWidget::positionOutOfFlowChildren() {
|
||||
if ( pos == CSSPosition::Absolute || pos == CSSPosition::Fixed ) {
|
||||
UIWidget* cb = htmlChild->getContainingBlock();
|
||||
if ( cb ) {
|
||||
Rectf offsets = htmlChild->getOffsets();
|
||||
Float top = PixelDensity::dpToPx( offsets.Top );
|
||||
Float left = PixelDensity::dpToPx( offsets.Left );
|
||||
Float top = htmlChild->mTopEq == "auto"
|
||||
? 0
|
||||
: htmlChild->lengthFromValue(
|
||||
htmlChild->mTopEq,
|
||||
CSS::PropertyRelativeTarget::ContainingBlockHeight, 0 );
|
||||
Float left = htmlChild->mLeftEq == "auto"
|
||||
? 0
|
||||
: htmlChild->lengthFromValue(
|
||||
htmlChild->mLeftEq,
|
||||
CSS::PropertyRelativeTarget::ContainingBlockWidth, 0 );
|
||||
|
||||
Vector2f cbPos( cb->getPixelsContentOffset().Left, cb->getPixelsContentOffset().Top );
|
||||
top += htmlChild->getLayoutPixelsMargin().Top;
|
||||
left += htmlChild->getLayoutPixelsMargin().Left;
|
||||
|
||||
Vector2f cbPos( cb->getPixelsContentOffset().Left,
|
||||
cb->getPixelsContentOffset().Top );
|
||||
cbPos.x += left;
|
||||
cbPos.y += top;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <eepp/ui/uitextspan.hpp>
|
||||
#include <eepp/ui/uithememanager.hpp>
|
||||
#include <eepp/ui/uiwidgetcreator.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
#define PUGIXML_HEADER_ONLY
|
||||
#include <pugixml/pugixml.hpp>
|
||||
@@ -94,11 +93,15 @@ bool UIHTMLBody::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
}
|
||||
|
||||
UIRichText* UIRichText::NewHtml() {
|
||||
return UIHTMLHtml::New( "html" );
|
||||
auto* html = UIHTMLHtml::New( "html" );
|
||||
html->setClipType( ClipType::None );
|
||||
return html;
|
||||
}
|
||||
|
||||
UIRichText* UIRichText::NewBody() {
|
||||
return UIHTMLBody::New( "body" );
|
||||
auto* body = UIHTMLBody::New( "body" );
|
||||
body->setClipType( ClipType::None );
|
||||
return body;
|
||||
}
|
||||
|
||||
UIRichText* UIRichText::NewBr() {
|
||||
@@ -261,7 +264,7 @@ bool UIRichText::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return UILayout::applyProperty( attribute );
|
||||
return UIHTMLWidget::applyProperty( attribute );
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -305,7 +308,7 @@ std::string UIRichText::getPropertyString( const PropertyDefinition* propertyDef
|
||||
? "center"
|
||||
: ( getTextAlign() == TEXT_ALIGN_RIGHT ? "right" : "left" );
|
||||
default:
|
||||
return UILayout::getPropertyString( propertyDef, propertyIndex );
|
||||
return UIHTMLWidget::getPropertyString( propertyDef, propertyIndex );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -701,16 +704,6 @@ void UIRichText::updateDefaultSpansStyle() {
|
||||
}
|
||||
}
|
||||
|
||||
void UIRichText::updateLayout() {
|
||||
if ( getLayouter() ) {
|
||||
getLayouter()->updateLayout();
|
||||
} else {
|
||||
UILayout::updateLayout();
|
||||
}
|
||||
|
||||
mDirtyLayout = false;
|
||||
}
|
||||
|
||||
Float UIRichText::getMinIntrinsicWidth() const {
|
||||
if ( mWidthPolicy == SizePolicy::Fixed ) {
|
||||
return getPropertyWidth();
|
||||
|
||||
@@ -82,6 +82,7 @@ EE_MAIN_FUNC int main( int argc, char** argv ) {
|
||||
|
||||
auto urlBar = ui->find( "url_bar" )->asType<UITextInput>();
|
||||
auto mainContainer = ui->find( "html_doc" );
|
||||
mainContainer->asType<UIWidget>()->setClipType( ClipType::None );
|
||||
auto backBtn = ui->find( "backbtn" )->asType<UIPushButton>();
|
||||
auto fwdBtn = ui->find( "fwdbtn" )->asType<UIPushButton>();
|
||||
auto scrollView = ui->find( "html_view" )->asType<UIScrollView>();
|
||||
@@ -98,13 +99,31 @@ EE_MAIN_FUNC int main( int argc, char** argv ) {
|
||||
if ( data.empty() )
|
||||
return;
|
||||
ui->ensureMainThread( [url, data, mainContainer, urlBar, ui, &app, scrollView, useHNDark] {
|
||||
scrollView->removeEventsOfType( Event::OnSizeChange );
|
||||
mainContainer->closeAllChildren();
|
||||
scrollView->getVerticalScrollBar()->setValue( 0 );
|
||||
ui->getStyleSheet().removeAllWithoutMarker( app.getStyleSheetDefaultMarker() );
|
||||
ui->setURIFromURL( url );
|
||||
auto urlStr = url.toString();
|
||||
auto hash = String::hash( urlStr );
|
||||
scrollView->getVerticalScrollBar()->setValue( 0 );
|
||||
ui->loadLayoutFromString( HTMLFormatter::HTMLtoXML( data ), mainContainer, hash );
|
||||
auto htmlNode = ui->findByType( UI_TYPE_HTML_HTML );
|
||||
auto bodyNode = ui->findByType( UI_TYPE_HTML_BODY );
|
||||
if ( htmlNode && bodyNode ) {
|
||||
auto html = htmlNode->asType<UIHTMLHtml>();
|
||||
auto body = bodyNode->asType<UIHTMLBody>();
|
||||
html->setMinHeight( scrollView->getPixelsSize().getHeight() );
|
||||
body->setMinHeight( scrollView->getPixelsSize().getHeight() );
|
||||
scrollView->on( Event::OnSizeChange, [scrollView, html, body]( auto ) {
|
||||
body->setMinHeight( scrollView->getSize().getHeight() );
|
||||
body->setPixelsSize( { html->getPixelsSize().getWidth(), 0 } );
|
||||
html->setMinHeight( scrollView->getSize().getHeight() );
|
||||
html->setPixelsSize( { html->getPixelsSize().getWidth(), 0 } );
|
||||
} );
|
||||
body->on( Event::OnClose, [scrollView]( auto ) {
|
||||
scrollView->removeEventsOfType( Event::OnSizeChange );
|
||||
} );
|
||||
}
|
||||
urlBar->setText( urlStr );
|
||||
|
||||
if ( useHNDark && url.getAuthority() == "news.ycombinator.com" ) {
|
||||
|
||||
@@ -196,3 +196,70 @@ UTEST( UIHTMLWidget, positionOutOfFlow_DoesNotAffectParentSize ) {
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
UTEST( UIHTMLWidget, positionOutOfFlow_PercentageAndMargin ) {
|
||||
init_ui_test();
|
||||
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
|
||||
|
||||
UIHTMLWidget* rootContainer = UIHTMLWidget::New();
|
||||
rootContainer->setParent( sceneNode->getRoot() );
|
||||
rootContainer->setCSSPosition( CSSPosition::Relative );
|
||||
rootContainer->setPixelsSize( 800, 600 );
|
||||
rootContainer->setPixelsPosition( 0, 0 );
|
||||
|
||||
UIHTMLWidget* absoluteChild = UIHTMLWidget::New();
|
||||
absoluteChild->setParent( rootContainer );
|
||||
absoluteChild->setPixelsSize( 400, 400 );
|
||||
|
||||
// Emulate the CSS parsing via applyProperty
|
||||
absoluteChild->applyProperty( StyleSheetProperty( "position", "absolute" ) );
|
||||
absoluteChild->applyProperty( StyleSheetProperty( "left", "50%" ) );
|
||||
absoluteChild->applyProperty( StyleSheetProperty( "margin-left", "-200px" ) );
|
||||
absoluteChild->applyProperty( StyleSheetProperty( "margin-top", "120px" ) );
|
||||
|
||||
sceneNode->updateDirtyLayouts();
|
||||
|
||||
UIWidget* cb = absoluteChild->getContainingBlock();
|
||||
EXPECT_EQ( cb, rootContainer );
|
||||
|
||||
Vector2f worldPos = absoluteChild->convertToWorldSpace( { 0, 0 } );
|
||||
// left should be 50% of 800 (400) plus margin-left (-200) = 200
|
||||
// top should be 0 + margin-top (120) = 120
|
||||
EXPECT_NEAR( 200.f, worldPos.x, 1.f );
|
||||
EXPECT_NEAR( 120.f, worldPos.y, 1.f );
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
#include <eepp/ui/tools/htmlformatter.hpp>
|
||||
#include <eepp/ui/css/stylesheetparser.hpp>
|
||||
|
||||
UTEST( UIHTMLWidget, positionOutOfFlow_ComplexHTML ) {
|
||||
init_ui_test();
|
||||
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
|
||||
|
||||
sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" );
|
||||
std::string html;
|
||||
FileSystem::fileGet( "assets/html/absolute_position.html", html );
|
||||
std::string xml = UI::Tools::HTMLFormatter::HTMLtoXML( html );
|
||||
sceneNode->loadLayoutFromString( xml );
|
||||
|
||||
sceneNode->update( Milliseconds( 16 ) );
|
||||
sceneNode->updateDirtyLayouts();
|
||||
|
||||
UIWidget* mainWidget = sceneNode->getRoot()->find<UIWidget>( "main" );
|
||||
ASSERT_TRUE( mainWidget != nullptr );
|
||||
EXPECT_GT( mainWidget->getPixelsSize().getHeight(), 0.f ); // This is not standard in HTML!
|
||||
|
||||
Vector2f worldPos = mainWidget->convertToWorldSpace( { 0, 0 } );
|
||||
// Window size is 1024x650
|
||||
// left: 50% of 1024 = 512
|
||||
// margin-left: -200px
|
||||
// 512 - 200 = 312
|
||||
EXPECT_NEAR( 312.f, worldPos.x, 1.f );
|
||||
|
||||
// top should just be margin-top
|
||||
EXPECT_NEAR( 120.f, worldPos.y, 1.f );
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user