Browse Source

scroll: pass overflow to parents

mattias 4 months ago
parent
commit
ff04a8f81b

+ 22 - 16
src/base/fresnel.controls.pas

@@ -81,7 +81,7 @@ type
     procedure DoRender(aRenderer: IFresnelRenderer); override;
   public
     function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength=NaN;
-      aMaxHeight: TFresnelLength=NaN): TFresnelPoint; override;
+      aMaxHeight: TFresnelLength=NaN): TFreIntrinsicContentSize; override;
     property Caption: TFresnelCaption read FCaption write SetCaption;
   end;
 
@@ -167,7 +167,7 @@ type
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
     function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength=NaN;
-      aMaxHeight: TFresnelLength=NaN): TFresnelPoint; override;
+      aMaxHeight: TFresnelLength=NaN): TFreIntrinsicContentSize; override;
     Property Image : TImageData Read FImage Write SetImage;
   end;
 
@@ -369,22 +369,28 @@ begin
 end;
 
 function TCustomImage.GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength;
-  aMaxHeight: TFresnelLength): TFresnelPoint;
+  aMaxHeight: TFresnelLength): TFreIntrinsicContentSize;
+var
+  W, H: TFresnelLength;
 begin
+  Result:=Default(TFreIntrinsicContentSize);
   if FImage=nil then
-    exit(Default(TFresnelPoint));
+    exit;
   case aMode of
   flmMinWidth,flmMinHeight:
-    exit(Default(TFresnelPoint));
+    exit;
   flmMax:
     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);
+      W:=FImage.Width;
+      H:=FImage.Height;
+      if (W>0) and (H>0) then
+      begin
+        if (not IsNan(aMaxWidth)) and (W>aMaxWidth) and (aMaxWidth>=0) then
+          H:=H*(aMaxWidth/W);
+        if (not IsNan(aMaxHeight)) and (H>aMaxHeight) and (aMaxHeight>=0) then
+          W:=W*(aMaxHeight/H);
+      end;
+      Result.SetSize(W,H);
     end;
   end;
 end;
@@ -515,7 +521,7 @@ begin
 end;
 
 function TCustomLabel.GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength;
-  aMaxHeight: TFresnelLength): TFresnelPoint;
+  aMaxHeight: TFresnelLength): TFreIntrinsicContentSize;
 begin
   GetFont;
 
@@ -535,7 +541,7 @@ begin
           FMinWidthSize:=Font.TextSizeMaxWidth(FCaption,FMinWidthSize.X);
         Include(FLabelStates,flsMinWidthValid);
       end;
-      Result:=FMinWidthSize;
+      Result.SetSize(FMinWidthSize);
     end;
   flmMax,flmMinHeight:
     begin
@@ -546,7 +552,7 @@ begin
       end;
 
       if IsNan(aMaxWidth) or (aMaxWidth<0) or (FMaxWidthSize.X<aMaxWidth) then
-        Result:=FMaxWidthSize
+        Result.SetSize(FMaxWidthSize)
       else begin
         if (not (flsLastSizeValid in FLabelStates)) or IsNan(FLastMax.X) then
         begin
@@ -554,7 +560,7 @@ begin
           FLastMax.Y:=NaN;
           FLastSize:=Font.TextSizeMaxWidth(FCaption,aMaxWidth);
         end;
-        Result:=FLastSize;
+        Result.SetSize(FLastSize);
       end;
     end;
   end;

+ 48 - 17
src/base/fresnel.dom.pas

@@ -1072,6 +1072,19 @@ type
     );
   TFresnelLayoutFlags = set of TFresnelLayoutFlag;
 
+  { TFreIntrinsicContentSize }
+
+  TFreIntrinsicContentSize = record
+    Width: TFresnelLength;
+    Height: TFresnelLength;
+    ScrollWidth: TFresnelLength;
+    ScrollHeight: TFresnelLength;
+    NeedGutterHorizontal, NeedGutterVertical: boolean;
+    function ToString: string;
+    procedure SetSize(const p: TFresnelPoint);
+    procedure SetSize(W, H: TFresnelLength);
+  end;
+
   { TFresnelLayoutNode }
 
   TFresnelLayoutNode = class(TComponent)
@@ -1094,13 +1107,6 @@ type
       gmAlways, // gutter always present
       gmAuto // gutter allocated if needed
       );
-    TContentSize = record
-      Width: TFresnelLength;
-      Height: TFresnelLength;
-      ScrollWidth: TFresnelLength;
-      ScrollHeight: TFresnelLength;
-      NeedGutterHorizontal, NeedGutterVertical: boolean;
-    end;
   public
     Container: TFresnelElement;
     SkipLayout: boolean; // e.g. element or ancestor display:none or visibility=collapse
@@ -1162,7 +1168,7 @@ type
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
     function GetIntrinsicContentSize(aMode: TFresnelLayoutMode;
-      aMaxWidth: TFresnelLength = NaN; aMaxHeight: TFresnelLength = NaN): TContentSize; virtual; // ignoring min|max-height
+      aMaxWidth: TFresnelLength = NaN; aMaxHeight: TFresnelLength = NaN): TFreIntrinsicContentSize; virtual; // ignoring min|max-height
     function GetRoot: TFresnelLayoutNode;
     function FitWidth(aWidth: TFresnelLength): TFresnelLength;
     function FitHeight(aHeight: TFresnelLength): TFresnelLength;
@@ -1502,7 +1508,7 @@ type
     property ComputedVisibility: TCSSNumericalID read FComputedVisibility;
     property ComputedWritingMode: TCSSNumericalID read FComputedWritingMode;
     // Layouter
-    function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength = NaN; aMaxHeight: TFresnelLength = NaN): TFresnelPoint; virtual; // ignoring min|max-height
+    function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength = NaN; aMaxHeight: TFresnelLength = NaN): TFreIntrinsicContentSize; virtual; // ignoring min|max-height
     function GetContainerContentWidth(UseNaNOnFail: boolean): TFresnelLength; virtual; // used value
     function GetContainerContentHeight(UseNaNOnFail: boolean): TFresnelLength; virtual; // used value
     function GetContainerOffset: TFresnelPoint;
