|
@@ -35,8 +35,9 @@ type
|
|
|
type
|
|
|
TLayoutSize = record
|
|
|
Mode: TFresnelLayoutMode;
|
|
|
+ WithScroll: boolean;
|
|
|
MaxWidth, MaxHeight: TFresnelLength;
|
|
|
- ContentSize: TFresnelPoint;
|
|
|
+ ContentSize: TFresnelLayoutNode.TContentSize;
|
|
|
end;
|
|
|
TLayoutSizeArray = array of TLayoutSize;
|
|
|
var
|
|
@@ -47,11 +48,14 @@ type
|
|
|
procedure Init; virtual; // called after layout node and parent was updated, CSS string values were computed, no used values yet
|
|
|
procedure DeductUsedLengths(NoChildren: boolean); virtual;
|
|
|
procedure ClearCachedLayoutSizes; virtual;
|
|
|
- function GetCachedLayoutSize(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength; out aSize: TFresnelPoint): boolean; virtual;
|
|
|
- procedure AddCachedLayoutSize(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength; const aSize: TFresnelPoint); virtual;
|
|
|
- function ComputeLayoutContent(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength; Commit: boolean): TFresnelPoint; virtual; abstract;
|
|
|
+ function GetCachedLayoutSize(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength;
|
|
|
+ aWithScroll: boolean; out aSize: TFresnelLayoutNode.TContentSize): boolean; virtual;
|
|
|
+ procedure AddCachedLayoutSize(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength;
|
|
|
+ aWithScroll: boolean; const aSize: TFresnelLayoutNode.TContentSize); virtual;
|
|
|
+ function ComputeLayoutContent(aMode: TFresnelLayoutMode; aMaxWidth, aMaxHeight: TFresnelLength;
|
|
|
+ Commit: boolean): TFresnelPoint; virtual; abstract;
|
|
|
function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength=NaN;
|
|
|
- aMaxHeight: TFresnelLength=NaN): TFresnelPoint; virtual;
|
|
|
+ aMaxHeight: TFresnelLength=NaN; aWithScroll: boolean = true): TFresnelLayoutNode.TContentSize; virtual;
|
|
|
function GetViewport: TFresnelViewport;
|
|
|
end;
|
|
|
TFLNodeLayouterClass = class of TFLNodeLayouter;
|
|
@@ -66,10 +70,12 @@ type
|
|
|
procedure ComputeUsedLengths(NoChildren: boolean); virtual;
|
|
|
procedure ComputeScrollbarGutter; virtual; // compute attribute scrollbar-gutter
|
|
|
procedure ComputeScrollbarsNoChildren; virtual;
|
|
|
+ procedure GetScrollGutterWidths(Horizontal: boolean; out LT, RB: TFresnelLength);
|
|
|
procedure AddScrollGutter(Horizontal: boolean);
|
|
|
+ procedure ApplyScrollSize(const aSize: TContentSize);
|
|
|
procedure DeductUsedLengths(NoChildren: boolean); virtual;
|
|
|
function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength=NaN;
|
|
|
- aMaxHeight: TFresnelLength=NaN): TFresnelPoint; override;
|
|
|
+ aMaxHeight: TFresnelLength=NaN): TContentSize; override;
|
|
|
destructor Destroy; override;
|
|
|
end;
|
|
|
|
|
@@ -247,70 +253,81 @@ end;
|
|
|
|
|
|
procedure TFLNodeLayouter.Apply;
|
|
|
var
|
|
|
- MaxWidth, MaxHeight, Avail: TFresnelLength;
|
|
|
+ MaxWidth, MaxHeight, aClientWidth, aClientHeight: TFresnelLength;
|
|
|
+ ContSize: TFresnelLayoutNode.TContentSize;
|
|
|
Size: TFresnelPoint;
|
|
|
- VP: TFresnelViewport;
|
|
|
- NeedScrollX, NeedScrollY: Boolean;
|
|
|
+ HasMaxWidth, HasMaxHeight: Boolean;
|
|
|
+begin
|
|
|
+ MaxWidth:=Node.Width;
|
|
|
+ if IsNan(MaxWidth) then
|
|
|
+ MaxWidth:=Node.MaxWidth;
|
|
|
+ HasMaxWidth:=not IsNan(MaxWidth);
|
|
|
|
|
|
- procedure CheckScrollY;
|
|
|
+ MaxHeight:=Node.Height;
|
|
|
+ if IsNan(MaxHeight) then
|
|
|
+ MaxHeight:=Node.MaxWidth;
|
|
|
+ HasMaxHeight:=not IsNan(MaxHeight);
|
|
|
+
|
|
|
+ if Node.CanScrollX or Node.CanScrollY then
|
|
|
begin
|
|
|
- if not Node.CanScrollY then exit;
|
|
|
- Avail:=Node.Height+Node.PaddingTop+Node.PaddingBottom-Node.ScrollGutterTop-Node.ScrollGutterBottom;
|
|
|
- NeedScrollY:=(Avail<Size.Y);
|
|
|
- if NeedScrollY and (not VP.ScrollbarsOverlay) and (Node.ScrollGutterRight=0) then
|
|
|
- Node.AddScrollGutter(false);
|
|
|
- end;
|
|
|
+ // can scroll
|
|
|
+ if IsNan(Node.ScrollWidth) then
|
|
|
+ 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);
|
|
|
+ {$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));
|
|
|
+ {$ENDIF}
|
|
|
+ end;
|
|
|
|
|
|
-begin
|
|
|
- // get maximum width/height for content
|
|
|
- if Node.CanScrollX then
|
|
|
- MaxWidth:=NaN
|
|
|
- else begin
|
|
|
- MaxWidth:=Node.Width;
|
|
|
- if IsNan(MaxWidth) then
|
|
|
- MaxWidth:=Node.MaxWidth;
|
|
|
- end;
|
|
|
+ aClientWidth:=MaxWidth;
|
|
|
+ if HasMaxWidth then
|
|
|
+ aClientWidth:=aClientWidth-Node.ScrollGutterLeft-Node.ScrollGutterRight;
|
|
|
+ aClientHeight:=MaxHeight;
|
|
|
+ if HasMaxHeight then
|
|
|
+ aClientHeight:=aClientHeight-Node.ScrollGutterTop-Node.ScrollGutterBottom;
|
|
|
|
|
|
- if Node.CanScrollY then
|
|
|
- MaxHeight:=NaN
|
|
|
- else begin
|
|
|
- MaxHeight:=Node.Height;
|
|
|
- if IsNan(MaxHeight) then
|
|
|
- MaxHeight:=Node.MaxWidth;
|
|
|
- end;
|
|
|
-
|
|
|
- // layout content and compute its size
|
|
|
- //writeln('TFLNodeLayouter.Apply "',Node.Element.Name,'" CanScrollX=',Node.CanScrollX,' CanScrollY=',Node.CanScrollY,' MaxWidth=',FloatToCSSStr(MaxWidth),' MaxHeight=',FloatToCSSStr(MaxHeight));
|
|
|
- Size:=ComputeLayoutContent(flmMaxWidth,MaxWidth,MaxHeight,true);
|
|
|
- if IsNan(Node.Width) then
|
|
|
- Node.Width:=Node.FitWidth(Size.X);
|
|
|
- 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);
|
|
|
-
|
|
|
- // check if scrolling is needed and add scrollbar gutter
|
|
|
- {$IFDEF VerboseFresnelScrolling}
|
|
|
- writeln('TFLNodeLayouter.Apply CHECK SCROLL NEEDED: "',Node.Element.Name,'" CanScrollX=',Node.CanScrollX,' CanScrollY=',Node.CanScrollY,' Width=',FloatToCSSStr(Node.Width),' Height=',FloatToCSSStr(Node.Height),' SizeX=',FloatToCSSStr(Size.X),' SizeY=',FloatToCSSStr(Size.Y));
|
|
|
- {$ENDIF}
|
|
|
- VP:=Node.Element.Viewport;
|
|
|
- CheckScrollY;
|
|
|
- if Node.CanScrollX then
|
|
|
- begin
|
|
|
- Avail:=Node.Width+Node.PaddingLeft+Node.PaddingRight-Node.ScrollGutterLeft-Node.ScrollGutterRight;
|
|
|
- NeedScrollX:=(Avail<Size.X);
|
|
|
- if NeedScrollX and (not VP.ScrollbarsOverlay) and (Node.ScrollGutterBottom=0) then
|
|
|
+ {$IFDEF VerboseFresnelScrolling}
|
|
|
+ writeln('TFLNodeLayouter.Apply "',Node.Element.Name,'" CanScrollX=',Node.CanScrollX,' CanScrollY=',Node.CanScrollY,' MaxSize=',FloatToCSSStr(MaxWidth),'x',FloatToCSSStr(MaxHeight),' Client=',FloatToCSSStr(aClientWidth),'x',FloatToCSSStr(aClientHeight));
|
|
|
+ {$ENDIF}
|
|
|
+ Size:=ComputeLayoutContent(flmMax,aClientWidth,aClientHeight,true);
|
|
|
+ {$IFDEF VerboseFresnelScrolling}
|
|
|
+ writeln('TFLNodeLayouter.Apply "',Node.Element.Name,'" ContentSize: ',FloatToCSSStr(Size.X),'x',FloatToCSSStr(Size.Y));
|
|
|
+ {$ENDIF}
|
|
|
+ if IsNan(Node.Width) then
|
|
|
begin
|
|
|
- Node.AddScrollGutter(true);
|
|
|
- CheckScrollY;
|
|
|
+ Node.Width:=Size.X;
|
|
|
+ if Node.Width>aClientWidth then
|
|
|
+ Node.Width:=aClientWidth;
|
|
|
end;
|
|
|
+ if IsNan(Node.Height) then
|
|
|
+ begin
|
|
|
+ Node.Height:=Node.FitHeight(Size.Y);
|
|
|
+ if Node.Height>aClientHeight then
|
|
|
+ Node.Height:=aClientHeight;
|
|
|
+ end;
|
|
|
+ Node.ScrollWidth:=Max(Size.X,aClientWidth);
|
|
|
+ Node.ScrollHeight:=Max(Size.Y,aClientHeight);
|
|
|
{$IFDEF VerboseFresnelScrolling}
|
|
|
- writeln('TFLNodeLayouter.Apply "',Node.Element.Name,'" NeedScrollX=',NeedScrollX,' NeedScrollY=',NeedScrollY,' Gutter: Left=',FloatToCSSStr(Node.ScrollGutterLeft),' Right=',FloatToCSSStr(Node.ScrollGutterRight),' Top=',FloatToCSSStr(Node.ScrollGutterTop),' Bottom=',FloatToCSSStr(Node.ScrollGutterBottom));
|
|
|
+ writeln('TFLNodeLayouter.Apply "',Node.Element.Name,'" ScrollSize: ',FloatToCSSStr(Node.ScrollWidth),'x',FloatToCSSStr(Node.ScrollHeight),' Width=',FloatToCSSStr(Node.Width),' Height=',FloatToCSSStr(Node.Height));
|
|
|
{$ENDIF}
|
|
|
- end;
|
|
|
|
|
|
- // todo: ScrollLeft, ScrollTop
|
|
|
- // todo: scroll anchor
|
|
|
+ // todo: ScrollLeft, ScrollTop
|
|
|
+ // todo: scroll anchor
|
|
|
+ end else begin
|
|
|
+ // no scrolling
|
|
|
+ //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);
|
|
|
+ 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);
|
|
|
+ end;
|
|
|
end;
|
|
|
|
|
|
procedure TFLNodeLayouter.Init;
|
|
@@ -329,7 +346,8 @@ begin
|
|
|
end;
|
|
|
|
|
|
function TFLNodeLayouter.GetCachedLayoutSize(aMode: TFresnelLayoutMode; aMaxWidth,
|
|
|
- aMaxHeight: TFresnelLength; out aSize: TFresnelPoint): boolean;
|
|
|
+ aMaxHeight: TFresnelLength; aWithScroll: boolean; out aSize: TFresnelLayoutNode.TContentSize
|
|
|
+ ): boolean;
|
|
|
|
|
|
function CheckMax(OldMax, NewMax, OldResult: TFresnelLength): boolean;
|
|
|
begin
|
|
@@ -390,26 +408,27 @@ begin
|
|
|
for i:=0 to length(FLastLayoutSizes)-1 do
|
|
|
with FLastLayoutSizes[i] do begin
|
|
|
if (aMode<>Mode) then continue;
|
|
|
+ if (aWithScroll<>WithScroll) then continue;
|
|
|
case aMode of
|
|
|
flmMinWidth:
|
|
|
- if not CheckMax(MaxHeight,aMaxHeight,ContentSize.Y) then continue;
|
|
|
+ if not CheckMax(MaxHeight,aMaxHeight,ContentSize.Height) then continue;
|
|
|
flmMinHeight:
|
|
|
- if not CheckMax(MaxWidth,aMaxWidth,ContentSize.X) then continue;
|
|
|
- flmMaxWidth,flmMaxHeight:
|
|
|
+ if not CheckMax(MaxWidth,aMaxWidth,ContentSize.Width) then continue;
|
|
|
+ flmMax:
|
|
|
begin
|
|
|
- if not CheckMax(MaxWidth,aMaxWidth,ContentSize.X) then continue;
|
|
|
- if not CheckMax(MaxHeight,aMaxHeight,ContentSize.Y) then continue;
|
|
|
+ if not CheckMax(MaxWidth,aMaxWidth,ContentSize.Width) then continue;
|
|
|
+ if not CheckMax(MaxHeight,aMaxHeight,ContentSize.Height) then continue;
|
|
|
end;
|
|
|
end;
|
|
|
aSize:=ContentSize;
|
|
|
exit(true);
|
|
|
end;
|
|
|
- aSize:=Default(TFresnelPoint);
|
|
|
+ aSize:=Default(TFresnelLayoutNode.TContentSize);
|
|
|
Result:=false;
|
|
|
end;
|
|
|
|
|
|
procedure TFLNodeLayouter.AddCachedLayoutSize(aMode: TFresnelLayoutMode; aMaxWidth,
|
|
|
- aMaxHeight: TFresnelLength; const aSize: TFresnelPoint);
|
|
|
+ aMaxHeight: TFresnelLength; aWithScroll: boolean; const aSize: TFresnelLayoutNode.TContentSize);
|
|
|
var
|
|
|
l: integer;
|
|
|
begin
|
|
@@ -417,6 +436,7 @@ begin
|
|
|
SetLength(FLastLayoutSizes,l+1);
|
|
|
with FLastLayoutSizes[l] do begin
|
|
|
Mode:=aMode;
|
|
|
+ WithScroll:=aWithScroll;
|
|
|
MaxWidth:=aMaxWidth;
|
|
|
MaxHeight:=aMaxHeight;
|
|
|
ContentSize:=aSize;
|
|
@@ -424,18 +444,155 @@ begin
|
|
|
end;
|
|
|
|
|
|
function TFLNodeLayouter.GetIntrinsicContentSize(aMode: TFresnelLayoutMode;
|
|
|
- aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength): TFresnelPoint;
|
|
|
+ aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength; aWithScroll: boolean): TFresnelLayoutNode.
|
|
|
+ TContentSize;
|
|
|
+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;
|
|
|
+ AddCachedLayoutSize(CurMode,CurMaxWidth,CurMaxHeight,false,Result);
|
|
|
+ end;
|
|
|
+ if AddGutter then begin
|
|
|
+ Result.Width:=Result.Width+GutterLeft+GutterRight;
|
|
|
+ Result.Height:=Result.Height+GutterTop+GutterBottom;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+var
|
|
|
+ GutterVertical, GutterHorizontal: TFresnelLayoutNode.TGutterMode;
|
|
|
+ HasMaxWidth, HasMaxHeight, Done: Boolean;
|
|
|
+ aClientHeight, aClientWidth: TFresnelLength;
|
|
|
+
|
|
|
+ procedure CheckOverflowX;
|
|
|
+ begin
|
|
|
+ if Node.CanScrollX and HasMaxWidth and (Result.Width>aMaxWidth) then
|
|
|
+ begin
|
|
|
+ // width does not fit and scrolling allowed
|
|
|
+ Result.Width:=aMaxWidth;
|
|
|
+ if GutterHorizontal=gmAuto then
|
|
|
+ begin
|
|
|
+ // auto horizontal gutter needed
|
|
|
+ Result.NeedGutterHorizontal:=true;
|
|
|
+ GutterHorizontal:=gmAlways;
|
|
|
+ Node.GetScrollGutterWidths(true,GutterTop,GutterBottom);
|
|
|
+ aClientHeight:=aClientHeight-GutterTop-GutterBottom;
|
|
|
+ if aClientHeight<0 then aClientHeight:=0;
|
|
|
+ Result.Height:=Result.Height+GutterTop+GutterBottom;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+ procedure CheckOverflowY;
|
|
|
+ begin
|
|
|
+ if Node.CanScrollY and HasMaxHeight and (Result.Height>aMaxHeight) then
|
|
|
+ begin
|
|
|
+ // height does not fit and scrolling allowed
|
|
|
+ Result.Height:=aMaxHeight;
|
|
|
+ if GutterVertical=gmAuto then
|
|
|
+ begin
|
|
|
+ // auto vertical gutter needed
|
|
|
+ Result.NeedGutterVertical:=true;
|
|
|
+ GutterVertical:=gmAlways;
|
|
|
+ Node.GetScrollGutterWidths(false,GutterLeft,GutterRight);
|
|
|
+ aClientWidth:=aClientWidth-GutterLeft-GutterRight;
|
|
|
+ if aClientWidth<0 then aClientWidth:=0;
|
|
|
+ Result.Width:=Result.Width+GutterLeft+GutterRight;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+var
|
|
|
+ aSize: TFresnelPoint;
|
|
|
begin
|
|
|
case aMode of
|
|
|
+ flmMinWidth: aMaxWidth:=NaN;
|
|
|
+ flmMinHeight: aMaxHeight:=NaN;
|
|
|
+ end;
|
|
|
+
|
|
|
+ GutterVertical:=Node.GutterVertical;
|
|
|
+ GutterHorizontal:=Node.GutterHorizontal;
|
|
|
+ if aWithScroll and (GutterVertical=gmNone) and (GutterHorizontal=gmNone) then
|
|
|
+ begin
|
|
|
+ // no gutters -> simply compute content
|
|
|
+ // Note: Even with scrolling short lines are broken at aMaxWidth, not at ScrollWidth.
|
|
|
+ // So the layout is the same.
|
|
|
+ aWithScroll:=false;
|
|
|
+ end;
|
|
|
+
|
|
|
+ if GetCachedLayoutSize(aMode,aMaxWidth,aMaxHeight,aWithScroll,Result) then
|
|
|
+ exit;
|
|
|
+
|
|
|
+ Done:=false;
|
|
|
+ if aWithScroll then
|
|
|
+ begin
|
|
|
+ // consider gutters
|
|
|
+ GutterLeft:=Node.ScrollGutterLeft;
|
|
|
+ GutterRight:=Node.ScrollGutterRight;
|
|
|
+ GutterTop:=Node.ScrollGutterTop;
|
|
|
+ GutterBottom:=Node.ScrollGutterBottom;
|
|
|
+
|
|
|
+ HasMaxWidth:=not IsNan(aMaxWidth);
|
|
|
+ aClientWidth:=aMaxWidth;
|
|
|
+ if HasMaxWidth then
|
|
|
+ begin
|
|
|
+ if aMaxWidth<0 then aMaxWidth:=0;
|
|
|
+ aClientWidth:=Max(0,aClientWidth-GutterLeft-GutterRight);
|
|
|
+ end;
|
|
|
+
|
|
|
+ HasMaxHeight:=not IsNan(aMaxHeight);
|
|
|
+ aClientHeight:=aMaxHeight;
|
|
|
+ if HasMaxHeight then
|
|
|
+ begin
|
|
|
+ if aMaxHeight<0 then aMaxHeight:=0;
|
|
|
+ aClientHeight:=Max(0,aClientHeight-GutterTop-GutterBottom);
|
|
|
+ end;
|
|
|
+
|
|
|
+ Compute(aMode,aClientWidth,aClientHeight,true);
|
|
|
+ case aMode of
|
|
|
flmMinWidth:
|
|
|
- aMaxWidth:=NaN;
|
|
|
+ CheckOverflowY;
|
|
|
flmMinHeight:
|
|
|
- aMaxHeight:=NaN;
|
|
|
+ CheckOverflowX;
|
|
|
+ flmMax:
|
|
|
+ begin
|
|
|
+ // maximize width
|
|
|
+ CheckOverflowX;
|
|
|
+ CheckOverflowY;
|
|
|
+ if HasMaxWidth and (Result.Width>aMaxWidth) and Node.CanScrollX then
|
|
|
+ begin
|
|
|
+ // width no longer fits -> compute again
|
|
|
+ Compute(flmMax,aClientWidth,aClientHeight,true);
|
|
|
+ Result.NeedGutterHorizontal:=true;
|
|
|
+ CheckOverflowX;
|
|
|
+ CheckOverflowY;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ Done:=true;
|
|
|
end;
|
|
|
- if GetCachedLayoutSize(aMode,aMaxWidth,aMaxHeight,Result) then
|
|
|
- exit;
|
|
|
- Result:=ComputeLayoutContent(aMode,aMaxWidth,aMaxHeight,false);
|
|
|
- AddCachedLayoutSize(aMode,aMaxWidth,aMaxHeight,Result);
|
|
|
+
|
|
|
+ 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;
|
|
|
+ end;
|
|
|
+ AddCachedLayoutSize(aMode,aMaxWidth,aMaxHeight,aWithScroll,Result);
|
|
|
end;
|
|
|
|
|
|
function TFLNodeLayouter.GetViewport: TFresnelViewport;
|
|
@@ -589,16 +746,26 @@ procedure TUsedLayoutNode.ComputeScrollbarsNoChildren;
|
|
|
|
|
|
procedure CalcGutter(ComputedOverflow: TCSSNumericalID; Horizontal: boolean);
|
|
|
var
|
|
|
- GutterNeeded: Boolean;
|
|
|
+ GutterMode: TGutterMode;
|
|
|
begin
|
|
|
- GutterNeeded:=false;
|
|
|
+ GutterMode:=gmNone;
|
|
|
case ComputedOverflow of
|
|
|
CSSRegistry.kwScroll:
|
|
|
- GutterNeeded:=true;
|
|
|
- CSSRegistry.kwAuto,CSSRegistry.kwHidden:
|
|
|
- GutterNeeded:=ScrollGutterStable;
|
|
|
+ GutterMode:=gmAlways;
|
|
|
+ CSSRegistry.kwAuto:
|
|
|
+ if ScrollGutterStable then
|
|
|
+ GutterMode:=gmAlways
|
|
|
+ else
|
|
|
+ GutterMode:=gmAuto;
|
|
|
+ CSSRegistry.kwHidden:
|
|
|
+ if ScrollGutterStable then
|
|
|
+ GutterMode:=gmAlways;
|
|
|
end;
|
|
|
- if GutterNeeded then
|
|
|
+ if Horizontal then
|
|
|
+ GutterHorizontal:=GutterMode
|
|
|
+ else
|
|
|
+ GutterVertical:=GutterMode;
|
|
|
+ if GutterMode=gmAlways then
|
|
|
AddScrollGutter(Horizontal);
|
|
|
end;
|
|
|
|
|
@@ -615,46 +782,72 @@ begin
|
|
|
ComputeScrollbarGutter;
|
|
|
if ScrollGutterWidth=CSSIDNone then
|
|
|
ScrollGutterWidth:=Element.GetComputedKeyword(fcaScrollbarWidth,CSSRegistry.Chk_ScrollbarWidth_KeywordIDs);
|
|
|
- if ScrollGutterWidth=CSSIDNone then
|
|
|
+ case ScrollGutterWidth of
|
|
|
+ CSSIDNone:
|
|
|
ScrollGutterWidth:=CSSRegistry.kwAuto;
|
|
|
+ CSSRegistry.kwNone:
|
|
|
+ exit;
|
|
|
+ end;
|
|
|
end else
|
|
|
exit;
|
|
|
{$IFDEF VerboseFresnelScrolling}
|
|
|
- writeln('TUsedLayoutNode.ComputeScrollbarsNoChildren ',Element.Name,' ComputedOverflowX=',CSSRegistry.Keywords[Element.ComputedOverflowX],' ComputedOverflowY=',CSSRegistry.Keywords[Element.ComputedOverflowY],' BothEdges=',ScrollGutterBothEdges,' Stable=',ScrollGutterStable,' ScrollGutterWidth=',ScrollGutterWidth);
|
|
|
+ writeln('TUsedLayoutNode.ComputeScrollbarsNoChildren ',Element.Name,' ComputedOverflowX=',CSSRegistry.Keywords[Element.ComputedOverflowX],' ComputedOverflowY=',CSSRegistry.Keywords[Element.ComputedOverflowY],' BothEdges=',ScrollGutterBothEdges,' Stable=',ScrollGutterStable,' ScrollGutterWidth=',CSSRegistry.Keywords[ScrollGutterWidth]);
|
|
|
{$ENDIF}
|
|
|
|
|
|
CalcGutter(Element.ComputedOverflowX,true);
|
|
|
CalcGutter(Element.ComputedOverflowY,false);
|
|
|
end;
|
|
|
|
|
|
-procedure TUsedLayoutNode.AddScrollGutter(Horizontal: boolean);
|
|
|
+procedure TUsedLayoutNode.GetScrollGutterWidths(Horizontal: boolean; out LT, RB: TFresnelLength);
|
|
|
var
|
|
|
VP: TFresnelViewport;
|
|
|
- GutterW: TFresnelLength;
|
|
|
+ W: TFresnelLength;
|
|
|
begin
|
|
|
+ LT:=0;
|
|
|
+ RB:=0;
|
|
|
VP:=Element.Viewport;
|
|
|
+ if VP.ScrollbarsOverlay then exit;
|
|
|
case ScrollGutterWidth of
|
|
|
- CSSRegistry.kwThin:
|
|
|
- GutterW:=VP.ScrollbarsThinWidth[Horizontal];
|
|
|
- CSSRegistry.kwAuto:
|
|
|
- GutterW:=VP.ScrollbarsWidth[Horizontal];
|
|
|
+ CSSRegistry.kwNone: exit;
|
|
|
+ CSSRegistry.kwThin: W:=VP.ScrollbarsThinWidth[Horizontal];
|
|
|
else
|
|
|
- exit; // none
|
|
|
+ // auto
|
|
|
+ W:=VP.ScrollbarsWidth[Horizontal];
|
|
|
end;
|
|
|
|
|
|
- if Horizontal then
|
|
|
- ScrollGutterBottom:=GutterW
|
|
|
- else
|
|
|
- ScrollGutterRight:=GutterW;
|
|
|
if ScrollGutterBothEdges then
|
|
|
begin
|
|
|
- if Horizontal then
|
|
|
- ScrollGutterTop:=GutterW
|
|
|
- else
|
|
|
- ScrollGutterLeft:=GutterW;
|
|
|
+ LT:=W;
|
|
|
+ RB:=W;
|
|
|
+ end
|
|
|
+ else if (not Horizontal) and (Element.ComputedDirection=CSSRegistry.kwRTL) then
|
|
|
+ LT:=W
|
|
|
+ else
|
|
|
+ RB:=W;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TUsedLayoutNode.AddScrollGutter(Horizontal: boolean);
|
|
|
+begin
|
|
|
+ if Horizontal then
|
|
|
+ begin
|
|
|
+ GetScrollGutterWidths(Horizontal,ScrollGutterTop,ScrollGutterBottom);
|
|
|
+ GutterHorizontal:=gmAlways;
|
|
|
+ end else begin
|
|
|
+ GetScrollGutterWidths(Horizontal,ScrollGutterLeft,ScrollGutterRight);
|
|
|
+ GutterVertical:=gmAlways;
|
|
|
end;
|
|
|
end;
|
|
|
|
|
|
+procedure TUsedLayoutNode.ApplyScrollSize(const aSize: TContentSize);
|
|
|
+begin
|
|
|
+ ScrollWidth:=aSize.ScrollWidth;
|
|
|
+ ScrollHeight:=aSize.ScrollHeight;
|
|
|
+ if (GutterHorizontal=gmAuto) and aSize.NeedGutterHorizontal then
|
|
|
+ AddScrollGutter(true);
|
|
|
+ if (GutterVertical=gmAuto) and aSize.NeedGutterVertical then
|
|
|
+ AddScrollGutter(false);
|
|
|
+end;
|
|
|
+
|
|
|
procedure TUsedLayoutNode.DeductUsedLengths(NoChildren: boolean);
|
|
|
var
|
|
|
aContainerWidth, aContainerHeight: TFresnelLength;
|
|
@@ -760,14 +953,12 @@ begin
|
|
|
end;
|
|
|
|
|
|
function TUsedLayoutNode.GetIntrinsicContentSize(aMode: TFresnelLayoutMode;
|
|
|
- aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength): TFresnelPoint;
|
|
|
+ aMaxWidth: TFresnelLength; aMaxHeight: TFresnelLength): TContentSize;
|
|
|
begin
|
|
|
if Layouter<>nil then
|
|
|
Result:=Layouter.GetIntrinsicContentSize(aMode,aMaxWidth,aMaxHeight)
|
|
|
- else begin
|
|
|
- Result.X:=0;
|
|
|
- Result.Y:=0;
|
|
|
- end;
|
|
|
+ else
|
|
|
+ Result:=Default(TContentSize);
|
|
|
end;
|
|
|
|
|
|
destructor TUsedLayoutNode.Destroy;
|
|
@@ -981,7 +1172,7 @@ begin
|
|
|
end;
|
|
|
|
|
|
// display-outside: inline or block
|
|
|
- if (not IsInline) or (aMode in [flmMinWidth,flmMaxHeight]) then
|
|
|
+ if (not IsInline) or (aMode=flmMinWidth) then
|
|
|
begin
|
|
|
// start new line
|
|
|
if FLineItems.Count>0 then
|
|
@@ -994,7 +1185,7 @@ begin
|
|
|
ChildPadBorderX:=ChildNode.BorderLeft+ChildNode.PaddingLeft+ChildNode.PaddingRight+ChildNode.BorderRight;
|
|
|
ChildPadBorderY:=ChildNode.BorderTop+ChildNode.PaddingTop+ChildNode.PaddingBottom+ChildNode.BorderBottom;
|
|
|
|
|
|
- if aMode in [flmMinWidth,flmMaxHeight] then
|
|
|
+ if aMode=flmMinWidth then
|
|
|
CurSpace:=0
|
|
|
else if IsNan(aMaxWidth) then
|
|
|
CurSpace:=NaN
|
|
@@ -1171,7 +1362,7 @@ begin
|
|
|
Result:=Default(TFresnelPoint);
|
|
|
|
|
|
if Commit then ;
|
|
|
- if aMode=flmMaxWidth then ;
|
|
|
+ if aMode=flmMax then ;
|
|
|
if IsNan(aMaxWidth) then ;
|
|
|
if IsNan(aMaxHeight) then ;
|
|
|
end;
|
|
@@ -1508,7 +1699,7 @@ procedure TFLFlexLayouter.ComputeChildAttributes(Item: TLineItem;
|
|
|
var
|
|
|
FlexItem: TFlexItem;
|
|
|
ItemNode: TUsedLayoutNode;
|
|
|
- p: TFresnelPoint;
|
|
|
+ Size: TFresnelLayoutNode.TContentSize;
|
|
|
begin
|
|
|
inherited ComputeChildAttributes(Item, El);
|
|
|
|
|
@@ -1534,13 +1725,13 @@ begin
|
|
|
FlexItem.Basis:=ItemNode.Width;
|
|
|
if IsNan(FlexItem.Basis) then
|
|
|
begin
|
|
|
- p:=El.GetIntrinsicContentSize(flmMaxWidth,ItemNode.MaxWidth,ItemNode.MaxHeight);
|
|
|
- FlexItem.Basis:=ItemNode.FitWidth(p.X);
|
|
|
+ Size:=ItemNode.GetIntrinsicContentSize(flmMax,ItemNode.MaxWidth,ItemNode.MaxHeight);
|
|
|
+ FlexItem.Basis:=ItemNode.FitWidth(Size.Width);
|
|
|
if IsNan(FlexItem.CrossContentSize) then
|
|
|
- FlexItem.CrossContentSize:=ItemNode.FitHeight(p.Y);
|
|
|
+ FlexItem.CrossContentSize:=ItemNode.FitHeight(Size.Height);
|
|
|
end else if IsNan(FlexItem.CrossContentSize) then begin
|
|
|
- p:=El.GetIntrinsicContentSize(flmMaxWidth,FlexItem.Basis,ItemNode.MaxHeight);
|
|
|
- FlexItem.CrossContentSize:=ItemNode.FitHeight(p.Y);
|
|
|
+ Size:=ItemNode.GetIntrinsicContentSize(flmMax,FlexItem.Basis,ItemNode.MaxHeight);
|
|
|
+ FlexItem.CrossContentSize:=ItemNode.FitHeight(Size.Height);
|
|
|
end;
|
|
|
|
|
|
end else begin
|
|
@@ -1553,13 +1744,13 @@ begin
|
|
|
FlexItem.Basis:=ItemNode.Height;
|
|
|
if IsNan(FlexItem.Basis) then
|
|
|
begin
|
|
|
- p:=El.GetIntrinsicContentSize(flmMaxHeight,ItemNode.MaxWidth,ItemNode.MaxHeight);
|
|
|
- FlexItem.Basis:=ItemNode.FitWidth(p.Y);
|
|
|
+ Size:=ItemNode.GetIntrinsicContentSize(flmMax,ItemNode.MaxWidth,ItemNode.MaxHeight);
|
|
|
+ FlexItem.Basis:=ItemNode.FitWidth(Size.Height);
|
|
|
if IsNan(FlexItem.CrossContentSize) then
|
|
|
- FlexItem.CrossContentSize:=ItemNode.FitWidth(p.X);
|
|
|
+ FlexItem.CrossContentSize:=ItemNode.FitWidth(Size.Width);
|
|
|
end else if IsNan(FlexItem.CrossContentSize) then begin
|
|
|
- p:=El.GetIntrinsicContentSize(flmMaxHeight,ItemNode.MaxWidth,FlexItem.Basis);
|
|
|
- FlexItem.CrossContentSize:=ItemNode.FitWidth(p.X);
|
|
|
+ Size:=ItemNode.GetIntrinsicContentSize(flmMax,ItemNode.MaxWidth,FlexItem.Basis);
|
|
|
+ FlexItem.CrossContentSize:=ItemNode.FitWidth(Size.Width);
|
|
|
end;
|
|
|
end;
|
|
|
end;
|
|
@@ -1698,8 +1889,8 @@ var
|
|
|
Item: TLineItem;
|
|
|
ChildNode: TUsedLayoutNode;
|
|
|
ChildEl: TFresnelElement;
|
|
|
- NewLeft, NewTop: TFresnelLength;
|
|
|
- r: TFresnelRect;
|
|
|
+ NewLeft, NewTop, FrameLeft, FrameRight, FrameTop, FrameBottom: TFresnelLength;
|
|
|
+ aBorderBox, aContentBox: TFresnelRect;
|
|
|
begin
|
|
|
for i:=0 to FLineItems.Count-1 do
|
|
|
begin
|
|
@@ -1733,27 +1924,29 @@ begin
|
|
|
NewTop:=NewTop-ChildNode.Bottom;
|
|
|
end;
|
|
|
|
|
|
- // used borderbox
|
|
|
- r.Left:=NewLeft+ChildNode.MarginLeft;
|
|
|
- r.Top:=NewTop+ChildNode.MarginTop;
|
|
|
- r.Right:=r.Left+ChildNode.BorderLeft+ChildNode.PaddingLeft
|
|
|
- +Item.ContentBoxWidth
|
|
|
- +ChildNode.PaddingRight+ChildNode.BorderRight;
|
|
|
- r.Bottom:=r.Top+ChildNode.BorderTop+ChildNode.PaddingTop
|
|
|
- +Item.ContentBoxHeight
|
|
|
- +ChildNode.PaddingBottom+ChildNode.BorderBottom;
|
|
|
- ChildEl.UsedBorderBox:=r;
|
|
|
+ FrameLeft:=ChildNode.BorderLeft+ChildNode.PaddingLeft;
|
|
|
+ FrameRight:=ChildNode.BorderRight+ChildNode.PaddingRight;
|
|
|
+ FrameTop:=ChildNode.BorderTop+ChildNode.PaddingTop;
|
|
|
+ FrameBottom:=ChildNode.BorderBottom+ChildNode.PaddingBottom;
|
|
|
+
|
|
|
+ // UsedBorderBox, UsedContentBox
|
|
|
+ aBorderBox.Left:=NewLeft+ChildNode.MarginLeft;
|
|
|
+ aBorderBox.Top:=NewTop+ChildNode.MarginTop;
|
|
|
+ aContentBox.Left:=aBorderBox.Left+FrameLeft;
|
|
|
+ aContentBox.Top:=aBorderBox.Top+FrameTop;
|
|
|
+ aContentBox.Right:=aContentBox.Left+Item.ContentBoxWidth;
|
|
|
+ aContentBox.Bottom:=aContentBox.Top+Item.ContentBoxHeight;
|
|
|
+ aBorderBox.Right:=aContentBox.Right+FrameRight;
|
|
|
+ aBorderBox.Bottom:=aContentBox.Bottom+FrameBottom;
|
|
|
+
|
|
|
+ ChildEl.UsedBorderBox:=aBorderBox;
|
|
|
+ ChildEl.UsedContentBox:=aContentBox;
|
|
|
ChildNode.Left:=NewLeft;
|
|
|
ChildNode.Top:=NewTop;
|
|
|
-
|
|
|
- // used contentbox
|
|
|
- r.Left:=r.Left+ChildNode.BorderLeft+ChildNode.PaddingLeft;
|
|
|
- r.Top:=r.Top+ChildNode.BorderTop+ChildNode.PaddingTop;
|
|
|
- r.Right:=r.Left+Item.ContentBoxWidth;
|
|
|
- r.Bottom:=r.Top+Item.ContentBoxHeight;
|
|
|
- ChildEl.UsedContentBox:=r;
|
|
|
- ChildNode.Width:=r.Width;
|
|
|
- ChildNode.Height:=r.Height;
|
|
|
+ ChildNode.Right:=aBorderBox.Right+ChildNode.MarginRight;
|
|
|
+ ChildNode.Bottom:=aBorderBox.Bottom+ChildNode.MarginBottom;
|
|
|
+ ChildNode.Width:=aContentBox.Width;
|
|
|
+ ChildNode.Height:=aContentBox.Height;
|
|
|
|
|
|
{$IFDEF VerboseFresnelPlacing}
|
|
|
writeln('TFLFlowLayouter.PlaceLineItems '+ChildEl.GetPath+' BorderBox='+ChildEl.UsedBorderBox.ToString);
|
|
@@ -1767,10 +1960,12 @@ function TFLLineLayouter.PlaceAbsoluteItem(ChildNode: TUsedLayoutNode; aMode: TF
|
|
|
// returns right, bottom marginbox, so the parent can compute its needed content size
|
|
|
var
|
|
|
ChildEl: TFresnelElement;
|
|
|
- NewLeft, NewTop, NewRight, NewBottom, NewWidth, NewHeight, FrameLeft, FrameRight,
|
|
|
+ NewLeft, NewTop, NewRight, NewBottom, NewWidth, NewHeight,
|
|
|
+ FrameLeft, FrameRight,
|
|
|
FrameTop, FrameBottom: TFresnelLength;
|
|
|
- p: TFresnelPoint;
|
|
|
+ Size: TFresnelLayoutNode.TContentSize;
|
|
|
r: TFresnelRect;
|
|
|
+ p: TFresnelPoint;
|
|
|
begin
|
|
|
Result.X:=0;
|
|
|
Result.Y:=0;
|
|
@@ -1816,18 +2011,18 @@ begin
|
|
|
if IsNan(NewHeight) then
|
|
|
begin
|
|
|
// auto width and height
|
|
|
- p:=ChildEl.GetIntrinsicContentSize(aMode,ChildNode.MaxWidth,ChildNode.MaxHeight);
|
|
|
- NewWidth:=ChildNode.FitWidth(p.X);
|
|
|
- NewHeight:=ChildNode.FitHeight(p.Y);
|
|
|
+ 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
|
|
|
- p:=ChildEl.GetIntrinsicContentSize(aMode,ChildNode.MaxWidth,NewHeight);
|
|
|
- NewWidth:=ChildNode.FitWidth(p.X);
|
|
|
+ Size:=ChildNode.GetIntrinsicContentSize(aMode,ChildNode.MaxWidth,NewHeight);
|
|
|
+ NewWidth:=ChildNode.FitWidth(Size.Width);
|
|
|
end;
|
|
|
end else if IsNan(NewHeight) then begin
|
|
|
// width set, height auto
|
|
|
- p:=ChildEl.GetIntrinsicContentSize(aMode,NewWidth,ChildNode.MaxHeight);
|
|
|
- NewHeight:=ChildNode.FitHeight(p.Y);
|
|
|
+ Size:=ChildNode.GetIntrinsicContentSize(aMode,NewWidth,ChildNode.MaxHeight);
|
|
|
+ NewHeight:=ChildNode.FitHeight(Size.Height);
|
|
|
end;
|
|
|
|
|
|
FrameLeft:=ChildNode.MarginLeft+ChildNode.BorderLeft+ChildNode.PaddingLeft;
|
|
@@ -1874,7 +2069,6 @@ begin
|
|
|
ChildNode.Width:=NewWidth;
|
|
|
ChildNode.Height:=NewHeight;
|
|
|
|
|
|
- // used borderbox
|
|
|
r.Left:=NewLeft+ChildNode.MarginLeft;
|
|
|
r.Top:=NewTop+ChildNode.MarginTop;
|
|
|
r.Right:=NewLeft+FrameLeft+NewWidth+FrameRight-ChildNode.MarginRight;
|
|
@@ -1999,7 +2193,7 @@ begin
|
|
|
|
|
|
//writeln('TViewportLayouter.Layout ',Node.Element.GetPath,' Width=',FloatToCSSStr(Node.Width),' Height=',FloatToCSSStr(Node.Height),' Layouter=',DbgSName(Node.Layouter));
|
|
|
|
|
|
- // sort for z-index
|
|
|
+ // sort children for z-index
|
|
|
SortStackingContext(Node);
|
|
|
|
|
|
// compute used layout lengths of children, with access to used grand child lengths
|