Browse Source

dom: added layout used lengths, compute min/max content sizes

mattias 10 months ago
parent
commit
854c1132c1

+ 2 - 2
demo/Slider/DemoSlider.pas

@@ -73,8 +73,8 @@ type
        +'  background-color: #fff;'+LineEnding
        +'  border-radius: 50%;'+LineEnding
        +'  cursor: pointer;'+LineEnding
-       +'  top: -.3em;'+LineEnding
-       +'  margin-left: -.6em;'+LineEnding
+       +'  top: -4px;'+LineEnding
+       +'  margin-left: -6px;'+LineEnding
        +'}'+LineEnding;
     var
     CaptionDiv: TDiv;

+ 3 - 0
src/base/fcl-css/fpcssresparser.pas

@@ -300,6 +300,9 @@ const
 
 
 type
+
+  { TCSSRegistryNamedItem }
+
   TCSSRegistryNamedItem = class
   public
     Name: TCSSString; // case sensitive

+ 1 - 1
src/base/fresnel.classes.pas

@@ -716,7 +716,7 @@ end;
 
 function TFresnelRect.ToString: string;
 begin
-  Result:=Format('Rect[%g,%g,r=%g,b=%g]',[Left,Top,Right,Bottom]);
+  Result:=Format('[%g,%g,r=%g,b=%g]',[Left,Top,Right,Bottom]);
 end;
 
 function TFresnelRect.GetHeight: TFresnelLength;

+ 75 - 64
src/base/fresnel.controls.pas

@@ -53,18 +53,11 @@ type
     class function GetCSSTypeStyle: TCSSString; override;
   end;
 
-  { TReplacedElement - base class for elements with special content and no child elements, e.g. label, video }
-
-  TReplacedElement = class(TFresnelElement)
-  public
-    procedure GetMinMaxPreferred(out MinWidth, MinHeight, PreferredWidth, PreferredHeight, MaxWidth, MaxHeight: TFresnelLength); virtual;
-    function AcceptChildrenAtDesignTime: boolean; override;
-  end;
-
   TFresnelLabelState = (
     flsMinCaptionValid,
+    flsMaxWidthValid,
     flsMinWidthValid,
-    flsSizeValid
+    flsLastSizeValid
     );
   TFresnelLabelStates = set of TFresnelLabelState;
 
@@ -76,18 +69,19 @@ type
   protected
     FLabelStates: TFresnelLabelStates;
     FMinCaption: String; // Caption with linebreak after each word
-    FMinWidth: TFresnelLength; // width of longest word of Caption
+    FMaxWidthSize: TFresnelPoint; // size for biggest width, no extra line breaks
+    FMinWidthSize: TFresnelPoint; // size for width of longest word
     FOldFont: IFresnelFont;
-    FSize: TFresnelPoint; // width+height of Caption
+    FLastMax: TFresnelPoint;
+    FLastSize: TFresnelPoint; // result for last call with fixed max width or height
     procedure ComputeMinCaption; virtual;
     function GetFont: IFresnelFont; override;
     procedure SetCaption(const AValue: TFresnelCaption); virtual;
     procedure SetName(const NewName: TComponentName); override;
     procedure DoRender(aRenderer: IFresnelRenderer); override;
   public
-    function GetMinWidthIntrinsicContentBox: TFresnelLength; override;
-    function GetPreferredContentBox_MaxWidth(MaxWidth: TFresnelLength): TFresnelPoint;
-      override;
+    function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength=NaN;
+      aMaxHeight: TFresnelLength=NaN): TFresnelPoint; override;
     property Caption: TFresnelCaption read FCaption write SetCaption;
   end;
 
@@ -163,7 +157,7 @@ type
 
   { TCustomImage }
 
-  TCustomImage = class(TFresnelElement)
+  TCustomImage = class(TReplacedElement)
   private
     FImage: TImageData;
     procedure SetImage(AValue: TImageData);
@@ -172,6 +166,8 @@ type
   Public
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
+    function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength=NaN;
+      aMaxHeight: TFresnelLength=NaN): TFresnelPoint; override;
     Property Image : TImageData Read FImage Write SetImage;
   end;
 
@@ -191,24 +187,6 @@ type
 
 implementation
 
-{ TReplacedElement }
-
-procedure TReplacedElement.GetMinMaxPreferred(out MinWidth, MinHeight,
-  PreferredWidth, PreferredHeight, MaxWidth, MaxHeight: TFresnelLength);
-begin
-  MinWidth:=NaN;
-  MinHeight:=NaN;
-  PreferredWidth:=NaN;
-  PreferredHeight:=NaN;
-  MaxWidth:=NaN;
-  MaxHeight:=NaN;
-end;
-
-function TReplacedElement.AcceptChildrenAtDesignTime: boolean;
-begin
-  Result:=false;
-end;
-
 { TSpan }
 
 class constructor TSpan.InitFresnelSpanClass;
@@ -272,7 +250,7 @@ end;
 
 class function TBody.GetCSSTypeStyle: TCSSString;
 begin
-  Result:='body { background-color: white; color: black; display: block; position: static; }';
+  Result:='body { background-color: white; color: black; display: block; position: static; margin: 8px; }';
 end;
 
 
@@ -383,6 +361,27 @@ begin
   inherited Destroy;
 end;
 
+function TCustomImage.GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength;
+  aMaxHeight: TFresnelLength): TFresnelPoint;
+begin
+  if FImage=nil then
+    exit(Default(TFresnelPoint));
+  case aMode of
+  flmMinWidth,flmMinHeight:
+    exit(Default(TFresnelPoint));
+  flmMaxWidth,flmMaxHeight:
+    begin
+      Result.X:=FImage.Width;
+      Result.Y:=FImage.Height;
+      if (Result.X=0) or (Result.Y=0) then exit;
+      if (not IsNan(aMaxWidth)) and (Result.X>aMaxWidth) and (aMaxWidth>=0) then
+        Result.Y:=Result.Y*(aMaxWidth/Result.X);
+      if (not IsNan(aMaxHeight)) and (Result.Y>aMaxHeight) and (aMaxHeight>=0) then
+        Result.X:=Result.X*(aMaxHeight/Result.Y);
+    end;
+  end;
+end;
+
 { TImage }
 
 class constructor TImage.InitFresnelImageClass;
@@ -457,7 +456,7 @@ begin
   Result:=inherited GetFont;
   if Result<>FOldFont then
   begin
-    FLabelStates:=FLabelStates-[flsMinCaptionValid,flsMinWidthValid,flsSizeValid];
+    FLabelStates:=FLabelStates-[flsMinCaptionValid,flsMinWidthValid,flsMaxWidthValid,flsLastSizeValid];
     FOldFont:=Result;
   end;
 end;
@@ -467,7 +466,7 @@ begin
   if FCaption=AValue then Exit;
   FCaption:=AValue;
   FMinCaption:='';
-  FLabelStates:=FLabelStates-[flsMinCaptionValid,flsMinWidthValid,flsSizeValid];
+  FLabelStates:=FLabelStates-[flsMinCaptionValid,flsMinWidthValid,flsMaxWidthValid,flsLastSizeValid];
   DomChanged;
 end;
 
@@ -508,38 +507,50 @@ begin
     aRenderer.ClearTextShadows;
 end;
 
-function TCustomLabel.GetMinWidthIntrinsicContentBox: TFresnelLength;
-var
-  p: TFresnelPoint;
+function TCustomLabel.GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength;
+  aMaxHeight: TFresnelLength): TFresnelPoint;
 begin
   GetFont;
-  if not (flsMinWidthValid in FLabelStates) then
-  begin
-    if not (flsMinCaptionValid in FLabelStates) then
-      ComputeMinCaption;
-    p:=Font.TextSize(FMinCaption);
-    FMinCaption:='';
-    Exclude(FLabelStates,flsMinCaptionValid);
-    FMinWidth:=p.X;
-    Include(FLabelStates,flsMinWidthValid);
-  end;
-  Result:=FMinWidth;
-end;
 
-function TCustomLabel.GetPreferredContentBox_MaxWidth(MaxWidth: TFresnelLength
-  ): TFresnelPoint;
-begin
-  GetFont;
-  if not (flsSizeValid in FLabelStates) then
-  begin
-    FSize:=Font.TextSize(FCaption);
-    Include(FLabelStates,flsSizeValid);
+  // todo writing-mode
+  if IsNan(aMaxHeight) then ;
+
+  case aMode of
+  flmMinWidth:
+    begin
+      // size when using the width of the longest word
+      if not (flsMinWidthValid in FLabelStates) then
+      begin
+        if not (flsMinCaptionValid in FLabelStates) then
+          ComputeMinCaption;
+        FMinWidthSize:=Font.TextSize(FMinCaption);
+        if FMinCaption<>FCaption then
+          FMinWidthSize:=Font.TextSizeMaxWidth(FCaption,FMinWidthSize.X);
+        Include(FLabelStates,flsMinWidthValid);
+      end;
+      Result:=FMinWidthSize;
+    end;
+  flmMaxHeight,flmMaxWidth,flmMinHeight:
+    begin
+      if not (flsMaxWidthValid in FLabelStates) then
+      begin
+        FMaxWidthSize:=Font.TextSize(FCaption);
+        Include(FLabelStates,flsMaxWidthValid);
+      end;
+
+      if IsNan(aMaxWidth) or (aMaxWidth<0) or (FMaxWidthSize.X<aMaxWidth) then
+        Result:=FMaxWidthSize
+      else begin
+        if (not (flsLastSizeValid in FLabelStates)) or IsNan(FLastMax.X) then
+        begin
+          FLastMax.X:=aMaxWidth;
+          FLastMax.Y:=NaN;
+          FLastSize:=Font.TextSizeMaxWidth(FCaption,aMaxWidth);
+        end;
+        Result:=FLastSize;
+      end;
+    end;
   end;
-  if MaxWidth>=FSize.X then
-    Result:=FSize
-  else
-    Result:=Font.TextSizeMaxWidth(FCaption,MaxWidth);
-  //writeln('TCustomLabel.GetPreferredContentBox_MaxWidth ',GetPath,' ',Result.ToString);
 end;
 
 { TLabel }

+ 347 - 262
src/base/fresnel.dom.pas

@@ -356,9 +356,14 @@ type
                                 var Value: string; // returns empty for invalid
                                 out Complete: boolean) of object;
       TGetAsStringEvent = function(El: TFresnelElement): string of object;
+      TGetLength = function(const aComp: TCSSResCompValue; El: TFresnelElement;
+        NoChildren: boolean{true=dont use child values}
+        ): TFresnelLength of object;
   public
-    OnCompute: TComputeEvent; // removes unknown/invalid values, substitutes calc(), converts to base unit (px)
+    LengthHorizontal: boolean; // // e.g. what dpi to use
     OnAsString: TGetAsStringEvent; // returns value of shorthand (e.g. font or margin-block)
+    OnCompute: TComputeEvent; // removes unknown/invalid values, substitutes calc(), converts to base unit (px)
+    OnGetLength: TGetLength; // resolve special length: cuPercent to 100% pixels and keyword to pixel
   end;
 
   { TFresnelCSSAttrDesc }
@@ -535,6 +540,10 @@ type
     function GetPlaceItems(El: TFresnelElement): string; virtual;
     function GetPlaceSelf(El: TFresnelElement): string; virtual;
 
+    // get length
+    function GetLength_ContainerContentWidth(const aComp: TCSSResCompValue; El: TFresnelElement; NoChildren: boolean): TFresnelLength; virtual;
+    function GetLength_ContainerContentHeight(const aComp: TCSSResCompValue; El: TFresnelElement; NoChildren: boolean): TFresnelLength; virtual;
+    function GetLength_BorderWidth(const aComp: TCSSResCompValue; El: TFresnelElement; NoChildren: boolean): TFresnelLength; virtual;
   public
     // keywords
     const
@@ -823,16 +832,33 @@ type
     constructor Create;
     procedure Init; override;
     // register attributes
-    function AddFresnelAttr(Attr: TFresnelCSSAttribute; aInherits: boolean; const OnCheck: TCSSAttributeDesc.TCheckEvent): TFresnelCSSAttrDesc; virtual; overload;
+    function AddFresnelAttr(Attr: TFresnelCSSAttribute; aInherits: boolean;
+      const OnCheck: TCSSAttributeDesc.TCheckEvent): TFresnelCSSAttrDesc; virtual; overload;
     function AddFresnelShorthand(Attr: TFresnelCSSAttribute; const OnCheck: TCSSAttributeDesc.TCheckEvent;
       const OnSplit: TCSSAttributeDesc.TSplitShorthandEvent;
       const OnAsString: TFresnelElementAttrDesc.TGetAsStringEvent;
       const CompProps: TFresnelCSSAttributeArray): TFresnelCSSAttrDesc; virtual; overload;
