Fix CSS auto margins for replaced HTML elements

Model HTML images as replaced UIHTMLWidget elements with independent loading, sizing, and rendering behavior.

Resolve auto margins according to each element's formatting context, including normal-flow blocks, positioned elements, flex items, and grid items. Preserve intrinsic sizing for block-level images with width:auto and constrain them through CSS min/max dimensions.

Correct absolute-positioned containing-block coordinates and rich-text handling of atomic inline elements. Add regression coverage for centered images, deferred image loading, grid margins, positioned elements, and document flow after image loading.

Document the remaining work required for dynamically changing non-atomic inline elements.

Avoid saving eeiv configuration during destruction (fixes window state saving).
This commit is contained in:
Martín Lucas Golini
2026-08-04 13:32:22 -03:00
parent 0de33c574d
commit 445aac792d
17 changed files with 1144 additions and 846 deletions
+73 -2
View File
@@ -1,11 +1,15 @@
#ifndef EE_UI_UIHTMLIMAGE_HPP
#define EE_UI_UIHTMLIMAGE_HPP
#include <eepp/ui/uiimage.hpp>
#include <atomic>
#include <eepp/network/uri.hpp>
#include <eepp/ui/uihtmlwidget.hpp>
#include <memory>
namespace EE { namespace UI {
class EE_API UIHTMLImage : public UIImage {
/** HTML replaced image element. CSS box/layout behavior comes from UIHTMLWidget. */
class EE_API UIHTMLImage : public UIHTMLWidget {
public:
static UIHTMLImage* New();
@@ -19,13 +23,80 @@ class EE_API UIHTMLImage : public UIImage {
virtual void draw();
virtual void setAlpha( const Float& alpha );
const DrawablePtr& getDrawable() const;
UIHTMLImage* setDrawable( DrawablePtr drawable );
UIHTMLImage* setDrawable( TexturePtr texture );
const Color& getColor() const;
UIHTMLImage* setColor( const Color& col );
const Vector2f& getAlignOffset() const;
virtual bool applyProperty( const StyleSheetProperty& attribute );
virtual Float getMinIntrinsicWidth() const;
virtual Float getMaxIntrinsicWidth() const;
virtual std::string getPropertyString( const PropertyDefinition* propertyDef,
const Uint32& propertyIndex = 0 ) const;
virtual void scheduledUpdate( const Time& time );
virtual void updateLayout();
virtual std::vector<PropertyId> getPropertiesImplemented() const;
const UIScaleType& getScaleType() const;
UIHTMLImage* setScaleType( const UIScaleType& scaleType );
const std::string& getAlt() const;
UIHTMLImage* setAlt( const std::string& alt );
virtual bool isInline() const;
protected:
UIHTMLImage();
virtual void onSizeChange();
virtual void onSizePolicyChange();
virtual void onAlignChange();
virtual void onParentSizeChange( const Vector2f& sizeChange );
virtual void onDisplayChange();
void autoSizeImage();
void calcDestSize();
void clearDrawable();
void onDrawableResourceChange();
bool loadFileDrawable( const Network::URI& uri );
void loadRemoteDrawable( const Network::URI& uri );
UIScaleType mScaleType{ UIScaleType::Expand };
DrawablePtr mDrawable;
Color mColor;
Vector2f mAlignOffset;
Vector2f mDestSize;
DrawableResourceConnection mResourceChangeConnection;
Uint32 mSpriteChangeCb{ 0 };
bool mDeferLoad{ false };
std::shared_ptr<std::atomic<bool>> mAsyncImageAlive;
Uint64 mRemoteImageLoadId{ 0 };
std::string mAlt;
};
+23 -5
View File
@@ -13,6 +13,23 @@ namespace EE { namespace UI {
class UILayouter;
enum class CSSFormattingRole : Uint8 {
Inline,
InlineBlock,
NormalFlowBlock,
Float,
Absolute,
Fixed,
FlexItem,
GridItem,
Table
};
struct CSSUsedMargins {
Rectf value;
Uint8 autoSides{ 0 };
};
struct UIHTMLWidgetFlexState {
CSSFlexDirection direction{ CSSFlexDirection::Row };
CSSFlexWrap wrap{ CSSFlexWrap::NoWrap };
@@ -98,12 +115,13 @@ class EE_API UIHTMLWidget : public UILayout {
void setBoxSizing( CSSBoxSizing boxSizing );
Rectf getNormalFlowLayoutPixelsMargin() const;
CSSFormattingRole getFormattingRole() const;
/** Returns the used CSS margin for a child participating in a block formatting context.
* Horizontal auto margins are resolved only for normal-flow block-level boxes. For
* inline-level, floated, and out-of-flow boxes every auto margin has a used value of zero. */
static Rectf getFormattingContextLayoutPixelsMargin( UIWidget* widget );
/** Returns stack-local used margins for the current formatting role. This never mutates the
* computed/resolved margins stored by UIWidget. Flex, grid, and positioned layout retain their
* module-specific auto-margin distribution; callers in those contexts receive zero for auto
* sides until the owning layouter solves them. */
CSSUsedMargins resolveUsedMargins() const;
const CSSBaselineAlignValue& getBaselineAlign() const { return mBaselineAlign; }