@@ -6520,9 +6526,9 @@ begin
 end;
 
 function TFresnelLayoutNode.GetIntrinsicContentSize(aMode: TFresnelLayoutMode;
-  aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength): TContentSize;
+  aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength): TFreIntrinsicContentSize;
 begin
-  Result:=Default(TContentSize);
+  Result:=Default(TFreIntrinsicContentSize);
   if aMode=flmMax then ;
   if IsNan(aMaxWidth) then ;
   if IsNan(aMaxHeight) then ;
@@ -6618,6 +6624,35 @@ begin
 end;
 {$ENDIF}
 
+{ TFreIntrinsicContentSize }
+
+function TFreIntrinsicContentSize.ToString: string;
+begin
+  Result:='Size='+FloatToCSSStr(Width)
+    +'x'+FloatToCSSStr(Height)
+    +' ScrollSize='+FloatToCSSStr(ScrollWidth)
+    +'x'+FloatToCSSStr(ScrollHeight);
+  if NeedGutterHorizontal then
+    Result+=' NeedGutterHorz';
+  if NeedGutterVertical then
+    Result+=' NeedGutterVert';
+end;
+
+procedure TFreIntrinsicContentSize.SetSize(const p: TFresnelPoint);
+begin
+  SetSize(p.X,p.Y);
+end;
+
+procedure TFreIntrinsicContentSize.SetSize(W, H: TFresnelLength);
+begin
+  Width:=W;
+  Height:=H;
+  ScrollWidth:=W;
+  ScrollHeight:=H;
+  NeedGutterHorizontal:=false;
+  NeedGutterVertical:=false;
+end;
+
 { TPseudoElScrollBar }
 
 procedure TPseudoElScrollBar.SetParent(const AValue: TFresnelElement);
@@ -8610,13 +8645,9 @@ begin
 end;
 
 function TFresnelElement.GetIntrinsicContentSize(aMode: TFresnelLayoutMode;
-  aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength): TFresnelPoint;
-var
-  aSize: TFresnelLayoutNode.TContentSize;
+  aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength): TFreIntrinsicContentSize;
 begin
-  aSize:=LayoutNode.GetIntrinsicContentSize(aMode,aMaxWidth,aMaxHeight);
-  Result.X:=aSize.Width;
-  Result.Y:=aSize.Height;
+  Result:=LayoutNode.GetIntrinsicContentSize(aMode,aMaxWidth,aMaxHeight);
 end;
 
 function TFresnelElement.GetContainerContentWidth(UseNaNOnFail: boolean): TFresnelLength;

+ 13 - 11
src/base/fresnel.edit.pp

@@ -101,7 +101,8 @@ Type
     class function GetCSSTypeStyle: TCSSString; override;
   public
     constructor Create(aOwner : TComponent); override;
-    function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength): TFresnelPoint; override;
+    function GetIntrinsicContentSize(aMode: TFresnelLayoutMode;
+      aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength): TFreIntrinsicContentSize; override;
     function CanFocus: Boolean; override;
     procedure DeleteSelection;
     procedure SetSelection(const aStart,aEnd : Integer);
@@ -127,7 +128,7 @@ implementation
 uses math;
 
 function TEdit.GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength;
-  aMaxHeight: TFresnelLength): TFresnelPoint;
+  aMaxHeight: TFresnelLength): TFreIntrinsicContentSize;
 
 var
   lFont : IFresnelFont;
@@ -137,23 +138,23 @@ begin
   lFont:=GetFont;
   // todo writing-mode
   if IsNan(aMaxHeight) then ;
-  lHorzPadding:=GetComputedLength(fcaPaddingLeft)+GetComputedLength(fcaPaddingRight);
-  lVertPadding:=GetComputedLength(fcaPaddingTop)+GetComputedLength(fcaPaddingBottom);
+  lHorzPadding:=LayoutNode.PaddingLeft+LayoutNode.PaddingRight;
+  lVertPadding:=LayoutNode.PaddingTop+LayoutNode.PaddingBottom;
   LSize:=LFont.TextSize('W');
   case aMode of
-    flmMinHeight,flmMax:
+    flmMinHeight:
       begin
       LSize.Offset(lHorzPadding,lVertPadding);
-      Result:=LSize;
+      Result.SetSize(LSize);
       end;
-    flmMinWidth:
+    flmMinWidth,flmMax:
       begin
       if MaxLength<>0 then
         lSize.X:=lSize.X*MaxLength
       else
         LSize.X:=aMaxWidth;
       LSize.Offset(lHorzPadding,lVertPadding);
-      Result:=lSize;
+      Result.SetSize(lSize);
       end;
   end;
 end;
@@ -488,7 +489,7 @@ begin
     Value := aNewName;
 end;
 
-Function TEdit.GetDrawText : TFresnelCaption;
+function TEdit.GetDrawText: TFresnelCaption;
 
 begin
   if FValue='' then
@@ -629,7 +630,7 @@ begin
   // WriteLn(', Cur: [Char:',FCursorPos:2,', Pos:',FCursorX:5:2,']');
 end;
 
-Function TEdit.CalcCharOffset(aXOffset: TFresnelLength) : Integer;
+function TEdit.CalcCharOffset(aXOffset: TFresnelLength): Integer;
 
 var
   lText: string;
@@ -674,7 +675,8 @@ begin
   // Writeln('CalcCharOffset : ',Result);
 end;
 
-Function TEdit.CalcXOffset(aCharPos: Integer; aUseOffset: Boolean = true; aUseDrawText : Boolean = False) : TFresnelLength;
+function TEdit.CalcXOffset(aCharPos: Integer; aUseOffset: Boolean; aUseDrawText: Boolean
+  ): TFresnelLength;
 
 var
   lText: string;

+ 174 - 134
src/base/fresnel.layouter.pas

@@ -37,7 +37,7 @@ type
         Mode: TFresnelLayoutMode;
         WithScroll: boolean;
         MaxWidth, MaxHeight: TFresnelLength;
-        ContentSize: TFresnelLayoutNode.TContentSize;
+        ContentSize: TFreIntrinsicContentSize;
       end;
       TLayoutSizeArray = array of TLayoutSize;
     var