-    function AddFresnelLonghand(Attr: TFresnelCSSAttribute; Inherits: boolean; const OnCheck: TCSSAttributeDesc.TCheckEvent; const InitialValue: TCSSString = ''): TFresnelCSSAttrDesc; virtual; overload;
+    function AddFresnelLonghand(Attr: TFresnelCSSAttribute; Inherits: boolean;
+      const OnCheck: TCSSAttributeDesc.TCheckEvent;
+      const InitialValue: TCSSString = ''): TFresnelCSSAttrDesc; virtual; overload;
+    function AddFresnelLonghandLength(Attr: TFresnelCSSAttribute; Inherits: boolean;
+      const OnCheck: TCSSAttributeDesc.TCheckEvent;
+      Horizontal: boolean;
+      const InitialValue: TCSSString = '';
+      const OnGetLength: TFresnelElementAttrDesc.TGetLength = nil
+      ): TFresnelCSSAttrDesc; virtual; overload;
     // register pseudo classes
     function AddFresnelPseudoClass(Pseudo: TFresnelCSSPseudoClass): TFresnelCSSPseudoClassDesc; virtual; overload;
   end;
 
+  TFresnelLayoutMode = (
+    flmMinWidth,
+    flmMinHeight,
+    flmMaxWidth,
+    flmMaxHeight
+    );
+  TFresnelLayoutModes = set of TFresnelLayoutMode;
+
   { TFresnelLayoutNode }
 
   TFresnelLayoutNode = class(TComponent)
@@ -844,10 +870,59 @@ type
     function GetNodes(Index: integer): TFresnelLayoutNode;
     procedure SetElement(const AValue: TFresnelElement);
     procedure SetParent(const AValue: TFresnelLayoutNode);
+  protected
+    FContainerContentHeight: TFresnelLength;
+    FContainerContentHeightValid: boolean;
+    FContainerContentWidth: TFresnelLength;
+    FContainerContentWidthValid: boolean;
   public
+    Container: TFresnelElement;
+    SkipLayout: boolean; // e.g. element or ancestor display:none or visibility=collapse
+    SkipRendering: boolean; // e.g. element or ancestor display:none or visibility<>visible
+    SubParent: boolean; // a node in between: has children but no Layouter, e.g. span
+
+    // used values
+    ZIndex: TFresnelLength;
+    BorderLeft: TFresnelLength;
+    BorderRight: TFresnelLength;
+    BorderTop: TFresnelLength;
+    BorderBottom: TFresnelLength;
+    PaddingLeft: TFresnelLength;
+    PaddingRight: TFresnelLength;
+    PaddingTop: TFresnelLength;
+    PaddingBottom: TFresnelLength;
+    MarginLeft: TFresnelLength;
+    MarginRight: TFresnelLength;
+    MarginTop: TFresnelLength;
+    MarginBottom: TFresnelLength;
+    LineHeight: TFresnelLength;
+
+    // attributes depending on position:
+    //  static: left, top, right, bottom are ignored
+    //  relative: relative to layout position. left 10px moves 10px to the right, right 10px moves 10px to the left
+    //  absolute: relative to container's contentbox. left 10px moves 10px to the right, right 10px moves 10px to the left
+    //  fixed: as absolute, except relative to viewport
+    //  sticky: as relative, except to container's non-scrolled contentbox
+    Left: TFresnelLength;
+    Top: TFresnelLength;
+    Right: TFresnelLength;
+    Bottom: TFresnelLength;
+
+    // content-boxed:
+    Width: TFresnelLength;
+    Height: TFresnelLength;
+    MinWidth: TFresnelLength;
+    MinHeight: TFresnelLength;
+    MaxWidth: TFresnelLength;
+    MaxHeight: TFresnelLength;
+
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
+    function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength = NaN; aMaxHeight: TFresnelLength = NaN): TFresnelPoint; virtual; // ignoring min|max-height
     function GetRoot: TFresnelLayoutNode;
+    function FitWidth(aWidth: TFresnelLength): TFresnelLength;
+    function FitHeight(aHeight: TFresnelLength): TFresnelLength;
+    procedure ResetUsedLengths; virtual;
     {$IF FPC_FULLVERSION>=30301}
     procedure SortNodes(const Compare: TListSortComparer_Context; Context: Pointer = nil); virtual;
     {$ELSE}
@@ -858,31 +933,27 @@ type
     property NodeCount: integer read GetNodeCount;
     property Nodes[Index: integer]: TFresnelLayoutNode read GetNodes;
   end;
-  TFLLayoutNode = TFresnelLayoutNode;
-  TFresnelLayoutElDataClass = class of TFresnelLayoutNode;
+  TFresnelLayoutNodeClass = class of TFresnelLayoutNode;
 
   { TFresnelLayouter }
 
   TFresnelLayouter = class(TComponent)
   public
-    procedure Apply(aViewport: TFresnelViewport); virtual; abstract;
-    procedure ComputeCSSLayoutNode(El: TFresnelElement); virtual; abstract;
-    procedure ComputedChildrenCSS(El: TFresnelElement); virtual; abstract;
+    procedure Apply; virtual; abstract;
   end;
   TFLLayouter = TFresnelLayouter;
   TFresnelLayouterClass = class of TFresnelLayouter;
 
   { IFresnelFont }
 
-  IFresnelFont = interface
-    ['{6B53C662-5598-419B-996B-7E839271B64E}']
+  IFresnelFont = interface ['{6B53C662-5598-419B-996B-7E839271B64E}']
     function GetFamily: string;
     function GetKerning: TFresnelCSSKerning;
     function GetSize: TFresnelLength; // in pixel
     function GetStyle: string;
     function GetVariant: string;
     function GetWeight: TFresnelLength; // 0..750
-    function GetWidth: TFresnelLength; // in percent
+    function GetWidth: TFresnelLength; // 1 = default
     function TextSize(const aText: string): TFresnelPoint;
     function TextSizeMaxWidth(const aText: string; MaxWidth: TFresnelLength): TFresnelPoint;
     function GetTool: TObject;
@@ -923,7 +994,7 @@ type
     Style: string;
     Variant_: string;
     Weight: TFresnelLength; // 100..750
-    Width: TFresnelLength; // in percent
+    Width: TFresnelLength; // 1 = default
     function Compare(const Desc: TFresnelFontDesc): integer;
     class function CompareDescriptors(const A, B: TFresnelFontDesc): integer; static;
   end;
@@ -961,9 +1032,7 @@ type
   private
     FAfterRender: TNotifyEvent;
     FBeforeRender: TNotifyEvent;
-    FComputedBorderBox: TFresnelRect;
     FComputedBoxSizing: TCSSNumericalID;
-    FComputedContentBox: TFresnelRect;
     FComputedDirection: TCSSNumericalID;
     FComputedVisibility: TCSSNumericalID;
     FComputedWritingMode: TCSSNumericalID;
@@ -978,6 +1047,8 @@ type
     // Todo: change to dictionary to reduce mem footprint
     FStandardEvents : Array[0..evtLastEvent] of TEventHandlerItem;
     FEventDispatcher : TFresnelEventDispatcher;
+    FUsedBorderBox: TFresnelRect;
+    FUsedContentBox: TFresnelRect;
     function GetNodeCount: integer;
     function GetNodes(Index: integer): TFresnelElement;
     function GetViewportConnected: boolean;
@@ -1030,7 +1101,7 @@ type
     procedure SetParentComponent(Value: TComponent); override;
     procedure ChildDestroying(El: TFresnelElement); virtual;
     procedure DoRender({%H-}aRenderer: IFresnelRenderer); virtual;
-    { IFResnelRenderable }
+    { IFresnelRenderable }
     Procedure BeforeRender;
     Procedure AfterRender;
     Procedure Render(aRenderer : IFresnelRenderer);
@@ -1080,10 +1151,12 @@ type
     // CSS computed attributes
     procedure ComputeStyleElement; virtual; // parse inline style
     procedure ComputeCSSValues; virtual; // call resolver to collect CSS values and resolve shorthands
-    procedure ComputeCSSAfterLayoutNode(Layouter: TFresnelLayouter); virtual; // after layouter node, before layouter traverse children
+    procedure ComputeCSSAfterLayoutNode(Layouter: TFresnelLayouter); virtual; // called after layouter node, before layouter traverse children
     function GetCSSString(AttrID: TCSSNumericalID; Compute: boolean; out Complete: boolean): string; virtual;
-    function GetComputedLength(Attr: TFresnelCSSAttribute; UseNaNOnFail: boolean = false): TFresnelLength; virtual; overload;
-    function GetComputedLength(const aComp: TCSSResCompValue; Attr: TFresnelCSSAttribute; UseNaNOnFail: boolean = false): TFresnelLength; virtual; overload;
+    function GetComputedLength(Attr: TFresnelCSSAttribute; UseNaNOnFail: boolean = false; NoChildren: boolean = false): TFresnelLength; virtual; overload;
+    function GetComputedLength(AttrID: TCSSNumericalID; UseNaNOnFail: boolean = false; NoChildren: boolean = false): TFresnelLength; virtual; overload;
+    function GetComputedLength(const aComp: TCSSResCompValue; AttrID: TCSSNumericalID;
+      UseNaNOnFail: boolean = false; NoChildren: boolean = false): TFresnelLength; virtual; overload;
     function GetComputedString(Attr: TFresnelCSSAttribute): string; virtual;
     function GetComputedCSSString(AttrID: TCSSNumericalID): string; virtual; overload;
     function GetComputedCSSString(const AttrName: string): string; overload;
@@ -1098,6 +1171,7 @@ type
     function GetComputedAlignContent(out SubKW: TCSSNumericalID): TCSSNumericalID; virtual;
     function GetComputedAlignItems(out SubKW: TCSSNumericalID): TCSSNumericalID; virtual;
     function GetComputedAlignSelf(out SubKW: TCSSNumericalID): TCSSNumericalID; virtual;
+    function GetComputedBackgroundOrigin: TCSSNumericalID; virtual;
     procedure GetComputedMarginBlockStartEndAttr(out aStartAttr, aEndAttr: TFresnelCSSAttribute); virtual;
     procedure GetComputedMarginInlineStartEndAttr(out aStartAttr, aEndAttr: TFresnelCSSAttribute); virtual;
     function GetComputedTextShadow(out aOffsetX, aOffsetY, aRadius: TFresnelLength; out aColor: TFPColor): boolean; virtual; // on fail returns 0
@@ -1108,9 +1182,7 @@ type
     procedure SetComputedCSSString(Attr: TFresnelCSSAttribute; const Value: string); virtual;
     procedure WriteComputedAttributes(Title: string);
     property ComputedAttribute[Attr: TFresnelCSSAttribute]: string read GetComputedString write SetComputedCSSString;
-    property ComputedBorderBox: TFresnelRect read FComputedBorderBox write FComputedBorderBox; // relative to layout parent
     property ComputedBoxSizing: TCSSNumericalID read FComputedBoxSizing;
-    property ComputedContentBox: TFresnelRect read FComputedContentBox write FComputedContentBox; // relative to layout parent
     property ComputedDirection: TCSSNumericalID read FComputedDirection;
     property ComputedDisplayInside: TCSSNumericalID read FComputedDisplayInside;
     property ComputedDisplayOutside: TCSSNumericalID read FComputedDisplayOutside;
@@ -1120,24 +1192,20 @@ type
     // CSS pseudo classes
     property CSSPseudoClass[Pseudo: TFresnelCSSPseudoClass]: boolean read GetCSSPseudoClass write SetCSSPseudoClass;
     // layouter
