|
@@ -64,6 +64,9 @@ type
|
|
|
procedure ApplyMinMaxHeight; virtual;
|
|
|
procedure ApplyMinMaxWidth; virtual;
|
|
|
procedure ComputeUsedLengths(NoChildren: boolean); virtual;
|
|
|
+ procedure ComputeScrollbarGutter; virtual; // compute attribute scrollbar-gutter
|
|
|
+ procedure ComputeScrollbarsNoChildren; virtual;
|
|
|
+ procedure AddScrollGutter(Horizontal: boolean);
|
|
|
procedure DeductUsedLengths(NoChildren: boolean); virtual;
|
|
|
function GetIntrinsicContentSize(aMode: TFresnelLayoutMode; aMaxWidth: TFresnelLength=NaN;
|
|
|
aMaxHeight: TFresnelLength=NaN): TFresnelPoint; override;
|
|
@@ -244,20 +247,70 @@ end;
|
|
|
|
|
|
procedure TFLNodeLayouter.Apply;
|
|
|
var
|
|
|
- MaxWidth, MaxHeight: TFresnelLength;
|
|
|
+ MaxWidth, MaxHeight, Avail: TFresnelLength;
|
|
|
Size: TFresnelPoint;
|
|
|
+ VP: TFresnelViewport;
|
|
|
+ NeedScrollX, NeedScrollY: Boolean;
|
|
|
+
|
|
|
+ procedure CheckScrollY;
|
|
|
+ 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;
|
|
|
+
|
|
|
begin
|
|
|
- MaxWidth:=Node.Width;
|
|
|
- if IsNan(MaxWidth) then
|
|
|
- MaxWidth:=Node.MaxWidth;
|
|
|
- MaxHeight:=Node.Height;
|
|
|
- if IsNan(MaxHeight) then
|
|
|
- MaxHeight:=Node.MaxWidth;
|
|
|
+ // 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;
|
|
|
+
|
|
|
+ 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:=Size.X;
|
|
|
+ Node.Width:=Node.FitWidth(Size.X);
|
|
|
if IsNan(Node.Height) then
|
|
|
- Node.Height:=Size.Y;
|
|
|
+ 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
|
|
|
+ begin
|
|
|
+ Node.AddScrollGutter(true);
|
|
|
+ CheckScrollY;
|
|
|
+ end;
|
|
|
+ {$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));
|
|
|
+ {$ENDIF}
|
|
|
+ end;
|
|
|
+
|
|
|
+ // todo: ScrollLeft, ScrollTop
|
|
|
+ // todo: scroll anchor
|
|
|
end;
|
|
|
|
|
|
procedure TFLNodeLayouter.Init;
|
|
@@ -486,12 +539,11 @@ begin
|
|
|
Right:=ComputeDistance(fcaRight);
|
|
|
Bottom:=ComputeDistance(fcaBottom);
|
|
|
|
|
|
- if NoChildren then begin
|
|
|
- if Element.ComputedOverflowX=CSSRegistry.kwScroll then
|
|
|
- ScrollBarHorizontalVisible:=true;
|
|
|
- if Element.ComputedOverflowY=CSSRegistry.kwScroll then
|
|
|
- ScrollBarVerticalVisible:=true;
|
|
|
- end;
|
|
|
+ CanScrollX:=Element.ComputedOverflowX in [CSSRegistry.kwScroll,CSSRegistry.kwAuto,CSSRegistry.kwHidden];
|
|
|
+ CanScrollY:=Element.ComputedOverflowY in [CSSRegistry.kwScroll,CSSRegistry.kwAuto,CSSRegistry.kwHidden];
|
|
|
+
|
|
|
+ if NoChildren then
|
|
|
+ ComputeScrollbarsNoChildren;
|
|
|
|
|
|
// compute width and height after border and padding
|
|
|
MinWidth:=GetContentSize(fcaMinWidth,true);
|
|
@@ -505,14 +557,102 @@ begin
|
|
|
|
|
|
DeductUsedLengths(NoChildren);
|
|
|
|
|
|
- if Element.ComputedOverflowX=CSSRegistry.kwAuto then
|
|
|
- begin
|
|
|
+ {$IFDEF VerboseFresnelPlacing}
|
|
|
+ writeln('TUsedLayoutNode.ComputeUsedLengths ',Element.GetPath,' NoChildren=',NoChildren,' Width=',FloatToCSSStr(Width),' Height=',FloatToCSSStr(Height),' MinWidth=',FloatToCSSStr(MinWidth),' MaxWidth=',FloatToCSSStr(MaxWidth));
|
|
|
+ {$ENDIF}
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TUsedLayoutNode.ComputeScrollbarGutter;
|
|
|
+var
|
|
|
+ AttrID, KW: TCSSNumericalID;
|
|
|
+ Complete: boolean;
|
|
|
+ aValue: String;
|
|
|
+ aComp: TCSSResCompValue;
|
|
|
+begin
|
|
|
+ AttrID:=CSSRegistry.FresnelAttrs[fcaScrollbarGutter].Index;
|
|
|
+ aValue:=Element.GetCSSString(AttrID,false,Complete);
|
|
|
+ aComp.EndP:=PChar(aValue);
|
|
|
+ repeat
|
|
|
+ if not Element.Resolver.ReadComp(aComp) then break;
|
|
|
+ if (aComp.Kind=rvkKeyword) then
|
|
|
+ begin
|
|
|
+ KW:=aComp.KeywordID;
|
|
|
+ case KW of
|
|
|
+ CSSRegistry.kwStable: ScrollGutterStable:=true;
|
|
|
+ CSSRegistry.kwBothEdges: ScrollGutterBothEdges:=true;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ until false;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TUsedLayoutNode.ComputeScrollbarsNoChildren;
|
|
|
|
|
|
+ procedure CalcGutter(ComputedOverflow: TCSSNumericalID; Horizontal: boolean);
|
|
|
+ var
|
|
|
+ GutterNeeded: Boolean;
|
|
|
+ begin
|
|
|
+ GutterNeeded:=false;
|
|
|
+ case ComputedOverflow of
|
|
|
+ CSSRegistry.kwScroll:
|
|
|
+ GutterNeeded:=true;
|
|
|
+ CSSRegistry.kwAuto,CSSRegistry.kwHidden:
|
|
|
+ GutterNeeded:=ScrollGutterStable;
|
|
|
+ end;
|
|
|
+ if GutterNeeded then
|
|
|
+ AddScrollGutter(Horizontal);
|
|
|
end;
|
|
|
|
|
|
- {$IFDEF VerboseFresnelPlacing}
|
|
|
- writeln('TUsedLayoutNode.ComputeUsedLengths ',Element.GetPath,' NoChildren=',NoChildren,' Width=',FloatToCSSStr(Width),' Height=',FloatToCSSStr(Height),' MinWidth=',FloatToCSSStr(MinWidth),' MaxWidth=',FloatToCSSStr(MaxWidth));
|
|
|
+begin
|
|
|
+ if Element.Viewport.ScrollbarsOverlay then exit;
|
|
|
+ // scrollbars require gutter space
|
|
|
+ // A gutter is needed, when ...
|
|
|
+ // overflow-x is scroll -> check here
|
|
|
+ // overflow-x is auto or hidden and scrollbar-gutter is stable -> check here
|
|
|
+ // overflow-x is auto and content does not fit -> check later
|
|
|
+
|
|
|
+ if CanScrollX or CanScrollY then
|
|
|
+ begin
|
|
|
+ ComputeScrollbarGutter;
|
|
|
+ if ScrollGutterWidth=CSSIDNone then
|
|
|
+ ScrollGutterWidth:=Element.GetComputedKeyword(fcaScrollbarWidth,CSSRegistry.Chk_ScrollbarWidth_KeywordIDs);
|
|
|
+ if ScrollGutterWidth=CSSIDNone then
|
|
|
+ ScrollGutterWidth:=CSSRegistry.kwAuto;
|
|
|
+ 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);
|
|
|
{$ENDIF}
|
|
|
+
|
|
|
+ CalcGutter(Element.ComputedOverflowX,true);
|
|
|
+ CalcGutter(Element.ComputedOverflowY,false);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TUsedLayoutNode.AddScrollGutter(Horizontal: boolean);
|
|
|
+var
|
|
|
+ VP: TFresnelViewport;
|
|
|
+ GutterW: TFresnelLength;
|
|
|
+begin
|
|
|
+ VP:=Element.Viewport;
|
|
|
+ case ScrollGutterWidth of
|
|
|
+ CSSRegistry.kwThin:
|
|
|
+ GutterW:=VP.ScrollbarsThinWidth[Horizontal];
|
|
|
+ CSSRegistry.kwAuto:
|
|
|
+ GutterW:=VP.ScrollbarsWidth[Horizontal];
|
|
|
+ else
|
|
|
+ exit; // none
|
|
|
+ end;
|
|
|
+
|
|
|
+ if Horizontal then
|
|
|
+ ScrollGutterBottom:=GutterW
|
|
|
+ else
|
|
|
+ ScrollGutterRight:=GutterW;
|
|
|
+ if ScrollGutterBothEdges then
|
|
|
+ begin
|
|
|
+ if Horizontal then
|
|
|
+ ScrollGutterTop:=GutterW
|
|
|
+ else
|
|
|
+ ScrollGutterLeft:=GutterW;
|
|
|
+ end;
|
|
|
end;
|
|
|
|
|
|
procedure TUsedLayoutNode.DeductUsedLengths(NoChildren: boolean);
|
|
@@ -1668,29 +1808,26 @@ begin
|
|
|
NewHeight:=NaN;
|
|
|
end;
|
|
|
|
|
|
- // Note: The DeductUsedLengths has already computed simple cases like
|
|
|
- // given container's width and child's left and right => child's width
|
|
|
+ // Note: The DeductUsedLengths has already computed simple cases
|
|
|
+ // e.g. given container's width and child's left and right => child's width
|
|
|
|
|
|
- if IsNan(NewWidth) or IsNan(NewHeight) then
|
|
|
+ if IsNan(NewWidth) then
|
|
|
begin
|
|
|
- if IsNan(NewWidth) then
|
|
|
+ if IsNan(NewHeight) then
|
|
|
begin
|
|
|
- if IsNan(NewHeight) then
|
|
|
- begin
|
|
|
- // auto width and height
|
|
|
- p:=ChildEl.GetIntrinsicContentSize(aMode,ChildNode.MaxWidth,ChildNode.MaxHeight);
|
|
|
- end else begin
|
|
|
- // height set, width auto
|
|
|
- p:=ChildEl.GetIntrinsicContentSize(aMode,ChildNode.MaxWidth,NewHeight);
|
|
|
- end;
|
|
|
- end else begin
|
|
|
- // width set, height auto
|
|
|
- p:=ChildEl.GetIntrinsicContentSize(aMode,NewWidth,ChildNode.MaxHeight);
|
|
|
- end;
|
|
|
- if IsNan(NewWidth) then
|
|
|
+ // auto width and height
|
|
|
+ p:=ChildEl.GetIntrinsicContentSize(aMode,ChildNode.MaxWidth,ChildNode.MaxHeight);
|
|
|
NewWidth:=ChildNode.FitWidth(p.X);
|
|
|
- if IsNan(NewHeight) then
|
|
|
NewHeight:=ChildNode.FitHeight(p.Y);
|
|
|
+ end else begin
|
|
|
+ // height set, width auto
|
|
|
+ p:=ChildEl.GetIntrinsicContentSize(aMode,ChildNode.MaxWidth,NewHeight);
|
|
|
+ NewWidth:=ChildNode.FitWidth(p.X);
|
|
|
+ end;
|
|
|
+ end else if IsNan(NewHeight) then begin
|
|
|
+ // width set, height auto
|
|
|
+ p:=ChildEl.GetIntrinsicContentSize(aMode,NewWidth,ChildNode.MaxHeight);
|
|
|
+ NewHeight:=ChildNode.FitHeight(p.Y);
|
|
|
end;
|
|
|
|
|
|
FrameLeft:=ChildNode.MarginLeft+ChildNode.BorderLeft+ChildNode.PaddingLeft;
|