@@ -49,13 +49,13 @@ type
     procedure DeductUsedLengths(NoChildren: boolean); virtual;
     procedure ClearCachedLayoutSizes; virtual;
     function GetCachedLayoutSize(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength;
-      aWithScroll: boolean; out aSize: TFresnelLayoutNode.TContentSize): boolean; virtual;
+      aWithScroll: boolean; out aSize: TFreIntrinsicContentSize): boolean; virtual;
     procedure AddCachedLayoutSize(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength;
-      aWithScroll: boolean; const aSize: TFresnelLayoutNode.TContentSize); virtual;
+      aWithScroll: boolean; const aSize: TFreIntrinsicContentSize); virtual;
     function ComputeLayoutContent(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength;
-      Commit: boolean): TFresnelPoint; virtual; abstract;
+      Commit: boolean): TFreIntrinsicContentSize; virtual; abstract;
     function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength=NaN;
-      aMaxHeight: TFresnelLength=NaN; aWithScroll: boolean = true): TFresnelLayoutNode.TContentSize; virtual;
+      aMaxHeight: TFresnelLength=NaN; aWithScroll: boolean = true): TFreIntrinsicContentSize; virtual;
     function GetViewport: TFresnelViewport;
   end;
   TFLNodeLayouterClass = class of TFLNodeLayouter;
@@ -72,10 +72,10 @@ type
     procedure ComputeScrollbarsNoChildren; virtual;
     procedure GetScrollGutterWidths(Horizontal: boolean; out LT, RB: TFresnelLength);
     procedure AddScrollGutter(Horizontal: boolean);
-    procedure ApplyScrollSize(const aSize: TContentSize);
+    procedure ApplyScrollSize(const aSize: TFreIntrinsicContentSize);
     procedure DeductUsedLengths(NoChildren: boolean); virtual;
     function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength=NaN;
-      aMaxHeight: TFresnelLength=NaN): TContentSize; override;
+      aMaxHeight: TFresnelLength=NaN): TFreIntrinsicContentSize; override;
     destructor Destroy; override;
   end;
 
@@ -95,6 +95,8 @@ type
         StaticTop: TFresnelLength;
         ContentBoxWidth: TFresnelLength;
         ContentBoxHeight: TFresnelLength;
+        IntrinsicWidth: TFresnelLength;
+        IntrinsicHeight: TFresnelLength;
       end;
       TLineItemClass = class of TLineItem;
 
@@ -104,7 +106,7 @@ type
     procedure PlaceLineItems; virtual;
     function PlaceAbsoluteItem(ChildNode: TUsedLayoutNode; aMode: TFresnelLayoutMode;
                   aMaxWidth, aMaxHeight: TFresnelLength; const DefaultPos: TFresnelPoint;
-                  Commit: boolean): TFresnelPoint; virtual;
+                  Commit: boolean): TFreIntrinsicContentSize; virtual;
     function AddAbsoluteItem(ChildNode: TUsedLayoutNode; NodeClass: TLineItemClass): TLineItem; virtual;
     function AddLineItem(ChildNode: TUsedLayoutNode; NodeClass: TLineItemClass): TLineItem; virtual;
     procedure ClearAbsoluteItems; virtual;
@@ -145,7 +147,9 @@ type
     procedure PlaceLineItems; override;
   public
     procedure DeductUsedLengths(NoChildren: boolean); override;
-    function ComputeLayoutContent(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength; Commit: boolean): TFresnelPoint; override;
+    function ComputeLayoutContent(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength; Commit:
+      boolean): TFreIntrinsicContentSize; override;
+    function MergeMargins(a, b: TFresnelLength): TFresnelLength;
   end;
 
   { TFLFlexLayouter }
@@ -179,24 +183,26 @@ type
     MainIsReverse: boolean; // flex-direction is row-reverse or column-reverse
     procedure StartLine; virtual;
     procedure EndLine(MaxMainSize, MainGap: TFresnelLength;
-      Commit: boolean; var NewContentSize: TFresnelPoint); virtual;
+      Commit: boolean; var NewContentSize: TFreIntrinsicContentSize); virtual;
     procedure FlexLineMainDirection(MaxMainSize, MainGap: TFresnelLength; Commit: boolean;
-      var NewContentSize: TFresnelPoint); virtual;
+      var NewContentSize: TFreIntrinsicContentSize); virtual;
     procedure FlexLineCrossDirection(Commit: boolean;
-      var NewContentSize: TFresnelPoint); virtual;
+      var NewContentSize: TFreIntrinsicContentSize); virtual;
     procedure PlaceLineItems; override;
     procedure ComputeChildAttributes(Item: TLineItem; El: TFresnelElement); override;
   public
     procedure Init; override;
     function GetComputedGap(IsHorizontal: boolean): TFresnelLength;
-    function ComputeLayoutContent(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength; Commit: boolean): TFresnelPoint; override;
+    function ComputeLayoutContent(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength;
+      Commit: boolean): TFreIntrinsicContentSize; override;
   end;
 
   { TFLGridLayouter }
 
   TFLGridLayouter = class(TFLNodeLayouter)
   public
-    function ComputeLayoutContent(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength; Commit: boolean): TFresnelPoint; override;
+    function ComputeLayoutContent(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength;
+      Commit: boolean): TFreIntrinsicContentSize; override;
   end;
 
   { TViewportLayouter }
@@ -254,8 +260,7 @@ end;
 procedure TFLNodeLayouter.Apply;
 var
   MaxWidth, MaxHeight, aClientWidth, aClientHeight: TFresnelLength;
-  ContSize: TFresnelLayoutNode.TContentSize;
-  Size: TFresnelPoint;
+  Size: TFreIntrinsicContentSize;
   HasMaxWidth, HasMaxHeight: Boolean;
   El: TFresnelElement;
   aClientBox: TFresnelRect;
@@ -280,10 +285,10 @@ begin
       {$IFDEF VerboseFresnelScrolling}
       writeln('TFLNodeLayouter.Apply "',Node.Element.Name,'" Before Intrinsic: CanScrollX=',Node.CanScrollX,' CanScrollY=',Node.CanScrollY,' MaxSize=',FloatToCSSStr(MaxWidth),'x',FloatToCSSStr(MaxHeight),' GutterHorz=',Node.GutterHorizontal,' GutterVert=',Node.GutterVertical);
       {$ENDIF}