-    function GetMaxWidthIntrinsicContentBox: TFresnelLength; virtual; // this element, excluding children, ignoring max-width
-    function GetMaxWidthContentBox: TFresnelLength; virtual; // this element, excluding children
-    function GetMaxWidthBorderBox: TFresnelLength; virtual; // this element, excluding children
-    function GetMinWidthIntrinsicContentBox: TFresnelLength; virtual; // this element, excluding children, ignoring min-width
-    function GetMinWidthContentBox: TFresnelLength; virtual; // this element, excluding children
-    function GetMinWidthBorderBox: TFresnelLength; virtual; // this element, excluding children
-    function GetPreferredContentBox_MaxWidth(MaxWidth: TFresnelLength): TFresnelPoint; virtual; // this element, excluding children, cannot be NaN
-    function GetPreferredBorderBox_MaxWidth(MaxWidth: TFresnelLength): TFresnelPoint; virtual; // this element, excluding children, cannot be NaN
-    function GetBlockContainer: TFresnelElement; virtual;
-    function GetBlockContainerWH(IsHorizontal: boolean; UseNaNOnFail: boolean): TFresnelLength; virtual;
+    function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength = NaN; aMaxHeight: TFresnelLength = NaN): TFresnelPoint; virtual; // ignoring min|max-height
+    function GetContainer: TFresnelElement; virtual;
+    function GetContainerContentWidth(UseNaNOnFail: boolean): TFresnelLength; virtual; // used value
+    function GetContainerContentHeight(UseNaNOnFail: boolean): TFresnelLength; virtual; // used value
     function IsBlockFormattingContext: boolean; virtual;
     property DOMIndex: integer read FDOMIndex write FDOMIndex;
     property LayoutNode: TFresnelLayoutNode read FLayoutNode write FLayoutNode;
+    property UsedBorderBox: TFresnelRect read FUsedBorderBox write FUsedBorderBox; // relative to layout parent's used contentbox
+    property UsedContentBox: TFresnelRect read FUsedContentBox write FUsedContentBox; // relative to layout parent's used contentbox
     // renderer
     function GetBoundingClientRect: TFresnelRect; virtual;
     property Rendered: boolean read FRendered write FRendered;
-    property RenderedBorderBox: TFresnelRect read FRenderedBorderBox write FRenderedBorderBox; // relative to layout parent
-    property RenderedContentBox: TFresnelRect read FRenderedContentBox write FRenderedContentBox; // relative to layout parent
+    property RenderedBorderBox: TFresnelRect read FRenderedBorderBox write FRenderedBorderBox; // relative to layout parent's rendered contentbox
+    property RenderedContentBox: TFresnelRect read FRenderedContentBox write FRenderedContentBox; // relative to layout parent's rendered contentbox
     // Events
     function AddEventListener(aID : TEventID; aHandler : TFresnelEventHandler) : Integer;
     Function AddEventListener(Const aName: TEventName; aHandler : TFresnelEventHandler) : Integer;
@@ -1166,6 +1234,13 @@ type
   TFresnelElementClass = class of TFresnelElement;
   TFresnelElementArray = array of TFresnelElement;
 
+  { TReplacedElement - base class for elements with special content and no child elements, e.g. label, video }
+
+  TReplacedElement = class(TFresnelElement)
+  public
+    function AcceptChildrenAtDesignTime: boolean; override;
+  end;
+
   TFresnelViewportLength = (
     vlFontMinSize,
     vlDPIHorizontal,
@@ -1201,14 +1276,12 @@ type
     FScrollbarWidth: array[boolean] of TFresnelLength;
     FHeight: TFresnelLength;
     FWidth: TFresnelLength;
-    FMaxPreferredWidth: TFresnelLength;
     procedure CSSResolverLog(Sender: TObject; Entry: TCSSResolverLogEntry);
   protected
     class var FFresnelEventsRegistered: boolean;
   protected
     function GetDPI(IsHorizontal: boolean): TFresnelLength; override;
     function GetHeight: TFresnelLength; virtual;
-    function GetMaxPreferredWidth: TFresnelLength; virtual;
     function GetScrollbarWidth(IsHorizontal: boolean): TFresnelLength; virtual;
     function GetVPLength(l: TFresnelViewportLength): TFresnelLength; virtual;
     function GetWidth: TFresnelLength; virtual;
@@ -1220,7 +1293,6 @@ type
     procedure SetDPI(IsHorizontal: boolean; const AValue: TFresnelLength);
     procedure SetFontMinSize(const AValue: TFresnelLength); virtual;
     procedure SetHeight(AValue: TFresnelLength); virtual;
-    procedure SetMaxPreferredWidth(const AValue: TFresnelLength); virtual;
     procedure SetScrollbarWidth(IsHorizontal: boolean;
       const AValue: TFresnelLength); virtual;
     procedure SetStylesheet(AValue: TStrings); virtual;
@@ -1253,7 +1325,6 @@ type
     property StylesheetStamp: TCSSNumericalID read FStylesheetStamp;
     property Width: TFresnelLength read GetWidth write SetWidth;
     property Height: TFresnelLength read GetHeight write SetHeight;
-    property MaxPreferredWidth: TFresnelLength read GetMaxPreferredWidth write SetMaxPreferredWidth;
     property Layouter: TFresnelLayouter read FLayouter write FLayouter;
     property FontEngine: TFresnelFontEngine read FFontEngine write FFontEngine;
     property OnDomChanged: TNotifyEvent read FOnDomChanged write FOnDomChanged;
@@ -4385,6 +4456,42 @@ begin
   Result:=anAlign+' '+aJustify;
 end;
 
+function TFresnelCSSRegistry.GetLength_ContainerContentWidth(const aComp: TCSSResCompValue;
+  El: TFresnelElement; NoChildren: boolean): TFresnelLength;
+begin
+  if (aComp.Kind=rvkFloat) and (aComp.FloatUnit=cuPercent) then
+    Result:=El.GetContainerContentWidth(true)
+  else
+    Result:=NaN;
+  if NoChildren then ;
+end;
+
+function TFresnelCSSRegistry.GetLength_ContainerContentHeight(const aComp: TCSSResCompValue;
+  El: TFresnelElement; NoChildren: boolean): TFresnelLength;
+begin
+  if (aComp.Kind=rvkFloat) and (aComp.FloatUnit=cuPercent) then
+    Result:=El.GetContainerContentHeight(true)
+  else
+    Result:=NaN;
+  if NoChildren then ;
+end;
+
+function TFresnelCSSRegistry.GetLength_BorderWidth(const aComp: TCSSResCompValue;
+  El: TFresnelElement; NoChildren: boolean): TFresnelLength;
+begin
+  case aComp.Kind of
+  rvkKeyword:
+    case aComp.KeywordID of
+    kwThin: exit(0.5);
+    kwMedium: exit(1);
+    kwThick: exit(2);
+    end;
+  end;
+  Result:=NaN;
+  if NoChildren then ;
+  if El=nil then ;
+end;
+
 function TFresnelCSSRegistry.RoundFontWidth(aWidth: TFresnelLength): TFontWidthName;
 var
   fw: TFontWidthName;
@@ -4654,10 +4761,10 @@ begin
     kwBothInline,kwBothBlock,kwBoth];
 
   // top, right, bottom, left
-  AddFresnelLonghand(fcaTop,false,@CheckLeftTopRightBottom,'auto');
-  AddFresnelLonghand(fcaRight,false,@CheckLeftTopRightBottom,'auto');
-  AddFresnelLonghand(fcaBottom,false,@CheckLeftTopRightBottom,'auto');
-  AddFresnelLonghand(fcaLeft,false,@CheckLeftTopRightBottom,'auto');
+  AddFresnelLonghandLength(fcaTop,false,@CheckLeftTopRightBottom,false,'auto',@GetLength_ContainerContentHeight);
+  AddFresnelLonghandLength(fcaRight,false,@CheckLeftTopRightBottom,true,'auto',@GetLength_ContainerContentWidth);
+  AddFresnelLonghandLength(fcaBottom,false,@CheckLeftTopRightBottom,false,'auto',@GetLength_ContainerContentHeight);
+  AddFresnelLonghandLength(fcaLeft,false,@CheckLeftTopRightBottom,true,'auto',@GetLength_ContainerContentWidth);
   with Chk_LeftTopRightBottom_Dim do begin
     AllowedUnits:=cuAllLengthsAndPercent;
     AllowNegative:=true;
@@ -4666,23 +4773,23 @@ begin
   end;
 
   // width, height
-  AddFresnelLonghand(fcaWidth,false,@CheckWidthHeight,'auto');
-  AddFresnelLonghand(fcaHeight,false,@CheckWidthHeight,'auto');
+  AddFresnelLonghandLength(fcaWidth,false,@CheckWidthHeight,true,'auto',@GetLength_ContainerContentWidth);
+  AddFresnelLonghandLength(fcaHeight,false,@CheckWidthHeight,false,'auto',@GetLength_ContainerContentHeight);
   Chk_WidthHeight_Dim.AllowedUnits:=cuAllLengthsAndPercent;
   Chk_WidthHeight_Dim.AllowFrac:=true;
   Chk_WidthHeight_Dim.AllowedKeywordIDs:=[kwAuto,kwFitContent,kwMinContent,kwMaxContent];
 
   // min-width, max-width, min-height, max-height
-  AddFresnelLonghand(fcaMinWidth,false,@CheckWidthHeight,'auto');
-  AddFresnelLonghand(fcaMaxWidth,false,@CheckWidthHeight,'auto');
-  AddFresnelLonghand(fcaMinHeight,false,@CheckWidthHeight,'auto');
-  AddFresnelLonghand(fcaMaxHeight,false,@CheckWidthHeight,'auto');
+  AddFresnelLonghandLength(fcaMinWidth,false,@CheckWidthHeight,true,'auto',@GetLength_ContainerContentWidth);
+  AddFresnelLonghandLength(fcaMaxWidth,false,@CheckWidthHeight,false,'auto',@GetLength_ContainerContentHeight);
+  AddFresnelLonghandLength(fcaMinHeight,false,@CheckWidthHeight,true,'auto',@GetLength_ContainerContentWidth);
+  AddFresnelLonghandLength(fcaMaxHeight,false,@CheckWidthHeight,false,'auto',@GetLength_ContainerContentHeight);
 
   // border-width
-  AddFresnelLonghand(fcaBorderTopWidth,false,@CheckBorderWidth,'medium');
-  AddFresnelLonghand(fcaBorderRightWidth,false,@CheckBorderWidth,'medium');
-  AddFresnelLonghand(fcaBorderBottomWidth,false,@CheckBorderWidth,'medium');
-  AddFresnelLonghand(fcaBorderLeftWidth,false,@CheckBorderWidth,'medium');
+  AddFresnelLonghandLength(fcaBorderTopWidth,false,@CheckBorderWidth,false,'medium',@GetLength_BorderWidth);
+  AddFresnelLonghandLength(fcaBorderRightWidth,false,@CheckBorderWidth,true,'medium',@GetLength_BorderWidth);
+  AddFresnelLonghandLength(fcaBorderBottomWidth,false,@CheckBorderWidth,false,'medium',@GetLength_BorderWidth);
+  AddFresnelLonghandLength(fcaBorderLeftWidth,false,@CheckBorderWidth,true,'medium',@GetLength_BorderWidth);
   Chk_BorderWidth_Dim.AllowedUnits:=cuAllLengths;
   Chk_BorderWidth_Dim.AllowFrac:=true;
   Chk_BorderWidth_Dim.AllowedKeywordIDs:=[kwThin,kwMedium,kwThick];
@@ -4799,11 +4906,11 @@ begin
   Chk_TextShadow_Radius_Dim.AllowedUnits:=cuAllLengths;
   Chk_TextShadow_Radius_Dim.AllowFrac:=true;
 
-  // margin
-  AddFresnelLonghand(fcaMarginTop,false,@CheckMargin,'0');
-  AddFresnelLonghand(fcaMarginRight,false,@CheckMargin,'0');
-  AddFresnelLonghand(fcaMarginBottom,false,@CheckMargin,'0');
-  AddFresnelLonghand(fcaMarginLeft,false,@CheckMargin,'0');
+  // margin, Note: % uses container's width even for top and bottom
+  AddFresnelLonghandLength(fcaMarginTop,false,@CheckMargin,false,'0',@GetLength_ContainerContentWidth);
+  AddFresnelLonghandLength(fcaMarginRight,false,@CheckMargin,true,'0',@GetLength_ContainerContentWidth);
+  AddFresnelLonghandLength(fcaMarginBottom,false,@CheckMargin,false,'0',@GetLength_ContainerContentWidth);
+  AddFresnelLonghandLength(fcaMarginLeft,false,@CheckMargin,true,'0',@GetLength_ContainerContentWidth);
   Chk_Margin_Dim.AllowedKeywordIDs:=[kwAuto];
   Chk_Margin_Dim.AllowedUnits:=cuAllLengthsAndPercent;
   Chk_Margin_Dim.AllowFrac:=true;
@@ -4828,11 +4935,11 @@ begin
   Chk_Opacity_Dim.AllowedUnits:=[cuNone,cuPercent];
   Chk_Opacity_Dim.AllowFrac:=true;
 
