mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
More fixes.
This commit is contained in:
@@ -16,7 +16,7 @@ The target is not a Reddit-specific hack. Each fix must move the HTML/CSS engine
|
||||
- Reference image: `bin/unit_tests/assets/html/reddit_old_thread_reference_image.png`
|
||||
- Smoke test: `UIHTML.redditOldThreadWebViewSmoke`
|
||||
|
||||
The smoke test loads `assets/ui/breeze.css` first and then opens the old Reddit fixture through `UIWebView`, so it follows the same path as the browser-like local workflow. It asserts the important page regions exist and emits `assets/html/eepp-reddit-old-thread-current.webp` through the existing image comparison helper.
|
||||
The smoke test opens the old Reddit fixture through `UIWebView` without loading the app theme. Browser-like HTML defaults are supplied by the HTML base defaults stylesheet when HTML widgets enter the node tree, so the test exercises the same default-style path used by real HTML content. It asserts the important page regions exist and emits `assets/html/eepp-reddit-old-thread-current.webp` through the existing image comparison helper.
|
||||
|
||||
The smoke test is opt-in because the full fixture is slow in ASAN:
|
||||
|
||||
@@ -38,7 +38,10 @@ Current progress:
|
||||
- `BlockLayouter` now forwards inherited float exclusions through normal non-floating blocks and preserves the parent-computed used width for BFC match-parent children next to floats.
|
||||
- The fixture now keeps the main `.entry` selftext box to the left of `.side` (`side.x=719`, `entry.x=44`, `entry.width=670` in the current smoke run).
|
||||
- External float exclusions are filtered for fixed-width descendants whose content box is entirely outside the float's horizontal range. This keeps the comment form textarea from being pushed below the sidebar while still letting match-parent content compute a float-constrained used width.
|
||||
- Remaining visible blockers: header/topbar layout is still badly overlapped, the vote arrow sprites/centering are incomplete, the comment form spacing is too large, and the footer/comments vertical spacing still diverges from Chrome.
|
||||
- The smoke test no longer loads `breeze.css`; HTML defaults now come from automatic HTML base-default injection.
|
||||
- Legacy `<strike>` is registered as an HTML phrasing element and receives default `line-through` styling, removing a missing-element warning from the fixture.
|
||||
- Horizontal `auto` margins are recomputed at RichText/block layout read points, and the old Reddit vote arrow is centered inside `.midcol` (`arrow.x=17`, `midcol.x=15`, `arrow.width=15`, `midcol.width=19` in the current smoke run).
|
||||
- Remaining visible blockers: header/topbar layout is still badly overlapped, vote arrow sprite painting/background positioning still needs verification, the comment form spacing is too large, and the footer/comments vertical spacing still diverges from Chrome.
|
||||
|
||||
## Reference Layout Invariants
|
||||
|
||||
@@ -133,7 +136,7 @@ Reduced tests:
|
||||
|
||||
### 4. CSS Display Defaults And Element Creation
|
||||
|
||||
The user currently loads `breeze.css` because not all base HTML element display/state defaults have moved into HTML element creation. This should be reduced.
|
||||
The old `breeze.css` dependency has been removed from the old Reddit smoke test. HTML base defaults are now injected automatically when HTML content is attached to a scene.
|
||||
|
||||
Old Reddit relies on browser defaults for:
|
||||
|
||||
@@ -142,7 +145,7 @@ Old Reddit relies on browser defaults for:
|
||||
- replaced/form controls such as `input`, `textarea`
|
||||
- hidden inputs and `display:none` nodes
|
||||
|
||||
Plan:
|
||||
Remaining plan:
|
||||
|
||||
- Audit `UIWidgetCreator` and HTML element constructors for browser-like default display.
|
||||
- Keep theme-specific visual styling in CSS, but move semantic defaults into element creation.
|
||||
@@ -329,12 +332,12 @@ Exit criteria:
|
||||
|
||||
### Phase 4: Defaults And Form Controls
|
||||
|
||||
Move semantic HTML defaults out of theme dependency where practical, keeping `breeze.css` as a visual theme rather than a behavior crutch.
|
||||
Keep semantic HTML defaults independent from the app theme. `breeze.css` should remain a visual UI theme, not a behavior crutch for HTML content.
|
||||
|
||||
Exit criteria:
|
||||
|
||||
- Minimal HTML default-display tests pass without loading `breeze.css`.
|
||||
- The old Reddit smoke test still passes with breeze loaded.
|
||||
- The old Reddit smoke test passes without loading `breeze.css`.
|
||||
- Form controls in the comment box and sidebar match expected broad geometry.
|
||||
|
||||
### Phase 5: Full-Page Visual Gate
|
||||
|
||||
@@ -328,6 +328,10 @@ class EE_API UIWidget : public UINode {
|
||||
|
||||
bool hasLayoutMarginBottomAuto() const;
|
||||
|
||||
bool hasLayoutMarginAuto() const;
|
||||
|
||||
UIWidget* updateLayoutMarginAuto();
|
||||
|
||||
/**
|
||||
* @brief Sets the layout margin for all sides in pixels.
|
||||
*
|
||||
|
||||
@@ -535,6 +535,8 @@ void BlockLayouter::positionRichTextChildren( Graphics::RichText* rt ) {
|
||||
curCharIdx += 1;
|
||||
Rectf atomicBounds( maxF, maxF, lowF, lowF );
|
||||
if ( getAtomicWidgetFragmentBounds( widget, atomicBounds ) ) {
|
||||
if ( widget->hasLayoutMarginAuto() )
|
||||
widget->updateLayoutMarginAuto();
|
||||
Rectf margin =
|
||||
widget->isType( UI_TYPE_HTML_WIDGET )
|
||||
? widget->asType<UIHTMLWidget>()->getNormalFlowLayoutPixelsMargin()
|
||||
@@ -574,6 +576,8 @@ void BlockLayouter::positionRichTextChildren( Graphics::RichText* rt ) {
|
||||
|
||||
size_t lineIdx = currentSpan > 0 ? currentLine : currentLine - 1;
|
||||
Float lineY = lines[lineIdx].y;
|
||||
if ( widget->hasLayoutMarginAuto() )
|
||||
widget->updateLayoutMarginAuto();
|
||||
Rectf margin =
|
||||
widget->isType( UI_TYPE_HTML_WIDGET )
|
||||
? widget->asType<UIHTMLWidget>()->getNormalFlowLayoutPixelsMargin()
|
||||
|
||||
@@ -1307,6 +1307,8 @@ void UIRichText::rebuildRichText( UILayout* container, RichText& richText, Intri
|
||||
"\n", widget->asType<UILineBreak>()->getRichText().getFontStyleConfig() );
|
||||
lastSpanEndsWithSpace = false;
|
||||
} else {
|
||||
if ( widget->hasLayoutMarginAuto() )
|
||||
widget->updateLayoutMarginAuto();
|
||||
Rectf margin =
|
||||
widget->isType( UI_TYPE_HTML_WIDGET )
|
||||
? widget->asType<UIHTMLWidget>()->getNormalFlowLayoutPixelsMargin()
|
||||
|
||||
@@ -188,7 +188,7 @@ UIWidget* UIWidget::setLayoutMarginTopAuto( bool isAuto ) {
|
||||
}
|
||||
|
||||
UIWidget* UIWidget::setLayoutMarginBottomAuto( bool isAuto ) {
|
||||
return setLayoutMarginAuto( MarginAuto::Top, isAuto );
|
||||
return setLayoutMarginAuto( MarginAuto::Bottom, isAuto );
|
||||
}
|
||||
|
||||
UIWidget* UIWidget::setLayoutMarginAuto( bool left, bool right, bool top, bool bottom ) {
|
||||
@@ -215,6 +215,15 @@ bool UIWidget::hasLayoutMarginBottomAuto() const {
|
||||
return mMarginAuto & MarginAuto::Bottom;
|
||||
}
|
||||
|
||||
bool UIWidget::hasLayoutMarginAuto() const {
|
||||
return mMarginAuto != 0;
|
||||
}
|
||||
|
||||
UIWidget* UIWidget::updateLayoutMarginAuto() {
|
||||
calculateAutoMargin();
|
||||
return this;
|
||||
}
|
||||
|
||||
UIWidget* UIWidget::setLayoutPixelsMargin( const Rectf& margin ) {
|
||||
if ( mLayoutMargin != margin ) {
|
||||
mLayoutMarginPx = margin;
|
||||
|
||||
@@ -88,7 +88,7 @@ b, strong { font-weight: bold; }
|
||||
i, em, cite { font-style: italic; }
|
||||
small { font-size: smaller; }
|
||||
u, ins { text-decoration: underline; }
|
||||
s, del { text-decoration: line-through; }
|
||||
s, strike, del { text-decoration: line-through; }
|
||||
code, kbd { font-family: monospace; }
|
||||
sub, sup { font-size: smaller; }
|
||||
mark { background-color: yellow; }
|
||||
@@ -182,7 +182,6 @@ RadioButton::active {
|
||||
}
|
||||
|
||||
)css";
|
||||
|
||||
}
|
||||
|
||||
void UIWidgetCreator::createBaseWidgetList() {
|
||||
@@ -273,6 +272,7 @@ void UIWidgetCreator::createBaseWidgetList() {
|
||||
registeredWidget["u"] = UITextSpan::NewUnderline;
|
||||
registeredWidget["ins"] = UITextSpan::NewUnderline;
|
||||
registeredWidget["s"] = UITextSpan::NewStrikethrough;
|
||||
registeredWidget["strike"] = [] { return UITextSpan::NewWithTag( "strike" ); };
|
||||
registeredWidget["del"] = UITextSpan::NewStrikethrough;
|
||||
registeredWidget["font"] = UITextSpan::NewFont;
|
||||
registeredWidget["code"] = UITextSpan::NewCode;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
using namespace EE;
|
||||
using namespace EE::UI;
|
||||
using namespace EE::UI::Tools;
|
||||
using namespace EE::Window;
|
||||
using namespace EE::Graphics;
|
||||
|
||||
@@ -276,6 +277,34 @@ UTEST( UIHTMLFloat, leftFloatOverflowHiddenBlockFormattingContextSitsBesideFloat
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
UTEST( UIHTMLFloat, autoHorizontalMarginsCenterBlockInsideFloat ) {
|
||||
init_float_test();
|
||||
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
|
||||
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( R"html(
|
||||
<body style="margin:0">
|
||||
<div id="midcol" style="float:left;width:19px;height:50px;overflow:hidden">
|
||||
<div id="arrow" style="display:block;width:15px;height:14px;margin-left:auto;margin-right:auto"></div>
|
||||
</div>
|
||||
</body>
|
||||
)html" ) );
|
||||
sceneNode->updateDirtyLayouts();
|
||||
|
||||
auto* midcol = sceneNode->find<UIWidget>( "midcol" );
|
||||
auto* arrow = sceneNode->find<UIWidget>( "arrow" );
|
||||
ASSERT_TRUE( midcol != nullptr );
|
||||
ASSERT_TRUE( arrow != nullptr );
|
||||
|
||||
Vector2f midcolPos = midcol->convertToWorldSpace( { 0, 0 } );
|
||||
Vector2f arrowPos = arrow->convertToWorldSpace( { 0, 0 } );
|
||||
Float midcolCenter = midcolPos.x + midcol->getPixelsSize().getWidth() / 2.f;
|
||||
Float arrowCenter = arrowPos.x + arrow->getPixelsSize().getWidth() / 2.f;
|
||||
|
||||
EXPECT_NEAR( midcolCenter, arrowCenter, 1.f );
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
UTEST( UIHTMLFloat, rightFloatConstrainsTextInsideFollowingNormalBlock ) {
|
||||
init_float_test();
|
||||
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
|
||||
|
||||
@@ -189,10 +189,7 @@ UTEST( UIHTML, redditOldThreadWebViewSmoke ) {
|
||||
UI::UISceneNode* sceneNode = UI::UISceneNode::New();
|
||||
SceneManager::instance()->add( sceneNode );
|
||||
UI::UIThemeManager* themeManager = sceneNode->getUIThemeManager();
|
||||
UITheme* theme = UITheme::load( "breeze", "breeze", "", font, "assets/ui/breeze.css" );
|
||||
ASSERT_TRUE( theme != nullptr );
|
||||
sceneNode->setStyleSheet( theme->getStyleSheet() );
|
||||
themeManager->setDefaultFont( font )->setDefaultTheme( theme )->add( theme );
|
||||
themeManager->setDefaultFont( font );
|
||||
|
||||
UIWebView* webView = UIWebView::New();
|
||||
webView->setParent( sceneNode->getRoot() );
|
||||
@@ -230,6 +227,7 @@ UTEST( UIHTML, redditOldThreadWebViewSmoke ) {
|
||||
Vector2f contentPos = content->convertToWorldSpace( { 0, 0 } );
|
||||
Vector2f midcolPos = midcol->asType<UIWidget>()->convertToWorldSpace( { 0, 0 } );
|
||||
Vector2f entryPos = entry->asType<UIWidget>()->convertToWorldSpace( { 0, 0 } );
|
||||
Vector2f arrowPos = arrow->asType<UIWidget>()->convertToWorldSpace( { 0, 0 } );
|
||||
|
||||
std::cerr << "old reddit rects: "
|
||||
<< "side=(" << sidePos.x << "," << sidePos.y << " "
|
||||
@@ -243,7 +241,16 @@ UTEST( UIHTML, redditOldThreadWebViewSmoke ) {
|
||||
<< midcol->asType<UIWidget>()->getPixelsSize().getHeight() << ") "
|
||||
<< "entry=(" << entryPos.x << "," << entryPos.y << " "
|
||||
<< entry->asType<UIWidget>()->getPixelsSize().getWidth() << "x"
|
||||
<< entry->asType<UIWidget>()->getPixelsSize().getHeight() << ")" << std::endl;
|
||||
<< entry->asType<UIWidget>()->getPixelsSize().getHeight() << ") "
|
||||
<< "arrow=(" << arrowPos.x << "," << arrowPos.y << " "
|
||||
<< arrow->asType<UIWidget>()->getPixelsSize().getWidth() << "x"
|
||||
<< arrow->asType<UIWidget>()->getPixelsSize().getHeight() << ")" << std::endl;
|
||||
|
||||
const Float midcolCenter =
|
||||
midcolPos.x + midcol->asType<UIWidget>()->getPixelsSize().getWidth() / 2.f;
|
||||
const Float arrowCenter =
|
||||
arrowPos.x + arrow->asType<UIWidget>()->getPixelsSize().getWidth() / 2.f;
|
||||
EXPECT_NEAR( midcolCenter, arrowCenter, 1.f );
|
||||
|
||||
if ( !FileSystem::fileExists( "output" ) )
|
||||
FileSystem::makeDir( "output" );
|
||||
@@ -253,6 +260,21 @@ UTEST( UIHTML, redditOldThreadWebViewSmoke ) {
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
UTEST( UIHTML, StrikeElementUsesDefaultLineThrough ) {
|
||||
init_ui_test();
|
||||
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
|
||||
|
||||
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML(
|
||||
R"html(<body><p><strike id="strike">old</strike></p></body>)html" ) );
|
||||
SceneManager::instance()->update();
|
||||
|
||||
auto* strike = sceneNode->getRoot()->find( "strike" )->asType<UITextSpan>();
|
||||
ASSERT_TRUE( strike != nullptr );
|
||||
EXPECT_TRUE( 0 != ( strike->getTextDecoration() & Text::StrikeThrough ) );
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
UTEST( UIRichText, anchorMargins ) {
|
||||
auto win = Engine::instance()->createWindow(
|
||||
WindowSettings( 800, 600, "Anchor Margins Test", WindowStyle::Default,
|
||||
@@ -1301,6 +1323,20 @@ UTEST( UILayout, marginAuto ) {
|
||||
EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Left, expectedMarginX, 1.f );
|
||||
EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Right, expectedMarginX, 1.f );
|
||||
|
||||
childWidget->setLayoutMarginAuto( false, false, true, true );
|
||||
sceneNode->updateDirtyLayouts();
|
||||
|
||||
Float expectedMarginY =
|
||||
( contWidget->getPixelsSize().getHeight() - childWidget->getPixelsSize().getHeight() ) /
|
||||
2.f;
|
||||
|
||||
EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Top, expectedMarginY, 1.f );
|
||||
EXPECT_NEAR( childWidget->getLayoutPixelsMargin().Bottom, expectedMarginY, 1.f );
|
||||
EXPECT_FALSE( childWidget->hasLayoutMarginLeftAuto() );
|
||||
EXPECT_FALSE( childWidget->hasLayoutMarginRightAuto() );
|
||||
EXPECT_TRUE( childWidget->hasLayoutMarginTopAuto() );
|
||||
EXPECT_TRUE( childWidget->hasLayoutMarginBottomAuto() );
|
||||
|
||||
Engine::destroySingleton();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user