-      ContSize:=GetIntrinsicContentSize(flmMax,MaxWidth,MaxHeight);
-      Node.ApplyScrollSize(ContSize);
+      Size:=GetIntrinsicContentSize(flmMax,MaxWidth,MaxHeight);
+      Node.ApplyScrollSize(Size);
       {$IFDEF VerboseFresnelScrolling}
-      writeln('TFLNodeLayouter.Apply "',Node.Element.Name,'" IntrinsicContentSize: Size=',FloatToCSSStr(ContSize.Width),'x',FloatToCSSStr(ContSize.Height),' ScrollSize=',FloatToCSSStr(ContSize.ScrollWidth),'x',FloatToCSSStr(ContSize.ScrollHeight),' NeedGutterHorz=',ContSize.NeedGutterHorizontal,' Vert=',ContSize.NeedGutterVertical,' Gutter:L=',FloatToCSSStr(Node.ScrollGutterLeft),',R=',FloatToCSSStr(Node.ScrollGutterRight),',T=',FloatToCSSStr(Node.ScrollGutterTop),',B=',FloatToCSSStr(Node.ScrollGutterBottom));
+      writeln('TFLNodeLayouter.Apply "',Node.Element.Name,'" IntrinsicContentSize: Size=',FloatToCSSStr(Size.Width),'x',FloatToCSSStr(Size.Height),' ScrollSize=',FloatToCSSStr(Size.ScrollWidth),'x',FloatToCSSStr(Size.ScrollHeight),' NeedGutterHorz=',Size.NeedGutterHorizontal,' Vert=',Size.NeedGutterVertical,' Gutter:L=',FloatToCSSStr(Node.ScrollGutterLeft),',R=',FloatToCSSStr(Node.ScrollGutterRight),',T=',FloatToCSSStr(Node.ScrollGutterTop),',B=',FloatToCSSStr(Node.ScrollGutterBottom));
       {$ENDIF}
     end;
 
@@ -306,22 +311,22 @@ begin
     {$ENDIF}
     Size:=ComputeLayoutContent(flmMax,aClientWidth,aClientHeight,true);
     {$IFDEF VerboseFresnelScrolling}
-    writeln('TFLNodeLayouter.Apply "',Node.Element.Name,'" ContentSize: ',FloatToCSSStr(Size.X),'x',FloatToCSSStr(Size.Y));
+    writeln('TFLNodeLayouter.Apply "',Node.Element.Name,'" Size: ',FloatToCSSStr(Size.Width),'x',FloatToCSSStr(Size.Height),' Scroll: ',FloatToCSSStr(Size.ScrollWidth),'x',FloatToCSSStr(Size.ScrollWidth));
     {$ENDIF}
     if IsNan(Node.Width) then
     begin
-      Node.Width:=Size.X;
+      Node.Width:=Size.Width;
       if Node.Width>aClientWidth then
         Node.Width:=aClientWidth;
     end;
     if IsNan(Node.Height) then
     begin
-      Node.Height:=Node.FitHeight(Size.Y);
+      Node.Height:=Node.FitHeight(Size.Height);
       if Node.Height>aClientHeight then
         Node.Height:=aClientHeight;
     end;
-    Node.ScrollWidth:=Max(Size.X,aClientWidth);
-    Node.ScrollHeight:=Max(Size.Y,aClientHeight);
+    Node.ScrollWidth:=Max(Size.ScrollWidth,aClientWidth);
+    Node.ScrollHeight:=Max(Size.ScrollHeight,aClientHeight);
     {$IFDEF VerboseFresnelScrolling}
     writeln('TFLNodeLayouter.Apply "',Node.Element.Name,'" ScrollSize: ',FloatToCSSStr(Node.ScrollWidth),'x',FloatToCSSStr(Node.ScrollHeight),' Width=',FloatToCSSStr(Node.Width),' Height=',FloatToCSSStr(Node.Height));
     {$ENDIF}
@@ -343,11 +348,11 @@ begin
     //writeln('TFLNodeLayouter.Apply "',Node.Element.Name,'" no scroll MaxWidth=',FloatToCSSStr(MaxWidth),' MaxHeight=',FloatToCSSStr(MaxHeight));
     Size:=ComputeLayoutContent(flmMax,MaxWidth,MaxHeight,true);
     if IsNan(Node.Width) then
-      Node.Width:=Node.FitWidth(Size.X);
+      Node.Width:=Node.FitWidth(Size.Width);
     if IsNan(Node.Height) then
-      Node.Height:=Node.FitHeight(Size.Y);
-    Node.ScrollWidth:=Max(Size.X,Node.Width);
-    Node.ScrollHeight:=Max(Size.Y,Node.Height);
+      Node.Height:=Node.FitHeight(Size.Height);
+    Node.ScrollWidth:=Max(Size.ScrollWidth,Node.Width);
+    Node.ScrollHeight:=Max(Size.ScrollHeight,Node.Height);
   end;
 
   // clipping
@@ -376,7 +381,7 @@ begin
 end;
 
 function TFLNodeLayouter.GetCachedLayoutSize(aMode: TFresnelLayoutMode; aMaxWidth,
-  aMaxHeight: TFresnelLength; aWithScroll: boolean; out aSize: TFresnelLayoutNode.TContentSize
+  aMaxHeight: TFresnelLength; aWithScroll: boolean; out aSize: TFreIntrinsicContentSize
   ): boolean;
 
   function CheckMax(OldMax, NewMax, OldResult: TFresnelLength): boolean;
@@ -453,12 +458,12 @@ begin
     aSize:=ContentSize;
     exit(true);
   end;
-  aSize:=Default(TFresnelLayoutNode.TContentSize);
+  aSize:=Default(TFreIntrinsicContentSize);
   Result:=false;
 end;
 
 procedure TFLNodeLayouter.AddCachedLayoutSize(aMode: TFresnelLayoutMode; aMaxWidth,
-  aMaxHeight: TFresnelLength; aWithScroll: boolean; const aSize: TFresnelLayoutNode.TContentSize);
+  aMaxHeight: TFresnelLength; aWithScroll: boolean; const aSize: TFreIntrinsicContentSize);
 var
   l: integer;
 begin