-  // padding
-  AddFresnelLonghand(fcaPaddingTop,false,@CheckPadding,'0');
-  AddFresnelLonghand(fcaPaddingRight,false,@CheckPadding,'0');
-  AddFresnelLonghand(fcaPaddingBottom,false,@CheckPadding,'0');
-  AddFresnelLonghand(fcaPaddingLeft,false,@CheckPadding,'0');
+  // padding, Note: % uses container's width even for top and bottom
+  AddFresnelLonghandLength(fcaPaddingTop,false,@CheckPadding,false,'0',@GetLength_ContainerContentWidth);
+  AddFresnelLonghandLength(fcaPaddingRight,false,@CheckPadding,true,'0',@GetLength_ContainerContentWidth);
+  AddFresnelLonghandLength(fcaPaddingBottom,false,@CheckPadding,false,'0',@GetLength_ContainerContentWidth);
+  AddFresnelLonghandLength(fcaPaddingLeft,false,@CheckPadding,true,'0',@GetLength_ContainerContentWidth);
   Chk_Padding_Dim.AllowedUnits:=cuAllLengthsAndPercent;
   Chk_Padding_Dim.AllowFrac:=true;
   AddFresnelShorthand(fcaPadding,@CheckPadding,@SplitPadding,@GetPadding,
@@ -4855,13 +4962,13 @@ begin
   // background-origin
   AddFresnelLonghand(fcaBackgroundOrigin,false,@CheckBackgroundOrigin,'padding-box');
   Chk_BackgroundOrigin_KeywordIDs:=[kwContentBox,kwPaddingBox,kwBorderBox];
-  // background-position-x
-  AddFresnelLonghand(fcaBackgroundPositionX,false,@CheckBackgroundPositionXY,'0%');
+  // background-position-x, % depends on background-origin
+  AddFresnelLonghandLength(fcaBackgroundPositionX,false,@CheckBackgroundPositionXY,true,'0%');
   Chk_BackgroundPositionX_Dim.AllowedKeywordIDs:=[kwLeft,kwCenter,kwRight];
   Chk_BackgroundPositionX_Dim.AllowedUnits:=cuAllLengthsAndPercent;
   Chk_BackgroundPositionX_Dim.AllowFrac:=true;
   // background-position-y
-  AddFresnelLonghand(fcaBackgroundPositionY,false,@CheckBackgroundPositionXY,'0%');
+  AddFresnelLonghandLength(fcaBackgroundPositionY,false,@CheckBackgroundPositionXY,false,'0%');
   Chk_BackgroundPositionY_Dim.AllowedKeywordIDs:=[kwTop,kwCenter,kwBottom];
   Chk_BackgroundPositionY_Dim.AllowedUnits:=cuAllLengthsAndPercent;
   Chk_BackgroundPositionY_Dim.AllowFrac:=true;
@@ -4973,14 +5080,13 @@ begin
   AddFresnelShorthand(fcaPlaceSelf,@CheckPlaceSelf,@SplitPlaceSelf,@GetPlaceSelf,
     [fcaAlignSelf,fcaJustifySelf]);
 
-
   // column-gap
-  AddFresnelLonghand(fcaColumnGap,false,@CheckColumnRowGap,'normal');
+  AddFresnelLonghandLength(fcaColumnGap,false,@CheckColumnRowGap,true,'normal',@GetLength_ContainerContentWidth);
+  // row-gap
+  AddFresnelLonghandLength(fcaRowGap,false,@CheckColumnRowGap,false,'normal',@GetLength_ContainerContentHeight);
   Chk_ColumnRowGap_Dim.AllowedKeywordIDs:=[kwNormal];
   Chk_ColumnRowGap_Dim.AllowedUnits:=cuAllLengthsAndPercent;
   Chk_ColumnRowGap_Dim.AllowFrac:=true;
-  // column-row
-  AddFresnelLonghand(fcaRowGap,false,@CheckColumnRowGap,'normal');
   // gap
   AddFresnelShorthand(fcaGap,@CheckGap,@SplitGap,@GetGap,[fcaColumnGap,fcaRowGap]);
 
@@ -4995,6 +5101,7 @@ function TFresnelCSSRegistry.AddFresnelAttr(Attr: TFresnelCSSAttribute;
 begin
   Result:=TFresnelCSSAttrDesc(AddAttribute(FresnelCSSAttributeNames[Attr],'',aInherits,true,TFresnelCSSAttrDesc));
   Result.Attr:=Attr;
+  Result.LengthHorizontal:=true;
   Result.OnCheck:=OnCheck;
   FresnelAttrs[Attr]:=Result;
 end;
@@ -5030,6 +5137,16 @@ begin
   Result.InitialValue:=InitialValue;
 end;
 
+function TFresnelCSSRegistry.AddFresnelLonghandLength(Attr: TFresnelCSSAttribute;
+  Inherits: boolean; const OnCheck: TCSSAttributeDesc.TCheckEvent; Horizontal: boolean;
+  const InitialValue: TCSSString; const OnGetLength: TFresnelElementAttrDesc.TGetLength
+  ): TFresnelCSSAttrDesc;
+begin
+  Result:=AddFresnelLonghand(Attr,Inherits,OnCheck,InitialValue);
+  Result.LengthHorizontal:=Horizontal;
+  Result.OnGetLength:=OnGetLength;
+end;
+
 procedure TFresnelCSSRegistry.SplitLonghandSides(var AttrIDs: TCSSNumericalIDArray;
   var Values: TCSSStringArray; Top, Right, Bottom, Left: TFresnelCSSAttribute;
   const Found: TCSSStringArray);
@@ -5160,6 +5277,16 @@ begin
   inherited Destroy;
 end;
 
+function TFresnelLayoutNode.GetIntrinsicContentSize(aMode: TFresnelLayoutMode;
+  aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength): TFresnelPoint;
+begin
+  Result.X:=0;
+  Result.Y:=0;
+  if aMode=flmMaxHeight then ;
+  if IsNan(aMaxWidth) then ;
+  if IsNan(aMaxHeight) then ;
+end;
+
 function TFresnelLayoutNode.GetRoot: TFresnelLayoutNode;
 begin
   Result:=Self;
@@ -5167,6 +5294,66 @@ begin
     Result:=Result.Parent;
 end;
 
+function TFresnelLayoutNode.FitWidth(aWidth: TFresnelLength): TFresnelLength;
+begin
+  Result:=aWidth;
+  if not IsNan(MinWidth) then
+  begin
+    if not IsNan(MaxWidth) then
+      Result:=MinWidth
+    else if (not IsNan(Result)) and (Result<MinWidth) then
+      Result:=MinWidth;
+  end else if (not IsNan(MaxWidth)) and (not IsNan(Result)) and (Result>MaxWidth) then
+    Result:=MaxWidth;
+end;
+
+function TFresnelLayoutNode.FitHeight(aHeight: TFresnelLength): TFresnelLength;
+begin
+  Result:=aHeight;
+  if not IsNan(MinHeight) then
+  begin
+    if not IsNan(MaxHeight) then
+      Result:=MinHeight
+    else if (not IsNan(Result)) and (Result<MinHeight) then
+      Result:=MinHeight;
+  end else if (not IsNan(MaxHeight)) and (not IsNan(Result)) and (Result>MaxHeight) then
+    Result:=MaxHeight;
+end;
+
+procedure TFresnelLayoutNode.ResetUsedLengths;
+begin
+  BorderLeft:=0;
+  BorderRight:=0;
+  BorderTop:=0;
+  BorderBottom:=0;
+  PaddingLeft:=0;
+  PaddingRight:=0;
+  PaddingTop:=0;
+  PaddingBottom:=0;
+  MarginLeft:=0;
+  MarginRight:=0;
+  MarginTop:=0;
+  MarginBottom:=0;
+  LineHeight:=0;
+
+  Left:=NaN;
+  Top:=NaN;
+  Right:=NaN;
+  Bottom:=NaN;
+
+  Width:=NaN;
+  Height:=NaN;
+  MinWidth:=NaN;
+  MinHeight:=NaN;
+  MaxWidth:=NaN;
+  MaxHeight:=NaN;
+
+  FContainerContentHeightValid:=false;
+  FContainerContentHeight:=NaN;
+  FContainerContentWidthValid:=false;
+  FContainerContentWidth:=NaN;
+end;
+
 {$IF FPC_FULLVERSION>=30301}
 procedure TFresnelLayoutNode.SortNodes(
   const Compare: TListSortComparer_Context; Context: Pointer);
@@ -5230,11 +5417,6 @@ begin
   Result:=FHeight;
 end;
 
-function TFresnelViewport.GetMaxPreferredWidth: TFresnelLength;
-begin
-  Result:=FMaxPreferredWidth;
-end;
-
 function TFresnelViewport.GetVPLength(l: TFresnelViewportLength
   ): TFresnelLength;
 begin
@@ -5365,12 +5547,6 @@ begin
   DomChanged;
 end;
 
-procedure TFresnelViewport.SetMaxPreferredWidth(const AValue: TFresnelLength);
-begin
-  if AValue=FMaxPreferredWidth then exit;
-  FMaxPreferredWidth:=AValue;
-end;
-
 procedure TFresnelViewport.SetScrollbarWidth(IsHorizontal: boolean;
   const AValue: TFresnelLength);
 begin
@@ -5436,21 +5612,6 @@ var
       TraverseCSS(El.Nodes[i]);
   end;
 
-  procedure TraverseLayout(El: TFresnelElement);
-  var
-    i: Integer;
-  begin
-    // create or free layout nodes
-    Layouter.ComputeCSSLayoutNode(El);
-    El.ComputeCSSAfterLayoutNode(Layouter);
-    //El.WriteComputedAttributes('TFresnelViewport.ApplyCSS');
-
-    for i:=0 to El.NodeCount-1 do
-      TraverseLayout(El.Nodes[i]);
-
-    Layouter.ComputedChildrenCSS(El);
-  end;
-
 begin
   //writeln('TFresnelViewport.ApplyCSS ',Name,' Width=',FloatToCSSStr(Width),',Height=',FloatToCSSStr(Height));
   DomModified:=false;
@@ -5463,7 +5624,7 @@ begin
   CurDomIndex:=0;
   TraverseCSS(Self);
   // layout
-  TraverseLayout(Self);
+  Layouter.Apply;
 end;
 
 procedure TFresnelViewport.DomChanged;
@@ -5638,7 +5799,6 @@ begin
   inherited Create(AOwner);
   FViewPort:=Self;
   FWidth:=800;
-  FMaxPreferredWidth:=100000;
   FHeight:=600;
   FDPI[false]:=FresnelDefaultDPI;
   FDPI[true]:=FresnelDefaultDPI;
@@ -6236,70 +6396,78 @@ begin
     UseInitialValue;
 end;
 
-function TFresnelElement.GetComputedLength(Attr: TFresnelCSSAttribute; UseNaNOnFail: boolean
-  ): TFresnelLength;
+function TFresnelElement.GetComputedLength(Attr: TFresnelCSSAttribute; UseNaNOnFail: boolean;
+  NoChildren: boolean): TFresnelLength;
 var
-  s: String;
-  aComp: TCSSResCompValue;
   AttrID: TCSSNumericalID;
+begin
+  AttrID:=CSSRegistry.FresnelAttrs[Attr].Index;
+  Result:=GetComputedLength(AttrID,UseNaNOnFail,NoChildren);
+end;
+
+function TFresnelElement.GetComputedLength(AttrID: TCSSNumericalID; UseNaNOnFail: boolean;
+  NoChildren: boolean): TFresnelLength;
+var
+  s: String;
   Complete: boolean;
+  aComp: TCSSResCompValue;
 begin
   if UseNaNOnFail then
     Result:=NaN
   else
     Result:=0;
 
-  AttrID:=CSSRegistry.FresnelAttrs[Attr].Index;
-  s:=GetCSSString(AttrID,true,Complete);
+  s:=GetCSSString(AttrID,not NoChildren,Complete);
   if s='' then exit;
 
   aComp.EndP:=PChar(s);
   if (not Resolver.ReadComp(aComp)) or (aComp.Kind<>rvkFloat) then
     exit;
-  Result:=GetComputedLength(aComp,Attr);
+  Result:=GetComputedLength(aComp,AttrID,UseNaNOnFail,NoChildren);
 end;
 
-function TFresnelElement.GetComputedLength(const aComp: TCSSResCompValue;
-  Attr: TFresnelCSSAttribute; UseNaNOnFail: boolean): TFresnelLength;
+function TFresnelElement.GetComputedLength(const aComp: TCSSResCompValue; AttrID: TCSSNumericalID;
+  UseNaNOnFail: boolean; NoChildren: boolean): TFresnelLength;
 
-  function GetIsHorizontal: boolean;
+  function ResolveSpecial(var aResult: TFresnelLength): boolean;
+  var
+    Desc: TCSSAttributeDesc;
+    ElDesc: TFresnelElementAttrDesc;
   begin
-    Result:=Attr in [
-      fcaLeft,
-      fcaRight,
-      fcaMinWidth,
-      fcaMaxWidth,
-      fcaWidth,
-      fcaBorderLeftWidth,
-      fcaBorderRightWidth,
-      fcaMarginLeft,
-      fcaMarginRight,
-      fcaMarginTop, // margin-top, -bottom percentage uses the container width
-      fcaMarginBottom,
-      fcaPaddingLeft,
-      fcaPaddingRight,
-      fcaPaddingTop, // padding-top, -bottom percentage uses the container width
-      fcaPaddingBottom,
-      fcaBackgroundPositionX,
-      fcaBackgroundSize,
-      fcaColumnGap];
+    Result:=false;
+    Desc:=CSSRegistry.Attributes[AttrID];
+    if Desc is TFresnelElementAttrDesc then
+    begin
+      ElDesc:=TFresnelElementAttrDesc(Desc);
+      if ElDesc.OnGetLength<>nil then
+      begin
+        aResult:=ElDesc.OnGetLength(aComp,Self,NoChildren);
+        if not IsNan(aResult) then
+          exit(true);
+        if not UseNaNOnFail then
+          aResult:=0;
+      end;
+    end;
   end;
 
 var
-  IsHorizontal: Boolean;
+  Desc: TCSSAttributeDesc;
+  Horizontal: Boolean;
 begin
   if UseNaNOnFail then
     Result:=NaN
   else
     Result:=0;
-  if (aComp.Kind<>rvkFloat) or IsNan(aComp.Float) then
+  if (aComp.Kind<>rvkFloat) then
+  begin
+    ResolveSpecial(Result);
     exit;
+  end;
   case aComp.FloatUnit of
   cuNone:
     if aComp.Float=0 then
-    begin
-      exit(aComp.Float);
-    end else begin
+      exit(0)
+    else begin
       // number without unit is not allowed for pixel length
       exit;
     end;
@@ -6307,34 +6475,16 @@ begin
     exit(aComp.Float);
   cuPercent:
     begin
-      case Attr of
-      fcaLeft,fcaRight,fcaWidth,fcaMinWidth,fcaMaxWidth:
-        Result:=GetBlockContainerWH(true,UseNaNOnFail);
-      fcaTop,fcaBottom,fcaHeight,fcaMinHeight,fcaMaxHeight:
-        Result:=GetBlockContainerWH(false,UseNaNOnFail);
-      else
-        IsHorizontal:=GetIsHorizontal;
-        if IsHorizontal then
-          Result:=GetComputedLength(fcaWidth,UseNaNOnFail)
-        else
-          Result:=GetComputedLength(fcaHeight,UseNaNOnFail);
-      end;
-      if IsNan(Result) then
-      begin
-        if UseNaNOnFail then
-          Result:=NaN
-        else
-          Result:=0;
-        exit;
-      end;
+      if not ResolveSpecial(Result) then exit;
       Result:=aComp.Float*Result/100;
       exit;
     end;
   end;
   if not (aComp.FloatUnit in cuAllLengths) then
     exit;
-  IsHorizontal:=GetIsHorizontal;
-  Result:=aComp.Float*GetPixPerUnit(aComp.FloatUnit,IsHorizontal);
+  Desc:=CSSRegistry.Attributes[AttrID];
+  Horizontal:=(Desc is TFresnelElementAttrDesc) and TFresnelElementAttrDesc(Desc).LengthHorizontal;
+  Result:=aComp.Float*GetPixPerUnit(aComp.FloatUnit,Horizontal);
 end;
 
 function TFresnelElement.GetComputedString(Attr: TFresnelCSSAttribute): string;
@@ -6419,102 +6569,13 @@ begin
   end;
 end;
 
-function TFresnelElement.GetMaxWidthIntrinsicContentBox: TFresnelLength;
-begin
-  Result:=Viewport.MaxPreferredWidth;
-end;
-
-function TFresnelElement.GetMaxWidthContentBox: TFresnelLength;
-var
-  aValue: String;
-begin
-  aValue:=ComputedAttribute[fcaMinWidth];
-  case aValue of
-  '',
-  'auto': Result:=GetMaxWidthIntrinsicContentBox;
-  'max-content': Result:=GetPreferredContentBox_MaxWidth(Viewport.MaxPreferredWidth).X;
-  'min-content': Result:=GetMinWidthIntrinsicContentBox;
-  else
-    Result:=GetComputedLength(fcaMaxWidth,false);
-  end;
-  if Result<0 then
-    Result:=0;
-end;
-
-function TFresnelElement.GetMaxWidthBorderBox: TFresnelLength;
-begin
-  Result:=GetMinWidthContentBox;
-  if IsNan(Result) or (Result<0) then
-    Result:=0;
-  // border and padding cannot be negative
-  Result:=GetComputedLength(fcaBorderLeftWidth,false)
-         +GetComputedLength(fcaBorderRightWidth,false)
-         +GetComputedLength(fcaPaddingLeft,false)
-         +GetComputedLength(fcaPaddingRight,false)
-         +Result;
-end;
-
-function TFresnelElement.GetMinWidthIntrinsicContentBox: TFresnelLength;
-begin
-  Result:=0;
-end;
-
-function TFresnelElement.GetMinWidthContentBox: TFresnelLength;
-var
-  aValue: String;
-begin
-  aValue:=ComputedAttribute[fcaMinWidth];
-  case aValue of
-  '',
-  'auto': Result:=GetMinWidthIntrinsicContentBox;
-  'max-content': Result:=GetPreferredContentBox_MaxWidth(Viewport.MaxPreferredWidth).X;
-  'min-content': Result:=GetMinWidthIntrinsicContentBox;
-  else
-    Result:=GetComputedLength(fcaMinWidth,false);
-  end;
-  if Result<0 then
-    Result:=0;
-end;
-
-function TFresnelElement.GetMinWidthBorderBox: TFresnelLength;
-begin
-  Result:=GetMinWidthContentBox;
-  if IsNan(Result) or (Result<0) then
-    Result:=0;
-  // border and padding cannot be negative
-  Result:=GetComputedLength(fcaBorderLeft,false)
-         +GetComputedLength(fcaBorderRight,false)
-         +GetComputedLength(fcaPaddingLeft,false)
-         +GetComputedLength(fcaPaddingRight,false);
-end;
-
-function TFresnelElement.GetPreferredContentBox_MaxWidth(
-  MaxWidth: TFresnelLength): TFresnelPoint;
-begin
-  Result:=default(TFresnelPoint);
-  if MaxWidth=0 then ;
-end;
-
-function TFresnelElement.GetPreferredBorderBox_MaxWidth(MaxWidth: TFresnelLength
-  ): TFresnelPoint;
-var
-  ExtraWidth, ExtraHeight: TFresnelLength;
+function TFresnelElement.GetIntrinsicContentSize(aMode: TFresnelLayoutMode;
+  aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength): TFresnelPoint;
 begin
-  // border and padding cannot be negative
-  ExtraWidth:=GetComputedLength(fcaBorderLeft,false)
-             +GetComputedLength(fcaBorderRight,false)
-             +GetComputedLength(fcaPaddingLeft,false)
-             +GetComputedLength(fcaPaddingRight,false);
-  ExtraHeight:=GetComputedLength(fcaBorderTop,false)
-             +GetComputedLength(fcaBorderBottom,false)
-             +GetComputedLength(fcaPaddingTop,false)
-             +GetComputedLength(fcaPaddingBottom,false);
-  Result:=GetPreferredContentBox_MaxWidth(Max(0,MaxWidth-ExtraWidth));
-  Result.X:=Result.X+ExtraWidth;
-  Result.Y:=Result.Y+ExtraHeight;
+  Result:=LayoutNode.GetIntrinsicContentSize(aMode,aMaxWidth,aMaxHeight);
 end;
 
-function TFresnelElement.GetBlockContainer: TFresnelElement;
+function TFresnelElement.GetContainer: TFresnelElement;
 begin
   case ComputedPosition of
   CSSRegistry.kwAbsolute:
@@ -6552,25 +6613,34 @@ begin
   end;
 end;
 
-function TFresnelElement.GetBlockContainerWH(IsHorizontal: boolean; UseNaNOnFail: boolean
-  ): TFresnelLength;
+function TFresnelElement.GetContainerContentWidth(UseNaNOnFail: boolean): TFresnelLength;
 var
   El: TFresnelElement;
 begin
-  // e.g. 'width' uses width of block container
-  El:=GetBlockContainer;
+  El:=LayoutNode.Container;
   if El<>nil then
   begin
-    if IsHorizontal then
-      Result:=El.GetComputedLength(fcaWidth,UseNaNOnFail)
-    else
-      Result:=El.GetComputedLength(fcaHeight,UseNaNOnFail);
-  end else begin
-    if IsHorizontal then
-      Result:=Viewport.Width
-    else
-      Result:=Viewport.Height;
-  end;
+    Result:=El.LayoutNode.Width;
+    if not UseNaNOnFail and IsNan(Result) then Result:=0;
+  end else if UseNaNOnFail then
+    Result:=NaN
+  else
+    Result:=0;
+end;
+
+function TFresnelElement.GetContainerContentHeight(UseNaNOnFail: boolean): TFresnelLength;
+var
+  El: TFresnelElement;
+begin
+  El:=LayoutNode.Container;
+  if El<>nil then
+  begin
+    Result:=El.LayoutNode.Height;
+    if not UseNaNOnFail and IsNan(Result) then Result:=0;
+  end else if UseNaNOnFail then
+    Result:=NaN
+  else
+    Result:=0;
 end;
 
 function TFresnelElement.IsBlockFormattingContext: boolean;
@@ -6589,15 +6659,14 @@ begin
     exit(true);
   end;
 
-  if ComputedDisplayOutside in [CSSIDNone,CSSRegistry.kwNone] then
-    exit(false);
-
   case ComputedDisplayOutside of
+  CSSIDNone,
+  CSSRegistry.kwNone: exit(false);
   CSSRegistry.kwBlock: exit(true);
   end;
 
   case ComputedDisplayInside of
-  //CSSRegistry.kwtable,
+  //CSSRegistry.kwTable,
   CSSRegistry.kwFlex,
   CSSRegistry.kwGrid,
   //CSSRegistry.kwRuby,
@@ -6832,6 +6901,12 @@ begin
   end;
 end;
 
+function TFresnelElement.GetComputedBackgroundOrigin: TCSSNumericalID;
+begin
+  Result:=GetComputedCSSKeyword(CSSRegistry.FresnelAttrs[fcaBackgroundOrigin].Index,
+                                CSSRegistry.Chk_BackgroundOrigin_KeywordIDs);
+end;
+
 procedure TFresnelElement.GetComputedMarginBlockStartEndAttr(out aStartAttr,
   aEndAttr: TFresnelCSSAttribute);
 begin
@@ -7364,6 +7439,9 @@ end;
 
 procedure TFresnelElement.BeforeRender;
 begin
+  FRenderedBorderBox:=FUsedBorderBox;
+  FRenderedContentBox:=FUsedContentBox;
+
   If Assigned(FBeforeRender) then
     FBeforeRender(Self);
 end;
@@ -7906,7 +7984,14 @@ end;
 
 procedure TFresnelElement.ComputeCSSAfterLayoutNode(Layouter: TFresnelLayouter);
 begin
+  if Layouter<>nil then ;
+end;
+
+{ TReplacedElement }
 
+function TReplacedElement.AcceptChildrenAtDesignTime: boolean;
+begin
+  Result:=false;
 end;
 
 initialization

+ 0 - 2
src/base/fresnel.forms.pas

@@ -500,7 +500,6 @@ begin
   try
     ApplyCSS;
     //Layouter.WriteLayoutTree;
-    Layouter.Apply(Self);
     //FLLog(etDebug,['TFresnelCustomForm.OnQueuedLayout ',Name,':',ClassName,' After Layouter.Apply, Invalidate...']);
     Invalidate;
   finally
@@ -654,7 +653,6 @@ begin
   if DomModified then
   begin
     ApplyCSS;
-    Layouter.Apply(Self);
   end;
   Renderer.Draw(Self);
 end;

File diff suppressed because it is too large
+ 592 - 321
src/base/fresnel.layouter.pas


+ 24 - 49
src/base/fresnel.renderer.pas

@@ -487,70 +487,41 @@ end;
 
 procedure TFresnelRenderer.DrawElement(El: TFresnelElement);
 var
-  LNode: TSimpleFresnelLayoutNode;
-  aLeft, aTop, aRight, aBottom,
-    aMarginLeft, aMarginTop, aMarginRight, aMarginBottom,
-    aBorderLeft, aBorderRight, aBorderTop, aBorderBottom,
-    aPaddingLeft, aPaddingRight, aPaddingTop, aPaddingBottom: TFresnelLength;
+  LNode: TUsedLayoutNode;
   aBorderBox, aContentBox: TFresnelRect;
   BorderParams: TBorderAndBackground;
   aRenderable : IFresnelRenderable;
   s: TFresnelCSSSide;
   Corner: TFresnelCSSCorner;
-
 begin
   FLLog(etDebug,'TFresnelRenderer.DrawElement %s Origin=%s',[El.GetPath,Origin.ToString]);
-  LNode:=TSimpleFresnelLayoutNode(El.LayoutNode);
+  LNode:=TUsedLayoutNode(El.LayoutNode);
   if LNode.SkipRendering then exit;
   aRenderable:=El as IFresnelRenderable;
   aRenderable.BeforeRender;
   El.Rendered:=true;
-  aLeft:=El.GetComputedLength(fcaLeft);
-  aTop:=El.GetComputedLength(fcaTop);
-  aRight:=El.GetComputedLength(fcaRight);
-  aBottom:=El.GetComputedLength(fcaBottom);
-  FLLog(etDebug,'TFresnelRenderer.DrawElement %s [(%gx%g),(%gx%g)]',[El.GetPath,aLeft,aTop,aright,aBottom]);
-
-  aMarginLeft:=El.GetComputedLength(fcaMarginLeft);
-  aMarginRight:=El.GetComputedLength(fcaMarginRight);
-  aMarginTop:=El.GetComputedLength(fcaMarginTop);
-  aMarginBottom:=El.GetComputedLength(fcaMarginBottom);
-
-  aBorderLeft:=El.GetComputedBorderWidth(fcaBorderLeftWidth);
-  aBorderRight:=El.GetComputedBorderWidth(fcaBorderRightWidth);
-  aBorderTop:=El.GetComputedBorderWidth(fcaBorderTopWidth);
-  aBorderBottom:=El.GetComputedBorderWidth(fcaBorderBottomWidth);
-
-  aPaddingLeft:=El.GetComputedLength(fcaPaddingLeft);
-  aPaddingRight:=El.GetComputedLength(fcaPaddingRight);
-  aPaddingTop:=El.GetComputedLength(fcaPaddingTop);
-  aPaddingBottom:=El.GetComputedLength(fcaPaddingBottom);
-
-  aBorderBox.Left:=aLeft+aMarginLeft;
-  aBorderBox.Top:=aTop+aMarginTop;
-  aBorderBox.Right:=aRight-aMarginRight;
-  aBorderBox.Bottom:=aBottom-aMarginBottom;
+
+  aBorderBox:=El.UsedBorderBox;
   El.RenderedBorderBox:=aBorderBox;
 
-  aContentBox.Left:=aLeft+aMarginLeft+aBorderLeft+aPaddingLeft;
-  aContentBox.Top:=aTop+aMarginTop+aBorderTop+aPaddingTop;
-  aContentBox.Right:=aRight-aMarginRight-aBorderRight-aPaddingRight;
-  aContentBox.Bottom:=aBottom-aMarginBottom-aBorderBottom-aPaddingBottom;
+  aContentBox:=El.UsedContentBox;
   El.RenderedContentBox:=aContentBox;
 
+  FLLog(etDebug,'TFresnelRenderer.DrawElement %s %s',[El.GetPath,aBorderBox.ToString]);
+
   //writeln('TFresnelRenderer.DrawElement ',El.Name,' BorderBox=',El.RenderedBorderBox.ToString,' ContentBox=',El.RenderedContentBox.ToString);
 
   BorderParams:=CreateBorderAndBackground;
   try
-    BorderParams.BoundingBox.Box:=El.RenderedBorderBox;
+    BorderParams.BoundingBox.Box:=aBorderBox;
     if not SubPixel then
       MathRoundRect(BorderParams.BoundingBox.Box);
 
     // border-width
-    BorderParams.Width[ffsLeft]:=aBorderLeft;
-    BorderParams.Width[ffsTop]:=aBorderTop;
-    BorderParams.Width[ffsRight]:=aBorderRight;
-    BorderParams.Width[ffsBottom]:=aBorderBottom;
+    BorderParams.Width[ffsLeft]:=LNode.BorderLeft;
+    BorderParams.Width[ffsTop]:=LNode.BorderTop;
+    BorderParams.Width[ffsRight]:=LNode.BorderRight;
+    BorderParams.Width[ffsBottom]:=LNode.BorderBottom;
 
     // background-color
     BorderParams.BackgroundColorFP:=El.GetComputedColor(fcaBackgroundColor,colTransparent);
@@ -588,18 +559,18 @@ end;
 procedure TFresnelRenderer.DrawChildren(El: TFresnelElement);
 var
   OldOrigin: TFresnelPoint;
-  LNode: TSimpleFresnelLayoutNode;
+  LNode: TUsedLayoutNode;
   i: Integer;
   ChildEl: TFresnelElement;
 begin
-  LNode:=TSimpleFresnelLayoutNode(El.LayoutNode);
+  LNode:=TUsedLayoutNode(El.LayoutNode);
 
   OldOrigin:=Origin;
   Origin:=OldOrigin+El.RenderedContentBox.TopLeft;
   //writeln('TFresnelRenderer.DrawChildren ',El.GetPath,' Old=',OldOrigin.ToString,' Origin=',Origin.ToString);
   for i:=0 to LNode.NodeCount-1 do
   begin
-    ChildEl:=TSimpleFresnelLayoutNode(LNode.Nodes[i]).Element;
+    ChildEl:=TUsedLayoutNode(LNode.Nodes[i]).Element;
     //writeln('TFresnelRenderer.DrawChildren ',El.GetPath,' Child=',ChildEl.GetPath);
     DrawElement(ChildEl);
   end;
@@ -610,21 +581,26 @@ procedure TFresnelRenderer.Draw(Viewport: TFresnelViewport);
 var
   aContentBox: TFresnelRect;
   BackgroundColorFP: TFPColor;
+  aRenderable: IFresnelRenderable;
 begin
   //debugln(['TFresnelRenderer.Draw Origin=',dbgs(Origin)]);
-  Viewport.Rendered:=true;
   aContentBox.Left:=0;
   aContentBox.Top:=0;
   aContentBox.Right:=Viewport.Width;
   aContentBox.Bottom:=Viewport.Height;
-  Viewport.RenderedBorderBox:=aContentBox;
-  Viewport.RenderedContentBox:=aContentBox;
+  Viewport.UsedBorderBox:=aContentBox;
+  Viewport.UsedContentBox:=aContentBox;
+  aRenderable:=Viewport as IFresnelRenderable;
+  aRenderable.BeforeRender;
+  Viewport.Rendered:=true;
 
   BackgroundColorFP:=Viewport.GetComputedColor(fcaBackgroundColor,colWhite);
-
   FillRect(BackgroundColorFP,aContentBox);
 
+  aRenderable.Render(Self as IFresnelRenderer);
   DrawChildren(Viewport);
+
+  aRenderable.AfterRender;
 end;
 
 procedure TFresnelRenderer.ClearTextShadows;
@@ -634,7 +610,6 @@ end;
 
 { TFresnelRenderer.TBorderAndBackground }
 
-
 constructor TFresnelRenderer.TBorderAndBackground.Create(aRenderer: TFresnelRenderer);
 begin
   FRenderer:=aRenderer;

+ 1 - 1
src/skia/fresnel.skiarenderer.pas

@@ -132,7 +132,7 @@ type
   protected
     Type
       TSkiaBorderAndBackground = class (TBorderAndBackground)
-        Radii : TSkRoundRectRadii;
+        Radii: TSkRoundRectRadii;
         procedure CalcRadii;
       end;
   protected

+ 22 - 0
tests/base/TCFlexLayout.pas

@@ -14,6 +14,28 @@ type
   TTestFlexLayout = class(TCustomTestFresnelCSS)
   published
     procedure TestFlexLayout_Empty;
+    // todo procedure TestFlexLayout_Empty_FlexInline;
+    // todo: test flex-direction:row, flex-wrap:nowrap
+    // todo: test flex-direction:row-reverse, flex-wrap:nowrap
+    // todo: test flex-direction:row, flex-wrap:wrap
+    // todo: test flex-direction:row-reverse, flex-wrap:wrap
+    // todo: test flex-direction:row, flex-wrap:wrap-reverse
+    // todo: test flex-direction:row-reverse, flex-wrap:wrap-reverse
+    // todo: test flex-direction:column, flex-wrap:nowrap
+    // todo: test flex-direction:column-reverse, flex-wrap:nowrap
+    // todo: test flex-direction:column, flex-wrap:wrap
+    // todo: test flex-direction:column-reverse, flex-wrap:wrap
+    // todo: test flex-direction:column, flex-wrap:wrap-reverse
+    // todo: test flex-direction:column-reverse, flex-wrap:wrap-reverse
+    // todo: test child visibility:collapse
+    // todo: test child visibility:hidden
+    // todo: test child position:relative
+    // todo: test child position:absolute
+    // todo: test child position:fixed
+    // todo: test child position:sticky
+    // todo: test justify-content: left, right, start, end, flex-start, flex-end, center, space-around, space-between, space-evenly
+    // todo: test align-items: stretch, normal, left, right, start, end, flex-start, flex-end, center, baseline, first baseline, last baseline
+    // todo: test column-gap, row-gap
     // todo: test padding-left,right,top,bottom percentage uses container's width
     // todo: test margin-left,right,top,bottom percentage uses container's width
   end;

+ 286 - 26
tests/base/TCFlowLayout.pas

@@ -5,7 +5,7 @@ unit TCFlowLayout;
 interface
 
 uses
-  Classes, SysUtils, testregistry, TCFresnelCSS, Fresnel.Controls, Fresnel.DOM;
+  Classes, SysUtils, testregistry, TCFresnelCSS, Fresnel.Controls, Fresnel.DOM, Fresnel.Classes;
 
 type
 
@@ -14,10 +14,13 @@ type
   TTestFlowLayout = class(TCustomTestFresnelCSS)
   published
     procedure TestFlowLayout_BodyDiv;
+    procedure TestFlowLayout_Slider_WithoutRangePoint;
     procedure TestFlowLayout_SliderRangePoint;
-    // todo: test padding-left,right,top,bottom percentage uses container's width
-    // todo: test margin-left,right,top,bottom percentage uses container's width
-    // todo: test div position:absolute, child div: margin-left: 10%; width: 20px
+
+    procedure TestMarginPercentage;
+    procedure TestPaddingPercentage;
+    procedure TestPositionAbsolute_Right_WidthAuto;
+    // todo: test break line
   end;
 
 
@@ -29,6 +32,7 @@ procedure TTestFlowLayout.TestFlowLayout_BodyDiv;
 var
   Body: TBody;
   Div1: TDiv;
+  r: TFresnelRect;
 begin
   Body:=TBody.Create(Viewport);
   Body.Name:='Body';
@@ -43,13 +47,28 @@ begin
   ]);
 
   Viewport.Draw;
