ui: improve HTML inline layout and old Reddit rendering

- implement per-side CSS border styles and patterned rendering
  - preserve rectangular rasterization for thin dotted borders
  - separate CSS and native-widget border defaults
  - correct inline atomic-box and vertical-align baseline layout
  - collapse adjacent block margins in rich-text flow
  - preserve forced line breaks and formatting margins
  - size HTML text inputs from font ascent and descent
  - expose replaced-control text baselines to inline layout
  - enforce single-owner painting for inline backgrounds and borders
  - enable and update the old Reddit visual regression golden
  - add generic layout, border, and form-control regression tests
  - document spec-first HTML work and project build conventions
This commit is contained in:
Martín Lucas Golini
2026-08-02 17:48:53 -03:00
parent 2df0861d76
commit 48b7e7c21f
32 changed files with 1344 additions and 1455 deletions
+9 -2
View File
@@ -191,10 +191,11 @@ class EE_API RichText : public Drawable {
InlineClear clearType = InlineClear::None, Float baseline = -1.f,
const BaselineAlignValue& baselineAlign = {}, InlineSource source = {},
bool isBlock = false, bool isBlockFormattingContext = false,
std::shared_ptr<const std::vector<FloatExclusion>> propagatedFloats = {} );
std::shared_ptr<const std::vector<FloatExclusion>> propagatedFloats = {},
const Rectf& margin = Rectf::Zero );
/** @brief Adds a virtual line break that is not associated with a DOM text character. */
void addLineBreak();
void addLineBreak( bool forceEmptyLine = false, Float lineHeight = 0.f );
virtual void draw( const Float& X, const Float& Y, const Vector2f& scale = Vector2f::One,
const Float& rotation = 0, BlendMode effect = BlendMode::Alpha(),
@@ -227,6 +228,7 @@ class EE_API RichText : public Drawable {
std::shared_ptr<Drawable> drawable;
Rectf margin;
Rectf padding;
Rectf formattingMargin;
Float lineHeight{ 0 };
BaselineAlignValue baselineAlign;
bool suppressBackground{ false };
@@ -234,6 +236,7 @@ class EE_API RichText : public Drawable {
InlineFloat floatType{ InlineFloat::None };
InlineClear clearType{ InlineClear::None };
bool isLineBreak{ false };
bool forceEmptyLine{ false };
bool isBlock{ false };
bool isBlockFormattingContext{ false };
std::shared_ptr<const std::vector<FloatExclusion>> propagatedFloats;
@@ -248,6 +251,7 @@ class EE_API RichText : public Drawable {
/** @brief Structure representing a rendered paragraph (line). */
struct RenderParagraph {
std::vector<RenderSpan> spans;
bool forcedEmptyLine{ false };
Float y{ 0 };
Float height{ 0 };
Float maxAscent{ 0 };
@@ -347,10 +351,12 @@ class EE_API RichText : public Drawable {
InlineSource source;
std::shared_ptr<Drawable> drawable;
Sizef size;
Rectf formattingMargin;
Float baseline{ 0 };
InlineFloat floatType{ InlineFloat::None };
InlineClear clearType{ InlineClear::None };
bool isLineBreak{ false };
bool forceEmptyLine{ false };
bool isBlock{ false };
bool isBlockFormattingContext{ false };
std::shared_ptr<const std::vector<FloatExclusion>> propagatedFloats;
@@ -388,6 +394,7 @@ class EE_API RichText : public Drawable {
size_t lineIndex{ 0 };
Rectf bounds;
Rectf paintBounds;
Rectf formattingMargin;
Int64 startCharIndex{ 0 };
Int64 endCharIndex{ 0 };
std::shared_ptr<Text> text;
+7
View File
@@ -17,10 +17,13 @@ class UINode;
enum class BorderType : Uint32 { Inside, Outside, Outline };
enum class BorderStyle : Uint32 { None, Hidden, Dotted, Dashed, Solid };
struct EE_API Border {
int width = 0;
Color color;
Color realColor;
BorderStyle style{ BorderStyle::Solid };
};
struct EE_API BorderRadiuses {
@@ -41,6 +44,10 @@ struct EE_API Borders {
static BorderType toBorderType( const std::string& borderType );
static std::string fromBorderStyle( BorderStyle borderStyle );
static BorderStyle toBorderStyle( const std::string& borderStyle );
static Sizef radiusFromString( const UINode* node, const std::string& val );
/** Resolves overlapping corner curves with the single proportional scale factor required by
@@ -207,6 +207,10 @@ enum class PropertyId : Uint32 {
BorderRightWidth = String::hash( "border-right-width" ),
BorderTopWidth = String::hash( "border-top-width" ),
BorderBottomWidth = String::hash( "border-bottom-width" ),
BorderLeftStyle = String::hash( "border-left-style" ),
BorderRightStyle = String::hash( "border-right-style" ),
BorderTopStyle = String::hash( "border-top-style" ),
BorderBottomStyle = String::hash( "border-bottom-style" ),
BorderTopLeftRadius = String::hash( "border-top-left-radius" ),
BorderTopRightRadius = String::hash( "border-top-right-radius" ),
BorderBottomLeftRadius = String::hash( "border-bottom-left-radius" ),
+8
View File
@@ -70,6 +70,14 @@ class EE_API UIBorderDrawable : public Drawable {
void setBottomWidth( const std::string& bottomWidth );
void setLeftStyle( const std::string& style );
void setRightStyle( const std::string& style );
void setTopStyle( const std::string& style );
void setBottomStyle( const std::string& style );
void setTopLeftRadius( const std::string& radius );
void setTopRightRadius( const std::string& radius );
+3
View File
@@ -29,6 +29,8 @@ class EE_API UIHTMLInput : public UIHTMLWidget {
virtual void updateLayout();
Float getReplacedElementBaseline() const;
const std::string& getInputType() const;
void setInputType( const std::string& type );
@@ -44,6 +46,7 @@ class EE_API UIHTMLInput : public UIHTMLWidget {
String mValue;
bool mChecked{ false };
bool mSyncingGeometry{ false };
bool mHasSpecifiedHeight{ false };
bool mHiddenByType{ false };
bool mVisibleBeforeHidden{ true };
bool mEnabledBeforeHidden{ true };
+4
View File
@@ -28,6 +28,10 @@ class EE_API UIHTMLTextInput : public UITextInput {
virtual void onAutoSize();
Float getIntrinsicContentHeight() const;
Float getTextBaseline() const;
Uint32 getHtmlSize() const;
void setHtmlSize( Uint32 size );
+4
View File
@@ -165,6 +165,10 @@ class EE_API UITextSpan : public UIRichText {
virtual void onFontStyleChanged();
virtual void drawBackground();
virtual void drawBorder();
virtual Uint32 onMessage( const NodeMessage* Msg );
};