@@ -474,24 +479,18 @@ begin
 end;
 
 function TFLNodeLayouter.GetIntrinsicContentSize(aMode: TFresnelLayoutMode;
-  aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength; aWithScroll: boolean): TFresnelLayoutNode.
-  TContentSize;
+  aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength; aWithScroll: boolean):
+  TFreIntrinsicContentSize;
 var
   GutterLeft, GutterRight, GutterTop, GutterBottom: TFresnelLength;
 
   procedure Compute(CurMode: TFresnelLayoutMode; CurMaxWidth, CurMaxHeight: TFresnelLength;
     AddGutter: boolean);
-  var
-    aSize: TFresnelPoint;
   begin
     if not GetCachedLayoutSize(CurMode,CurMaxWidth,CurMaxHeight,false,Result) then
     begin
-      aSize:=ComputeLayoutContent(CurMode,CurMaxWidth,CurMaxHeight,false);
-      Result:=Default(TFresnelLayoutNode.TContentSize);
-      Result.Width:=aSize.X;
-      Result.Height:=aSize.Y;
-      Result.ScrollWidth:=aSize.X;
-      Result.ScrollHeight:=aSize.Y;
+      Result:=ComputeLayoutContent(CurMode,CurMaxWidth,CurMaxHeight,false);
+      Result:=Default(TFreIntrinsicContentSize);
       AddCachedLayoutSize(CurMode,CurMaxWidth,CurMaxHeight,false,Result);
     end;
     if AddGutter then begin
@@ -618,12 +617,7 @@ begin
 
   if not Done then
   begin
-    aSize:=ComputeLayoutContent(aMode,aMaxWidth,aMaxHeight,false);
-    Result:=Default(TFresnelLayoutNode.TContentSize);
-    Result.Width:=aSize.X;
-    Result.Height:=aSize.Y;
-    Result.ScrollWidth:=aSize.X;
-    Result.ScrollHeight:=aSize.Y;
+    Result:=ComputeLayoutContent(aMode,aMaxWidth,aMaxHeight,false);
   end;
   AddCachedLayoutSize(aMode,aMaxWidth,aMaxHeight,aWithScroll,Result);
 end;
@@ -871,7 +865,7 @@ begin
   end;
 end;
 
-procedure TUsedLayoutNode.ApplyScrollSize(const aSize: TContentSize);
+procedure TUsedLayoutNode.ApplyScrollSize(const aSize: TFreIntrinsicContentSize);
 begin
   ScrollWidth:=aSize.ScrollWidth;
   ScrollHeight:=aSize.ScrollHeight;
@@ -986,12 +980,12 @@ begin
 end;
 
 function TUsedLayoutNode.GetIntrinsicContentSize(aMode: TFresnelLayoutMode;
-  aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength): TContentSize;
+  aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength): TFreIntrinsicContentSize;
 begin
   if Layouter<>nil then
     Result:=Layouter.GetIntrinsicContentSize(aMode,aMaxWidth,aMaxHeight)
   else
-    Result:=Default(TContentSize);
+    Result:=Default(TFreIntrinsicContentSize);
 end;
 
 destructor TUsedLayoutNode.Destroy;
@@ -1118,36 +1112,50 @@ begin
 end;
 
 function TFLFlowLayouter.ComputeLayoutContent(aMode: TFresnelLayoutMode; aMaxWidth,
-  aMaxHeight: TFresnelLength; Commit: boolean): TFresnelPoint;
+  aMaxHeight: TFresnelLength; Commit: boolean): TFreIntrinsicContentSize;
 var
   ChildIndex: Integer;
   ChildNode: TUsedLayoutNode;
   ChildEl, El: TFresnelElement;
   IsInline: Boolean;
   NewChildRight, CurSpace, OldMarginBoxBottom,
-    ChildMBoxLeft, ChildMBoxRight { MarginBox relative to parent ContentBox left },
+    ChildMBoxLeft, ChildMBoxRight { MarginBox relative to parent ClientBox left },
     ChildWidth, ChildHeight { ContentBox },
     ChildMinWidth, ChildMinHeight { ContentBox, at least 0 },
     ChildMaxWidth, ChildMaxHeight { ContentBox, can be NaN },
   ChildMarginLeft, ChildMarginRight, ChildMarginTop, ChildMarginBottom,
   ChildPadBorderX, ChildPadBorderY: TFresnelLength;
-  ChildPrefSize, ChildDefPos: TFresnelPoint;
+  ChildPrefSize: TFreIntrinsicContentSize;
+  ChildDefPos: TFresnelPoint;
   AbsItem: TLineItem;
 
   procedure AddLineNodeCache;
   var
     N: TFlowItem;
+    r: TFresnelLength;
   begin
     FLLog(etDebug,['AddLineNodeCache ',ChildEl.GetPath,' L=',FloatToStr(ChildMBoxLeft),',R=',FloatToStr(ChildMBoxRight),',W=',FloatToStr(ChildWidth),',H=',FloatToStr(ChildHeight)]);
     N:=TFlowItem(AddLineItem(ChildNode,TFlowItem));
     N.StaticLeft:=ChildMBoxLeft;
     N.ContentBoxWidth:=ChildWidth;
     N.ContentBoxHeight:=ChildHeight;
-    Result.X:=Max(Result.X,ChildMBoxRight);
+    N.IntrinsicWidth:=Max(ChildWidth,ChildPrefSize.ScrollWidth);
+    N.IntrinsicHeight:=Max(ChildHeight,ChildPrefSize.ScrollHeight);
+    Result.Width:=Max(Result.Width,ChildMBoxRight);
+
+    if Result.ScrollWidth<Result.Width then
+      Result.ScrollWidth:=Result.Width;
+    r:=ChildPrefSize.ScrollWidth-ChildWidth;
+    if r>0 then
+    begin
+      r:=ChildMBoxRight+r;
+      if Result.ScrollWidth<r then
+        Result.ScrollWidth:=r;
+    end;
   end;
 
 begin
-  Result:=default(TFresnelPoint);
+  Result:=default(TFreIntrinsicContentSize);
 
   //writeln('TFLFlowLayouter.ComputeLayoutContent ',Node.Element.GetPath,' ',aMode,' MaxWidth=',FloatToCSSStr(aMaxWidth),' MaxHeight=',FloatToCSSStr(aMaxHeight),' Commit=',Commit);
 