+
+  // body
+  AssertEquals('Body.Rendered',true,Body.Rendered);
+  AssertEquals('Body.GetComputedString(fcaBoxSizing)','content-box',Body.GetComputedString(fcaBoxSizing));
+  AssertEquals('Body.GetComputedString(fcaDisplay)','block',Body.GetComputedString(fcaDisplay));
+  AssertEquals('Body.GetComputedString(fcaFloat)','none',Body.GetComputedString(fcaFloat));
+  AssertEquals('Body.GetComputedString(fcaLineHeight)','normal',Body.GetComputedString(fcaLineHeight));
+  AssertEquals('Body.GetComputedString(fcaPosition)','static',Body.GetComputedString(fcaPosition));
+  AssertEquals('Body.GetComputedString(fcaZIndex)','auto',Body.GetComputedString(fcaZIndex));
+  AssertEquals('Body.GetComputedString(fcaMarginLeft)','8px',Body.GetComputedString(fcaMarginLeft));
+  AssertEquals('Body.GetComputedString(fcaMarginTop)','8px',Body.GetComputedString(fcaMarginTop));
+
+  r:=Body.UsedBorderBox;
+  AssertEquals('Body.UsedBorderBox.Left',8,r.Left);
+  AssertEquals('Body.UsedBorderBox.Top',8,r.Top);
+  AssertEquals('Body.UsedBorderBox.Right',792,r.Right);
+  AssertEquals('Body.UsedBorderBox.Bottom',18,r.Bottom);
+
+  // div1
   AssertEquals('Div1.Rendered',true,Div1.Rendered);
   AssertEquals('Div1.GetComputedString(fcaWidth)','20px',Div1.GetComputedString(fcaWidth));
   AssertEquals('Div1.GetComputedString(fcaHeight)','10px',Div1.GetComputedString(fcaHeight));
-  AssertEquals('Div1.GetComputedString(fcaLeft)','0px',Div1.GetComputedString(fcaLeft));
-  AssertEquals('Div1.GetComputedString(fcaTop)','0px',Div1.GetComputedString(fcaTop));
-  AssertEquals('Div1.GetComputedString(fcaRight)','20px',Div1.GetComputedString(fcaRight));
-  AssertEquals('Div1.GetComputedString(fcaBottom)','10px',Div1.GetComputedString(fcaBottom));
 
   AssertEquals('Div1.GetComputedString(fcaBoxSizing)','content-box',Div1.GetComputedString(fcaBoxSizing));
   AssertEquals('Div1.GetComputedString(fcaDisplay)','block',Div1.GetComputedString(fcaDisplay));
@@ -58,20 +77,42 @@ begin
   AssertEquals('Div1.GetComputedString(fcaPosition)','static',Div1.GetComputedString(fcaPosition));
   AssertEquals('Div1.GetComputedString(fcaZIndex)','auto',Div1.GetComputedString(fcaZIndex));
 
-  AssertEquals('Body.GetComputedString(fcaBoxSizing)','content-box',Body.GetComputedString(fcaBoxSizing));
-  AssertEquals('Body.GetComputedString(fcaDisplay)','block',Body.GetComputedString(fcaDisplay));
-  AssertEquals('Body.GetComputedString(fcaFloat)','none',Body.GetComputedString(fcaFloat));
-  AssertEquals('Body.GetComputedString(fcaLineHeight)','normal',Body.GetComputedString(fcaLineHeight));
-  AssertEquals('Body.GetComputedString(fcaPosition)','static',Body.GetComputedString(fcaPosition));
-  AssertEquals('Body.GetComputedString(fcaZIndex)','auto',Body.GetComputedString(fcaZIndex));
+  r:=Div1.UsedBorderBox;
+  AssertEquals('Div1.UsedBorderBox.Left',0,r.Left);
+  AssertEquals('Div1.UsedBorderBox.Top',0,r.Top);
+  AssertEquals('Div1.UsedBorderBox.Right',20,r.Right);
+  AssertEquals('Div1.UsedBorderBox.Bottom',10,r.Bottom);
+end;
+
+procedure TTestFlowLayout.TestFlowLayout_Slider_WithoutRangePoint;
+var
+  Body: TBody;
+  SliderDiv: TDiv;
+begin
+  Body:=TBody.Create(Viewport);
+  Body.Name:='Body';
+  Body.Parent:=Viewport;
 