@@ -1223,7 +1231,9 @@ begin
     else if IsNan(aMaxWidth) then
       CurSpace:=NaN
     else
-      CurSpace:=Max(0,aMaxWidth-FLineBorderBoxRight-Max(FLineMarginRight,ChildNode.MarginLeft)-ChildNode.MarginRight);
+      CurSpace:=Max(0,aMaxWidth-FLineBorderBoxRight
+                      -MergeMargins(FLineMarginRight,ChildNode.MarginLeft)
+                      -ChildNode.MarginRight);
 
     //writeln('TFLFlowLayouter.ComputeLayoutContent ',ChildEl.GetPath,' Margin=',FloatToStr(ChildMarginLeft),',',FloatToStr(ChildMarginTop),',',FloatToStr(ChildMarginRight),',',FloatToStr(ChildMarginBottom),' Border=',FloatToStr(ChildBorderLeft),',',FloatToStr(ChildBorderTop),',',FloatToStr(ChildBorderRight),',',FloatToStr(ChildBorderBottom),' Padding=',FloatToStr(ChildPaddingLeft),',',FloatToStr(ChildPaddingTop),',',FloatToStr(ChildPaddingRight),',',FloatToStr(ChildPaddingBottom));
 
@@ -1248,53 +1258,50 @@ begin
       //writeln('TFLFlowLayouter.ComputeLayoutContent BLOCK FULL LINE: ',ChildEl.GetPath,' CurSpace=',FloatToStr(CurSpace),' ChildPadBorderX=',FloatToStr(ChildPadBorderX));
       ChildWidth:=CurSpace-ChildPadBorderX;
     end;
-    if not IsNan(ChildWidth) then
+    if IsNan(ChildWidth) then
     begin
+      if not IsNan(CurSpace) then
+      begin
+        if not IsNan(ChildMaxWidth) then
+          CurSpace:=Min(CurSpace-ChildPadBorderX,ChildMaxWidth);
+        CurSpace:=Max(CurSpace-ChildPadBorderX,ChildMinWidth);
+      end;
+    end else begin
       if not IsNan(ChildMaxWidth) then
         ChildWidth:=Min(ChildWidth,ChildMaxWidth);
       ChildWidth:=Max(ChildWidth,ChildMinWidth);
+      CurSpace:=ChildWidth;
     end;
 
-    if IsNan(ChildWidth) or IsNan(ChildHeight) then
+    if (aMode=flmMax) or IsNan(ChildWidth) or IsNan(ChildHeight) then
     begin
-      // one or both sides are dynamic
-      if IsNan(ChildWidth) then
-      begin
-        if not IsNan(CurSpace) then
-        begin
-          if not IsNan(ChildMaxWidth) then
-            CurSpace:=Min(CurSpace-ChildPadBorderX,ChildMaxWidth);
-          CurSpace:=Max(CurSpace-ChildPadBorderX,ChildMinWidth);
-        end;
-      end else begin
-        CurSpace:=ChildWidth;
-      end;
       ChildPrefSize:=ChildEl.GetIntrinsicContentSize(aMode,CurSpace,NaN);
       //if ChildEl.Name='CaptionLabel' then
-      //  writeln('TFLFlowLayouter.ComputeLayoutContent ',ChildEl.GetPath,' MaxWidth=',CurSpace,' Preferred=',ChildPrefSize.ToString,' ChildPadBorderX=',ChildPadBorderX,' ChildPadBorderY=',ChildPadBorderY);
+        writeln('TFLFlowLayouter.ComputeLayoutContent ',ChildEl.GetPath,' MaxWidth=',FloatToCSSStr(CurSpace),' Preferred=',ChildPrefSize.ToString,' ChildPadBorderX=',FloatToCSSStr(ChildPadBorderX),' ChildPadBorderY=',FloatToCSSStr(ChildPadBorderY));
       // apply min, max
       if IsNan(ChildWidth) then
       begin
-        ChildWidth:=ChildPrefSize.X;
+        ChildWidth:=ChildPrefSize.Width;
         if not IsNan(ChildMaxWidth) then
           ChildWidth:=Min(ChildWidth,ChildMaxWidth);
         ChildWidth:=Max(ChildWidth,ChildMinWidth);
       end;
       if IsNan(ChildHeight) then
       begin
-        ChildHeight:=ChildPrefSize.Y;
+        ChildHeight:=ChildPrefSize.Height;
         if not IsNan(ChildMaxHeight) then
           ChildHeight:=Min(ChildHeight,ChildMaxHeight);
         ChildHeight:=Max(ChildHeight,ChildMinHeight);
       end;
-    end;
+    end else
+      ChildPrefSize:=Default(TFreIntrinsicContentSize);
 
     NewChildRight:=0;
     if (FLineItems.Count>0) and IsInline and (not IsNan(aMaxWidth)) then
     begin
       // check if inline element fits in line
       NewChildRight:=FLineBorderBoxRight
-                     +Max(FLineMarginRight,ChildMarginLeft) // margin collapsing
+                     +MergeMargins(FLineMarginRight,ChildMarginLeft)
                      +ChildWidth+ChildPadBorderX
                      +ChildMarginRight;
       if NewChildRight>aMaxWidth then
@@ -1368,7 +1375,7 @@ begin
   end;
   EndLine(Commit);
 
-  if Commit and (FAbsoluteItems<>nil) then
+  if (FAbsoluteItems<>nil) and (Commit or (aMode=flmMax)) then
   begin
     // place absolute items
     for ChildIndex:=0 to FAbsoluteItems.Count-1 do
@@ -1379,20 +1386,46 @@ begin
       ChildDefPos.X:=AbsItem.StaticLeft;
       ChildDefPos.Y:=AbsItem.StaticTop;
 
-      PlaceAbsoluteItem(ChildNode,aMode,aMaxWidth,aMaxHeight,ChildDefPos,Commit);
+      ChildPrefSize:=PlaceAbsoluteItem(ChildNode,aMode,aMaxWidth,aMaxHeight,ChildDefPos,Commit);
+      if Result.ScrollWidth<ChildPrefSize.ScrollWidth then
+        Result.ScrollWidth:=ChildPrefSize.ScrollWidth;
+      if Result.ScrollHeight<ChildPrefSize.ScrollHeight then
+        Result.ScrollHeight:=ChildPrefSize.ScrollHeight;
     end;