-  AssertEquals('Body.GetComputedString(fcaLeft)','0px',Body.GetComputedString(fcaLeft));
-  AssertEquals('Body.GetComputedString(fcaTop)','0px',Body.GetComputedString(fcaTop));
-  AssertEquals('Body.GetComputedString(fcaRight)','800px',Body.GetComputedString(fcaRight));
-  AssertEquals('Body.GetComputedString(fcaBottom)','10px',Body.GetComputedString(fcaBottom));
-  AssertEquals('Body.GetComputedString(fcaWidth)','800px',Body.GetComputedString(fcaWidth));
-  AssertEquals('Body.GetComputedString(fcaHeight)','10px',Body.GetComputedString(fcaHeight));
+  SliderDiv:=TDiv.Create(Viewport);
+  SliderDiv.Name:='SliderDiv';
+  SliderDiv.Parent:=Body;
 
+  Viewport.Stylesheet.Text:=LinesToStr([
+    '#SliderDiv {',
+    '  margin: 7px 0 5px;',
+    '  position: relative;',
+    '  border: 1px solid #5080e0;',
+    '  height: 9px;',
+    '  width: 100%;',
+    '}']);
+
+  Viewport.Draw;
+  AssertEquals('SliderDiv.Rendered',true,SliderDiv.Rendered);
+  AssertEquals('SliderDiv.GetComputedString(fcaPosition)','relative',SliderDiv.GetComputedString(fcaPosition));
+  AssertEquals('SliderDiv.RenderedBorderBox.Left',0,SliderDiv.RenderedBorderBox.Left);
+  AssertEquals('SliderDiv.RenderedBorderBox.Top',7,SliderDiv.RenderedBorderBox.Top);
+  AssertEquals('SliderDiv.RenderedBorderBox.Right',786,SliderDiv.RenderedBorderBox.Right);
+  AssertEquals('SliderDiv.RenderedBorderBox.Bottom',18,SliderDiv.RenderedBorderBox.Bottom);
 end;
 
 procedure TTestFlowLayout.TestFlowLayout_SliderRangePoint;
@@ -109,6 +150,7 @@ begin
     '  z-index: 1;',
     '  font-size: .7em;',
     '  border: 0;',
+    '  background-color: #5ca0cc;',
     '  top: 0;',
     '  height: 100%;',
     '  width: 50%;',
@@ -120,27 +162,245 @@ begin
     '  height: 15px;',
     '  border: 1px solid #385590;',
     '  border-radius: 50%;',
+    '  background-color: #fff;',
     '  top: -.3em;',
     '  margin-left: -.6em;',
-    '  left: 51%;',
+    '  left: 50%;',
     '}']);
 
   Viewport.Draw;
   AssertEquals('SliderDiv.Rendered',true,SliderDiv.Rendered);
+
+  // first check computed values
   AssertEquals('SliderDiv.GetComputedString(fcaPosition)','relative',SliderDiv.GetComputedString(fcaPosition));
-  AssertEquals('SliderDiv.RenderedBorderBox.Left',0,SliderDiv.RenderedBorderBox.Left);
-  AssertEquals('SliderDiv.RenderedBorderBox.Top',7,SliderDiv.RenderedBorderBox.Top);
-  AssertEquals('SliderDiv.RenderedBorderBox.Right',802,SliderDiv.RenderedBorderBox.Right);
-  AssertEquals('SliderDiv.RenderedBorderBox.Bottom',18,SliderDiv.RenderedBorderBox.Bottom);
+  AssertEquals('SliderDiv.GetComputedString(fcaWidth)','100%',SliderDiv.GetComputedString(fcaWidth));
+  AssertEquals('SliderDiv.GetComputedString(fcaHeight)','9px',SliderDiv.GetComputedString(fcaHeight));
+  AssertEquals('SliderDiv.GetComputedString(fcaMarginTop)','7px',SliderDiv.GetComputedString(fcaMarginTop));
+  AssertEquals('SliderDiv.GetComputedString(fcaMarginRight)','0',SliderDiv.GetComputedString(fcaMarginRight));
+  AssertEquals('SliderDiv.GetComputedString(fcaMarginBottom)','5px',SliderDiv.GetComputedString(fcaMarginBottom));
+  AssertEquals('SliderDiv.GetComputedString(fcaMarginLeft)','0',SliderDiv.GetComputedString(fcaMarginLeft));
 
   AssertEquals('RangeDiv.GetComputedString(fcaDisplay)','block',RangeDiv.GetComputedString(fcaDisplay));
   AssertEquals('RangeDiv.GetComputedString(fcaPosition)','absolute',RangeDiv.GetComputedString(fcaPosition));
   AssertEquals('RangeDiv.GetComputedString(fcaZIndex)','1',RangeDiv.GetComputedString(fcaZIndex));
+  AssertEquals('RangeDiv.Font.GetSize',7,RangeDiv.Font.GetSize);
+  AssertEquals('RangeDiv.GetComputedString(fcaHeight)','100%',RangeDiv.GetComputedString(fcaHeight));
+  AssertEquals('RangeDiv.GetComputedString(fcaWidth)','50%',RangeDiv.GetComputedString(fcaWidth));
 
-  AssertEquals('PointDiv.Rendered',true,PointDiv.Rendered);
   AssertEquals('PointDiv.GetComputedString(fcaPosition)','absolute',PointDiv.GetComputedString(fcaPosition));
   AssertEquals('PointDiv.GetComputedString(fcaZIndex)','2',PointDiv.GetComputedString(fcaZIndex));
+  AssertEquals('PointDiv.GetComputedString(fcaWidth)','15px',PointDiv.GetComputedString(fcaWidth));
+  AssertEquals('PointDiv.GetComputedString(fcaHeight)','15px',PointDiv.GetComputedString(fcaHeight));
+  AssertEquals('PointDiv.GetComputedString(fcaTop)','-0.3em',PointDiv.GetComputedString(fcaTop));
+  AssertEquals('PointDiv.GetComputedString(fcaLeft)','50%',PointDiv.GetComputedString(fcaLeft));
+  AssertEquals('PointDiv.GetComputedString(fcaMarginLeft)','-0.6em',PointDiv.GetComputedString(fcaMarginLeft));
+
+  // then check layout values
+  //writeln('TTestFlowLayout.TestFlowLayout_SliderRangePoint Body.RenderedBorderBox=',Body.RenderedBorderBox.ToString);
+  AssertEquals('Body.RenderedBorderBox.Left',8,Body.RenderedBorderBox.Left);
+  AssertEquals('Body.RenderedBorderBox.Top',8,Body.RenderedBorderBox.Top);
+  AssertEquals('Body.RenderedContentBox.Width',784,Body.RenderedContentBox.Width);
+  AssertEquals('Body.RenderedBorderBox.Right',792,Body.RenderedBorderBox.Right); // 800-8
+  AssertEquals('Body.RenderedBorderBox.Bottom',31,Body.RenderedBorderBox.Bottom); // 8(body margin)+Slider:7(margin)+1(border)+9(height)+1(border)+5(margin)
+
+  //writeln('TTestFlowLayout.TestFlowLayout_SliderRangePoint SliderDiv.RenderedBorderBox=',SliderDiv.RenderedBorderBox.ToString);
+  AssertEquals('SliderDiv.RenderedBorderBox.Left',0,SliderDiv.RenderedBorderBox.Left);
+  AssertEquals('SliderDiv.RenderedBorderBox.Top',7,SliderDiv.RenderedBorderBox.Top);
+  AssertEquals('SliderDiv.RenderedBorderBox.Right',786,SliderDiv.RenderedBorderBox.Right); // 784(width:100%) + 2*1(border) + 0(margin)
+  AssertEquals('SliderDiv.RenderedBorderBox.Bottom',18,SliderDiv.RenderedBorderBox.Bottom);
+  AssertEquals('SliderDiv.RenderedContentBox.Width',784,SliderDiv.RenderedContentBox.Width);
+  AssertEquals('SliderDiv.RenderedContentBox.Left',1,SliderDiv.RenderedContentBox.Left);
+  AssertEquals('SliderDiv.RenderedContentBox.Top',8,SliderDiv.RenderedContentBox.Top);
+  AssertEquals('SliderDiv.RenderedContentBox.Right',785,SliderDiv.RenderedContentBox.Right);
+  AssertEquals('SliderDiv.RenderedContentBox.Bottom',17,SliderDiv.RenderedContentBox.Bottom);
+
+  //writeln('TTestFlowLayout.TestFlowLayout_SliderRangePoint RangeDiv.RenderedBorderBox=',RangeDiv.RenderedBorderBox.ToString);
+  AssertEquals('RangeDiv.Rendered',true,RangeDiv.Rendered);
+  AssertEquals('RangeDiv.RenderedBorderBox.Left',0,RangeDiv.RenderedBorderBox.Left);
+  AssertEquals('RangeDiv.RenderedBorderBox.Top',0,RangeDiv.RenderedBorderBox.Top);
+  AssertEquals('RangeDiv.RenderedBorderBox.Right',392,RangeDiv.RenderedBorderBox.Right);
+  AssertEquals('RangeDiv.RenderedBorderBox.Bottom',9,RangeDiv.RenderedBorderBox.Bottom);
+  AssertEquals('RangeDiv.RenderedContentBox.Left',0,RangeDiv.RenderedContentBox.Left);
+  AssertEquals('RangeDiv.RenderedContentBox.Top',0,RangeDiv.RenderedContentBox.Top);
+  AssertEquals('RangeDiv.RenderedContentBox.Right',392,RangeDiv.RenderedContentBox.Right);
+  AssertEquals('RangeDiv.RenderedContentBox.Bottom',9,RangeDiv.RenderedContentBox.Bottom);
+
+  AssertEquals('PointDiv.Rendered',true,PointDiv.Rendered);
+  AssertEquals('PointDiv.LayoutNode.MarginLeft',-6,PointDiv.LayoutNode.MarginLeft);
+  AssertEquals('PointDiv.LayoutNode.Width',15,PointDiv.LayoutNode.Width);
+  AssertEquals('PointDiv.LayoutNode.Height',15,PointDiv.LayoutNode.Height);
+  AssertEquals('PointDiv.LayoutNode.Top',-3,PointDiv.LayoutNode.Top);
+  AssertEquals('PointDiv.LayoutNode.Left',392,PointDiv.LayoutNode.Left);
+  AssertEquals('PointDiv.RenderedBorderBox.Left',386,PointDiv.RenderedBorderBox.Left);
+  AssertEquals('PointDiv.RenderedBorderBox.Top',-3,PointDiv.RenderedBorderBox.Top);
+  AssertEquals('PointDiv.RenderedBorderBox.Right',403,PointDiv.RenderedBorderBox.Right);
+  AssertEquals('PointDiv.RenderedBorderBox.Bottom',14,PointDiv.RenderedBorderBox.Bottom);
+  AssertEquals('PointDiv.RenderedContentBox.Left',387,PointDiv.RenderedContentBox.Left);
+  AssertEquals('PointDiv.RenderedContentBox.Top',-2,PointDiv.RenderedContentBox.Top);
+  AssertEquals('PointDiv.RenderedContentBox.Right',402,PointDiv.RenderedContentBox.Right);
+  AssertEquals('PointDiv.RenderedContentBox.Bottom',13,PointDiv.RenderedContentBox.Bottom);
+end;
+
+procedure TTestFlowLayout.TestMarginPercentage;
+var
+  Body: TBody;
+  Div1: TDiv;
+begin
+  Body:=TBody.Create(Viewport);
+  Body.Name:='Body';
+  Body.Parent:=Viewport;
+
+  Div1:=TDiv.Create(Viewport);
+  Div1.Name:='Div1';
+  Div1.Parent:=Body;
+
+  Viewport.Stylesheet.Text:=LinesToStr([
+    'body {',
+    '  margin: 0;',
+    '}',
+    'div {',
+    '  margin: 5% 10% 15% 20%;',
+    '  height: 10px;',
+    '}']);
+
+  Viewport.Draw;
+  AssertEquals('Body.RenderedContentBox.Width',800,Body.RenderedContentBox.Width);
+
+  AssertEquals('Div1.Rendered',true,Div1.Rendered);
+
+  //writeln('TTestFlowLayout.TestPaddingPercentage ',Div1.RenderedBorderBox.ToString);
+  AssertEquals('Div1.LayoutNode.MarginLeft',160,Div1.LayoutNode.MarginLeft);
+  AssertEquals('Div1.LayoutNode.MarginTop',40,Div1.LayoutNode.MarginTop);
+  AssertEquals('Div1.LayoutNode.MarginRight',80,Div1.LayoutNode.MarginRight);
+  AssertEquals('Div1.LayoutNode.MarginBottom',120,Div1.LayoutNode.MarginBottom);
+  AssertEquals('Div1.RenderedBorderBox.Left',160,Div1.RenderedBorderBox.Left);
+  AssertEquals('Div1.RenderedBorderBox.Top',40,Div1.RenderedBorderBox.Top);
+  AssertEquals('Div1.RenderedBorderBox.Right',720,Div1.RenderedBorderBox.Right);
+  AssertEquals('Div1.RenderedBorderBox.Bottom',50,Div1.RenderedBorderBox.Bottom);
+end;
+
+procedure TTestFlowLayout.TestPaddingPercentage;
+var
+  Body: TBody;
+  Div1: TDiv;
+begin
+  Body:=TBody.Create(Viewport);
+  Body.Name:='Body';
+  Body.Parent:=Viewport;
+
+  Div1:=TDiv.Create(Viewport);
+  Div1.Name:='Div1';
+  Div1.Parent:=Body;
+
+  Viewport.Stylesheet.Text:=LinesToStr([
+    'body {',
+    '  margin: 0;',
+    '}',
+    'div {',
+    '  padding: 5% 10% 15% 20%;',
+    '  height: 10px;',
+    '}']);
+
+  Viewport.Draw;
+  AssertEquals('Body.RenderedContentBox.Width',800,Body.RenderedContentBox.Width);
+
+  AssertEquals('Div1.Rendered',true,Div1.Rendered);
+
+  //writeln('TTestFlowLayout.TestPaddingPercentage ',Div1.RenderedBorderBox.ToString);
+  AssertEquals('Div1.LayoutNode.PaddingLeft',160,Div1.LayoutNode.PaddingLeft);
+  AssertEquals('Div1.LayoutNode.PaddingTop',40,Div1.LayoutNode.PaddingTop);
+  AssertEquals('Div1.LayoutNode.PaddingRight',80,Div1.LayoutNode.PaddingRight);
+  AssertEquals('Div1.LayoutNode.PaddingBottom',120,Div1.LayoutNode.PaddingBottom);
+  AssertEquals('Div1.RenderedContentBox.Left',160,Div1.RenderedContentBox.Left);
+  AssertEquals('Div1.RenderedContentBox.Top',40,Div1.RenderedContentBox.Top);
+  AssertEquals('Div1.RenderedContentBox.Right',720,Div1.RenderedContentBox.Right);
+  AssertEquals('Div1.RenderedContentBox.Bottom',50,Div1.RenderedContentBox.Bottom);
+end;
+
+procedure TTestFlowLayout.TestPositionAbsolute_Right_WidthAuto;
+var
+  Body: TBody;
+  Div1, Div2, Div3: TDiv;
+begin
+  Body:=TBody.Create(Viewport);
+  Body.Name:='Body';
+  Body.Parent:=Viewport;
+
+  Div1:=TDiv.Create(Viewport);
+  Div1.Name:='Div1';
+  Div1.Parent:=Body;
+
+  Div2:=TDiv.Create(Viewport);
+  Div2.Name:='Div2';
+  Div2.Parent:=Div1;
+
+  Div3:=TDiv.Create(Viewport);
+  Div3.Name:='Div3';
+  Div3.Parent:=Div2;
+
+  Viewport.Stylesheet.Text:=LinesToStr([
+    'body {',
+    '  margin: 0;', // content width = 800
+    '}',
+    '#Div1 {',
+    '  position: absolute;',
+    '  height: 120px;', // content height = 120
+    '  right: 30px;',
+    // content-width is on top-down run 0, and on second run max-content, which is width of Div3, so 100px
+    '}',
+    '#Div2 {',
+    '  margin: 10%;', // 10% of 0 on top-down run!
+    '  padding: 10%;', // 10% of 0 on top-down run!
+    '  box-sizing: border-box;',
+    '  width: 80%;', // 30% of 360 = 108, content width = 108 - 2*padding = 88
+    '  height: 60%;', // same as width 30%, content height = 88
+    '}',
+    '#Div3 {',
+    '  width: 100px;',
+    '  height: 30px;',
+    '}']);
+
+  Viewport.Draw;
+  AssertEquals('Body.RenderedContentBox.Width',800,Body.RenderedContentBox.Width);
+
+  AssertEquals('Div3.Rendered',true,Div3.Rendered);
+
+  //writeln('TTestFlowLayout.TestPositionAbsolute_Right_WidthAuto Div3.RenderedBorderBox=',Div3.RenderedBorderBox.ToString);
+  AssertEquals('Div3.LayoutNode.Width',100,Div3.LayoutNode.Width);
+  AssertEquals('Div3.LayoutNode.Height',30,Div3.LayoutNode.Height);
+
+  //writeln('TTestFlowLayout.TestPositionAbsolute_Right_WidthAuto Div1.RenderedBorderBox=',Div1.RenderedBorderBox.ToString);
+  AssertEquals('Div1.GetComputedString(fcaPosition)','absolute',Div1.GetComputedString(fcaPosition));
+  AssertEquals('Div1.GetComputedString(fcaBoxSizing)','content-box',Div1.GetComputedString(fcaBoxSizing));
+  AssertEquals('Div1.GetComputedString(fcaWidth)','auto',Div1.GetComputedString(fcaWidth));
+  AssertEquals('Div1.LayoutNode.Height',120,Div1.LayoutNode.Height);
+  AssertEquals('Div1.LayoutNode.Width',100,Div1.LayoutNode.Width);
+  AssertEquals('Div1.LayoutNode.Right',30,Div1.LayoutNode.Right);
+  AssertEquals('Div1.LayoutNode.Left',670,Div1.LayoutNode.Left); // 800-30-100 = 670
 
+  //writeln('TTestFlowLayout.TestPositionAbsolute_Right_WidthAuto Div2.RenderedBorderBox=',Div2.RenderedBorderBox.ToString);
+  AssertEquals('Div2.GetComputedString(fcaPosition)','static',Div2.GetComputedString(fcaPosition));
+  AssertEquals('Div2.GetComputedString(fcaBoxSizing)','border-box',Div2.GetComputedString(fcaBoxSizing));
+  AssertEquals('Div2.GetComputedString(fcaWidth)','80%',Div2.GetComputedString(fcaWidth));
+  AssertEquals('Div2.GetComputedString(fcaHeight)','60%',Div2.GetComputedString(fcaHeight));
+  AssertEquals('Div2.GetComputedString(fcaMarginLeft)','10%',Div2.GetComputedString(fcaMarginLeft));
+  AssertEquals('Div2.GetComputedString(fcaMarginTop)','10%',Div2.GetComputedString(fcaMarginTop));
+  AssertEquals('Div2.GetComputedString(fcaMarginRight)','10%',Div2.GetComputedString(fcaMarginRight));
+  AssertEquals('Div2.GetComputedString(fcaMarginBottom)','10%',Div2.GetComputedString(fcaMarginBottom));
+  AssertEquals('Div2.GetComputedString(fcaPaddingLeft)','10%',Div2.GetComputedString(fcaPaddingLeft));
+  AssertEquals('Div2.GetComputedString(fcaPaddingTop)','10%',Div2.GetComputedString(fcaPaddingTop));
+  AssertEquals('Div2.GetComputedString(fcaPaddingRight)','10%',Div2.GetComputedString(fcaPaddingRight));
+  AssertEquals('Div2.GetComputedString(fcaPaddingBottom)','10%',Div2.GetComputedString(fcaPaddingBottom));
+  AssertEquals('Div2.LayoutNode.MarginLeft',10,Div2.LayoutNode.MarginLeft); // 10% of Div2.width 100
+  AssertEquals('Div2.LayoutNode.MarginRight',10,Div2.LayoutNode.MarginRight);
+  AssertEquals('Div2.LayoutNode.MarginTop',10,Div2.LayoutNode.MarginTop); // 10% of Div2.width 100
+  AssertEquals('Div2.LayoutNode.MarginBottom',10,Div2.LayoutNode.MarginBottom);
+  AssertEquals('Div2.LayoutNode.PaddingLeft',10,Div2.LayoutNode.PaddingLeft); // 10% of Div2.width 100
+  AssertEquals('Div2.LayoutNode.PaddingRight',10,Div2.LayoutNode.PaddingRight);
+  AssertEquals('Div2.LayoutNode.PaddingTop',10,Div2.LayoutNode.PaddingTop); // 10% of Div2.width 100
+  AssertEquals('Div2.LayoutNode.PaddingBottom',10,Div2.LayoutNode.PaddingBottom);
+  AssertEquals('Div2.LayoutNode.Width',60,Div2.LayoutNode.Width);
+  AssertEquals('Div2.LayoutNode.Height',52,Div2.LayoutNode.Height);
 end;
 
 Initialization

+ 4 - 1
tests/base/TCFresnelCSS.pas

@@ -137,6 +137,10 @@ type
     //procedure Test_MarginInline_AsString; // todo
     procedure Test_Padding_AsString;
     procedure Test_BackgroundPosition_AsString;
+    // todo: Test_Gap_AsString
+    // todo: Test_PlaceContent_AsString
+    // todo: Test_PlaceItems_AsString
+    // todo: Test_PlaceSelf_AsString
 
     procedure TestVar_NoDefault;
     procedure TestVar_Initial;
@@ -549,7 +553,6 @@ begin
     LayoutQueued:=false;
     ApplyCSS;
     //Layouter.WriteLayoutTree;
-    Layouter.Apply(Self);
   end;
   Renderer.Draw(Self);
 end;

Some files were not shown because too many files changed in this diff