-    ClearAbsoluteItems;
   end;
+  if FAbsoluteItems<>nil then
+    ClearAbsoluteItems;
 
-  Result.Y:=Max(Result.Y,FLastLineBorderBoxBottom+FLastLineMarginBottom);
+  Result.Height:=Max(Result.Height,FLastLineBorderBoxBottom+FLastLineMarginBottom);
+end;
+
+function TFLFlowLayouter.MergeMargins(a, b: TFresnelLength): TFresnelLength;
+begin
+  if a>=0 then
+  begin
+    if b>=a then
+      Result:=b // 0<=a<=b -> maximum b
+    else if b>=0 then
+      Result:=a  // 0<=b<a -> maximum a
+    else
+      Result:=a+b; // a>=0, b<0 -> sum
+  end else begin
+    // a<0
+    if b>=0 then
+      Result:=a+b // a<0, b>=0 -> sum
+    else if b<a then
+      Result:=b // b<a<0
+    else
+      Result:=a; // a<=b<=0 -> minimum a
+  end;
 end;
 
 { TFLGridLayouter }
 
 function TFLGridLayouter.ComputeLayoutContent(aMode: TFresnelLayoutMode; aMaxWidth,
-  aMaxHeight: TFresnelLength; Commit: boolean): TFresnelPoint;
+  aMaxHeight: TFresnelLength; Commit: boolean): TFreIntrinsicContentSize;
 begin
-  Result:=Default(TFresnelPoint);
+  Result:=Default(TFreIntrinsicContentSize);
 
   if Commit then ;
   if aMode=flmMax then ;
@@ -1408,7 +1441,7 @@ begin
 end;
 
 procedure TFLFlexLayouter.EndLine(MaxMainSize, MainGap: TFresnelLength; Commit: boolean;
-  var NewContentSize: TFresnelPoint);
+  var NewContentSize: TFreIntrinsicContentSize);
 begin
   if FLineItems.Count=0 then exit;
   FlexLineMainDirection(MaxMainSize,MainGap,Commit,NewContentSize);
@@ -1419,7 +1452,7 @@ begin
 end;
 
 procedure TFLFlexLayouter.FlexLineMainDirection(MaxMainSize, MainGap: TFresnelLength;
-  Commit: boolean; var NewContentSize: TFresnelPoint);
+  Commit: boolean; var NewContentSize: TFreIntrinsicContentSize);
 const
   MinAdjust = 0.0001;
 var
@@ -1460,9 +1493,9 @@ var
   procedure AdjustMainContentSize(p: TFresnelLength);
   begin
     if MainIsRow then
-      NewContentSize.X:=Max(NewContentSize.X,p)
+      NewContentSize.Width:=Max(NewContentSize.Width,p)
     else
-      NewContentSize.Y:=Max(NewContentSize.Y,p);
+      NewContentSize.Height:=Max(NewContentSize.Height,p);
   end;
 
 var
@@ -1643,8 +1676,8 @@ begin
   end;
 end;
 
-procedure TFLFlexLayouter.FlexLineCrossDirection(Commit: boolean; var NewContentSize: TFresnelPoint
-  );
+procedure TFLFlexLayouter.FlexLineCrossDirection(Commit: boolean;
+  var NewContentSize: TFreIntrinsicContentSize);
 var
   i: Integer;
   Item: TFlexItem;
@@ -1717,9 +1750,9 @@ begin
   end;
 
   if MainIsRow then
-    NewContentSize.Y:=Max(NewContentSize.Y,MaxSize)
+    NewContentSize.Height:=Max(NewContentSize.Height,MaxSize)
   else
-    NewContentSize.X:=Max(NewContentSize.X,MaxSize);
+    NewContentSize.Width:=Max(NewContentSize.Width,MaxSize);
 end;
 
 procedure TFLFlexLayouter.PlaceLineItems;
@@ -1732,7 +1765,7 @@ procedure TFLFlexLayouter.ComputeChildAttributes(Item: TLineItem;
 var
   FlexItem: TFlexItem;
   ItemNode: TUsedLayoutNode;
-  Size: TFresnelLayoutNode.TContentSize;
+  Size: TFreIntrinsicContentSize;
 begin
   inherited ComputeChildAttributes(Item, El);
 
@@ -1829,7 +1862,7 @@ begin
 end;
 
 function TFLFlexLayouter.ComputeLayoutContent(aMode: TFresnelLayoutMode; aMaxWidth,
-  aMaxHeight: TFresnelLength; Commit: boolean): TFresnelPoint;
+  aMaxHeight: TFresnelLength; Commit: boolean): TFreIntrinsicContentSize;
 var
   ChildIndex: Integer;
   ChildNode: TUsedLayoutNode;
@@ -1841,7 +1874,7 @@ var
   ChildDefPos: TFresnelPoint;
   ItemAdded: Boolean;
 begin
-  Result:=default(TFresnelPoint);
+  Result:=default(TFreIntrinsicContentSize);
 
   if MainIsRow then
   begin
@@ -1990,19 +2023,18 @@ end;
 
 function TFLLineLayouter.PlaceAbsoluteItem(ChildNode: TUsedLayoutNode; aMode: TFresnelLayoutMode;
   aMaxWidth, aMaxHeight: TFresnelLength; const DefaultPos: TFresnelPoint; Commit: boolean
-  ): TFresnelPoint;
+  ): TFreIntrinsicContentSize;
 // returns right, bottom marginbox, so the parent can compute its needed content size
 var
   ChildEl: TFresnelElement;
   NewLeft, NewTop, NewRight, NewBottom, NewWidth, NewHeight,
     FrameLeft, FrameRight,
     FrameTop, FrameBottom: TFresnelLength;
-  Size: TFresnelLayoutNode.TContentSize;
+  Size: TFreIntrinsicContentSize;
   r: TFresnelRect;
   p: TFresnelPoint;
 begin
-  Result.X:=0;
-  Result.Y:=0;
+  Result:=Default(TFreIntrinsicContentSize);
 
   ChildEl:=ChildNode.Element;
 
@@ -2028,7 +2060,7 @@ begin
     NewTop:=p.y+DefaultPos.Y;
   end;
 
-  if Commit then
+  if aMode=flmMax then
   begin
     NewWidth:=ChildNode.Width;
     NewHeight:=ChildNode.Height;
@@ -2040,59 +2072,67 @@ begin
   // Note: The DeductUsedLengths has already computed simple cases
   //   e.g. given container's width and child's left and right => child's width
 
+  aMaxWidth:=NewWidth;
+  aMaxHeight:=NewHeight;
+  if IsNan(aMaxWidth) then
+    aMaxWidth:=ChildNode.MaxWidth;
+  if IsNan(aMaxHeight) then
+    aMaxHeight:=ChildNode.MaxHeight;
+  Size:=ChildEl.GetIntrinsicContentSize(aMode,aMaxWidth,aMaxHeight);
   if IsNan(NewWidth) then
-  begin
-    if IsNan(NewHeight) then
-    begin
-      // auto width and height
-      Size:=ChildNode.GetIntrinsicContentSize(aMode,ChildNode.MaxWidth,ChildNode.MaxHeight);
-      NewWidth:=ChildNode.FitWidth(Size.Width);
-      NewHeight:=ChildNode.FitHeight(Size.Height);
-    end else begin
-      // height set, width auto
-      Size:=ChildNode.GetIntrinsicContentSize(aMode,ChildNode.MaxWidth,NewHeight);
-      NewWidth:=ChildNode.FitWidth(Size.Width);
-    end;
-  end else if IsNan(NewHeight) then begin
-    // width set, height auto
-    Size:=ChildNode.GetIntrinsicContentSize(aMode,NewWidth,ChildNode.MaxHeight);
+    NewWidth:=ChildNode.FitWidth(Size.Width);
+  if IsNan(NewHeight) then
     NewHeight:=ChildNode.FitHeight(Size.Height);
-  end;
 
   FrameLeft:=ChildNode.MarginLeft+ChildNode.BorderLeft+ChildNode.PaddingLeft;
   FrameRight:=ChildNode.PaddingRight+ChildNode.BorderRight+ChildNode.MarginRight;
   FrameTop:=ChildNode.MarginTop+ChildNode.BorderTop+ChildNode.PaddingTop;
   FrameBottom:=ChildNode.PaddingBottom+ChildNode.BorderBottom+ChildNode.MarginBottom;
-  if Commit then
+  if IsNan(NewLeft) then
   begin
-    if IsNan(NewLeft) then
-    begin
-      if IsNan(aMaxWidth) or IsNan(NewRight) then
-        NewLeft:=0
-      else
-        NewLeft:=aMaxWidth-NewRight-NewWidth-FrameLeft-FrameRight;
-    end;
-    if IsNan(NewTop) then
-    begin
-      if IsNan(aMaxHeight) or IsNan(NewBottom) then
-        NewTop:=0
-      else
-        NewTop:=aMaxHeight-NewBottom-NewHeight-FrameTop-FrameBottom;
-    end;
+    if IsNan(aMaxWidth) or IsNan(NewRight) then
+      NewLeft:=0
+    else
+      NewLeft:=aMaxWidth-NewRight-NewWidth-FrameLeft-FrameRight;
+  end;
+  if IsNan(NewTop) then
+  begin
+    if IsNan(aMaxHeight) or IsNan(NewBottom) then
+      NewTop:=0
+    else
+      NewTop:=aMaxHeight-NewBottom-NewHeight-FrameTop-FrameBottom;
   end;
 
   // compute right, bottom of marginbox
-  Result.X:=FrameLeft+NewWidth+FrameRight;
+  Result.Width:=FrameLeft+NewWidth+FrameRight;
   if not IsNan(NewLeft) then
-    Result.X:=Result.X+NewLeft;
+    Result.Width:=Result.Width+NewLeft;
   if not IsNan(NewRight) then
-    Result.X:=Result.X+NewRight;
+    Result.Width:=Result.Width+NewRight;
+  Result.ScrollWidth:=Result.Width;
+  if Size.ScrollWidth>NewWidth then
+  begin
+    Result.ScrollWidth:=FrameLeft+Result.ScrollWidth;
+    if not IsNan(NewLeft) then
+      Result.ScrollWidth:=Result.ScrollWidth+NewLeft;
+    if Result.ScrollWidth<Result.Width then
+      Result.ScrollWidth:=Result.Width;
+  end;
 
-  Result.Y:=FrameTop+NewWidth+FrameBottom;
+  Result.Height:=FrameTop+NewWidth+FrameBottom;
   if not IsNan(NewTop) then
-    Result.Y:=Result.Y+NewTop;
+    Result.Height:=Result.Height+NewTop;
   if not IsNan(NewBottom) then
-    Result.Y:=Result.Y+NewBottom;
+    Result.Height:=Result.Height+NewBottom;
+  Result.ScrollHeight:=Result.Height;
+  if Size.ScrollHeight>NewHeight then
+  begin
+    Result.ScrollHeight:=FrameTop+Result.ScrollHeight;
+    if not IsNan(NewTop) then
+      Result.ScrollHeight:=Result.ScrollHeight+NewTop;
+    if Result.ScrollHeight<Result.Height then
+      Result.ScrollHeight:=Result.Height;
+  end;
 
   if Commit then
   begin

+ 10 - 0
tests/exploration/scroll/ScrollWheelTest1.lpr

@@ -16,6 +16,7 @@ type
   TMainForm = class(TFresnelForm)
     Body: TBody;
     HeaderLabel: TLabel;
+    BlaLabel: TLabel;
   public
     constructor CreateNew(AOwner: TComponent); override;
   end;
@@ -38,6 +39,7 @@ begin
   Body:=TBody.Create(Self);
   with Body do begin
     Name:='Body';
+    Style:='border: 1px solid green;';
     Parent:=Self;
   end;
 
@@ -49,6 +51,14 @@ begin
     Parent:=Body;
   end;
 
+  BlaLabel:=TLabel.Create(Self);
+  with BlaLabel do begin
+    Name:='BlaLabel';
+    Style:='border: 1px solid blue; position: absolute; right: 1px;';
+    Caption:='Bla';
+    Parent:=Body;
+  end;
+
   Visible:=true;
 end;