|
@@ -223,16 +223,16 @@ type
|
|
|
procedure SetViewport(const AValue: TFresnelViewport);
|
|
|
protected
|
|
|
procedure ErrorLayout(const ID: int64; const Msg: string); virtual;
|
|
|
- procedure Layout(Node: TUsedLayoutNode); virtual;
|
|
|
+ procedure Layout(El: TFresnelElement); virtual;
|
|
|
function CreateLayoutNode(El: TFresnelElement): TUsedLayoutNode; virtual;
|
|
|
public
|
|
|
constructor Create(AOwner: TComponent); override;
|
|
|
procedure Apply; override;
|
|
|
procedure UpdateLayouter(El: TFresnelElement; LNode: TUsedLayoutNode); virtual;
|
|
|
- procedure UpdateLayoutParent(El: TFresnelElement; LNode: TUsedLayoutNode); virtual;
|
|
|
- procedure ComputeCSSLayoutNode(El: TFresnelElement); virtual; // called after basic CSS properties were computed, create the layout nodes
|
|
|
+ function GetContainer(El: TFresnelElement): TFresnelElement; virtual;
|
|
|
+ procedure UpdateLayoutNode(El: TFresnelElement); virtual; // called after basic CSS properties were computed, create the layout nodes
|
|
|
procedure SortStackingContext(LNode: TUsedLayoutNode); virtual; // apply z-index
|
|
|
- procedure WriteLayoutTree; // for debugging: write tree information to log
|
|
|
+ procedure WriteRenderingTree; // for debugging: write tree information to log
|
|
|
property Viewport: TFresnelViewport read FViewport write SetViewport;
|
|
|
// needs:
|
|
|
// viewport width, height, DPI, font size default, font size min
|
|
@@ -241,37 +241,28 @@ type
|
|
|
// measure text width
|
|
|
|
|
|
// produces:
|
|
|
- // layout tree
|
|
|
+ // layout rendering tree
|
|
|
// boxes
|
|
|
// margin, border, padding
|
|
|
// left, top, width, height
|
|
|
// clipping: left, top, right, bottom
|
|
|
end;
|
|
|
|
|
|
-function CompareLayoutNodesZIndexDomIndex(Item1, Item2{$IF FPC_FULLVERSION>=30301}, {%H-}Context{$ENDIF}: Pointer): integer;
|
|
|
+function CompareLayoutNodesZIndex(Item1, Item2{$IF FPC_FULLVERSION>=30301}, {%H-}Context{$ENDIF}: Pointer): integer;
|
|
|
|
|
|
implementation
|
|
|
|
|
|
-function CompareLayoutNodesZIndexDomIndex(Item1, Item2{$IF FPC_FULLVERSION>=30301}, {%H-}Context{$ENDIF}: Pointer): integer;
|
|
|
+function CompareLayoutNodesZIndex(Item1, Item2{$IF FPC_FULLVERSION>=30301}, {%H-}Context{$ENDIF}: Pointer): integer;
|
|
|
var
|
|
|
Node1: TUsedLayoutNode absolute Item1;
|
|
|
Node2: TUsedLayoutNode absolute Item2;
|
|
|
- DomIndex1, DomIndex2: Integer;
|
|
|
begin
|
|
|
if Node1.ZIndex>Node2.ZIndex then
|
|
|
Result:=1
|
|
|
else if Node1.ZIndex<Node2.ZIndex then
|
|
|
Result:=-1
|
|
|
- else begin
|
|
|
- DomIndex1:=Node1.Element.DOMIndex;
|
|
|
- DomIndex2:=Node2.Element.DOMIndex;
|
|
|
- if DomIndex1>DomIndex2 then
|
|
|
- Result:=1
|
|
|
- else if DomIndex1<DomIndex2 then
|
|
|
- Result:=-1
|
|
|
- else
|
|
|
- Result:=0;
|
|
|
- end;
|
|
|
+ else
|
|
|
+ Result:=0;
|
|
|
end;
|
|
|
|
|
|
{ TFLNodeLayouter }
|
|
@@ -439,9 +430,9 @@ end;
|
|
|
procedure TUsedLayoutNode.ApplyMinMaxWidth;
|
|
|
begin
|
|
|
if IsNan(Width) then exit;
|
|
|
- if Width<0 then Width:=0;
|
|
|
if (not IsNan(MaxWidth)) and (Width>MaxWidth) then
|
|
|
Width:=MaxWidth;
|
|
|
+ if Width<0 then Width:=0;
|
|
|
if (not IsNan(MinWidth)) and (Width<MinWidth) then
|
|
|
Width:=MinWidth;
|
|
|
end;
|
|
@@ -564,9 +555,6 @@ begin
|
|
|
begin
|
|
|
// left, top, right, bottom are used
|
|
|
|
|
|
- // either Left or Right must be set
|
|
|
- if IsNan(Left) and IsNan(Right) then
|
|
|
- Left:=0;
|
|
|
if IsNan(Width) then
|
|
|
begin
|
|
|
if (not IsNan(Left)) and (not IsNan(Right)) then
|
|
@@ -584,11 +572,14 @@ begin
|
|
|
// width is set
|
|
|
if IsNan(Left) then
|
|
|
begin
|
|
|
- // width & right -> left
|
|
|
- aContainerWidth:=Element.GetContainerContentWidth(true);
|
|
|
- if not IsNan(aContainerWidth) then
|
|
|
- Left:=aContainerWidth-Width-MarginLeft-BorderLeft-PaddingLeft
|
|
|
- -Right-MarginRight-BorderRight-PaddingRight;
|
|
|
+ if not IsNan(Right) then
|
|
|
+ begin
|
|
|
+ // width & right -> left
|
|
|
+ aContainerWidth:=Element.GetContainerContentWidth(true);
|
|
|
+ if not IsNan(aContainerWidth) then
|
|
|
+ Left:=aContainerWidth-Width-MarginLeft-BorderLeft-PaddingLeft
|
|
|
+ -Right-MarginRight-BorderRight-PaddingRight;
|
|
|
+ end;
|
|
|
end else if IsNan(Right) then begin
|
|
|
// width & left -> right
|
|
|
aContainerWidth:=Element.GetContainerContentWidth(true);
|
|
@@ -598,9 +589,6 @@ begin
|
|
|
end;
|
|
|
end;
|
|
|
|
|
|
- // either Top or Bottom must be set
|
|
|
- if IsNan(Top) and IsNan(Bottom) then
|
|
|
- Top:=0;
|
|
|
if IsNan(Height) then
|
|
|
begin
|
|
|
if (not IsNan(Top)) and (not IsNan(Bottom)) then
|
|
@@ -618,11 +606,14 @@ begin
|
|
|
// Height is set
|
|
|
if IsNan(Top) then
|
|
|
begin
|
|
|
- // Height & Bottom -> Top
|
|
|
- aContainerHeight:=Element.GetContainerContentHeight(true);
|
|
|
- if not IsNan(aContainerHeight) then
|
|
|
- Top:=aContainerHeight-Height-MarginTop-BorderTop-PaddingTop
|
|
|
- -Bottom-MarginBottom-BorderBottom-PaddingBottom;
|
|
|
+ if not IsNan(Bottom) then
|
|
|
+ begin
|
|
|
+ // Height & Bottom -> Top
|
|
|
+ aContainerHeight:=Element.GetContainerContentHeight(true);
|
|
|
+ if not IsNan(aContainerHeight) then
|
|
|
+ Top:=aContainerHeight-Height-MarginTop-BorderTop-PaddingTop
|
|
|
+ -Bottom-MarginBottom-BorderBottom-PaddingBottom;
|
|
|
+ end;
|
|
|
end else if IsNan(Bottom) then begin
|
|
|
// Height & Top -> Bottom
|
|
|
aContainerHeight:=Element.GetContainerContentHeight(true);
|
|
@@ -754,9 +745,9 @@ end;
|
|
|
function TFLFlowLayouter.ComputeLayoutContent(aMode: TFresnelLayoutMode; aMaxWidth,
|
|
|
aMaxHeight: TFresnelLength; Commit: boolean): TFresnelPoint;
|
|
|
var
|
|
|
- NodeIndex: Integer;
|
|
|
- ChildNode: TUsedLayoutNode;
|
|
|
- ChildEl: TFresnelElement;
|
|
|
+ ChildIndex: Integer;
|
|
|
+ ChildNode, LastFlowNode: TUsedLayoutNode;
|
|
|
+ ChildEl, LastFlowEl, El: TFresnelElement;
|
|
|
IsInline: Boolean;
|
|
|
NewChildRight, CurSpace, OldMarginBoxBottom,
|
|
|
ChildMBoxLeft, ChildMBoxRight { MarginBox relative to parent ContentBox left },
|
|
@@ -765,7 +756,7 @@ var
|
|
|
ChildMaxWidth, ChildMaxHeight { ContentBox, can be NaN },
|
|
|
ChildMarginLeft, ChildMarginRight, ChildMarginTop, ChildMarginBottom,
|
|
|
ChildPadBorderX, ChildPadBorderY: TFresnelLength;
|
|
|
- ChildPrefSize, ChildPrefPos: TFresnelPoint;
|
|
|
+ ChildPrefSize, ChildDefPos: TFresnelPoint;
|
|
|
|
|
|
procedure AddLineNodeCache;
|
|
|
var
|
|
@@ -795,41 +786,22 @@ begin
|
|
|
|
|
|
FLastLineBorderBoxBottom:=0;
|
|
|
FLastLineMarginBottom:=0;
|
|
|
+ El:=Node.Element;
|
|
|
|
|
|
// add elements to the line until full
|
|
|
StartLine;
|
|
|
- for NodeIndex:=0 to Node.NodeCount-1 do
|
|
|
+ for ChildIndex:=0 to El.NodeCount-1 do
|
|
|
begin
|
|
|
- ChildNode:=TUsedLayoutNode(Node.Nodes[NodeIndex]);
|
|
|
- ChildEl:=ChildNode.Element;
|
|
|
- //writeln('TFLFlowLayouter.ComputeLayoutBorderBox ',ChildEl.GetPath);
|
|
|
-
|
|
|
- // skip hidden
|
|
|
- if (ChildEl.ComputedVisibility=CSSRegistry.kwCollapse)
|
|
|
- or (ChildEl.ComputedDisplayOutside=CSSIDNone) then
|
|
|
- begin
|
|
|
- continue;
|
|
|
- end;
|
|
|
+ ChildEl:=El.Nodes[ChildIndex];
|
|
|
+ ChildNode:=TUsedLayoutNode(ChildEl.LayoutNode);
|
|
|
|
|
|
- IsInline:=ChildEl.ComputedDisplayOutside=CSSRegistry.kwInline;
|
|
|
+ if ChildNode.SkipLayout then continue;
|
|
|
|
|
|
// skip position: absolute and fixed
|
|
|
- case ChildEl.ComputedPosition of
|
|
|
- CSSRegistry.kwStatic,CSSRegistry.kwRelative,CSSRegistry.kwSticky: ;
|
|
|
- else
|
|
|
- // position is absolute or fixed
|
|
|
- if IsInline then
|
|
|
- begin
|
|
|
- ChildPrefPos.X:=FLineBorderBoxRight+FLineMarginRight;
|
|
|
- ChildPrefPos.Y:=FLineBorderBoxTop;
|
|
|
- end else begin
|
|
|
- ChildPrefPos.X:=0;
|
|
|
- ChildPrefPos.Y:=FLineMarginBottom;
|
|
|
- end;
|
|
|
- PlaceAbsoluteItem(ChildNode,aMode,aMaxWidth,aMaxHeight,ChildPrefPos,Commit);
|
|
|
-
|
|
|
+ if ChildEl.ComputedPosition in [CSSRegistry.kwAbsolute,CSSRegistry.kwFixed] then
|
|
|
continue;
|
|
|
- end;
|
|
|
+
|
|
|
+ IsInline:=ChildEl.ComputedDisplayOutside=CSSRegistry.kwInline;
|
|
|
|
|
|
// display-outside: inline or block
|
|
|
if (not IsInline) or (aMode in [flmMinWidth,flmMaxHeight]) then
|
|
@@ -852,7 +824,7 @@ begin
|
|
|
else
|
|
|
CurSpace:=Max(0,aMaxWidth-FLineBorderBoxRight-Max(FLineMarginRight,ChildNode.MarginLeft)-ChildNode.MarginRight);
|
|
|
|
|
|
- //writeln('TFLFlowLayouter.ComputeLayoutBorderBox ',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));
|
|
|
+ //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));
|
|
|
|
|
|
ChildMarginLeft:=ChildNode.MarginLeft;
|
|
|
ChildMarginRight:=ChildNode.MarginRight;
|
|
@@ -867,12 +839,12 @@ begin
|
|
|
ChildMinHeight:=ChildNode.MinHeight; // at least 0
|
|
|
ChildMaxHeight:=ChildNode.MaxHeight; // can be NaN
|
|
|
|
|
|
- //writeln('TFLFlowLayouter.ComputeLayoutBorderBox ',ChildEl.GetPath,' Commit=',Commit,' ChildWidth=',FloatToStr(ChildWidth),' ChildHeight=',FloatToStr(ChildHeight));
|
|
|
+ //writeln('TFLFlowLayouter.ComputeLayoutContent ',ChildEl.GetPath,' Commit=',Commit,' ChildWidth=',FloatToStr(ChildWidth),' ChildHeight=',FloatToStr(ChildHeight));
|
|
|
|
|
|
if Commit and (not IsInline) and IsNan(ChildWidth) and (not IsNan(CurSpace)) then
|
|
|
begin
|
|
|
// a block element expands the full line
|
|
|
- //writeln('TFLFlowLayouter.ComputeLayoutBorderBox BLOCK FULL LINE: ',ChildEl.GetPath,' CurSpace=',FloatToStr(CurSpace),' ChildPadBorderX=',FloatToStr(ChildPadBorderX));
|
|
|
+ //writeln('TFLFlowLayouter.ComputeLayoutContent BLOCK FULL LINE: ',ChildEl.GetPath,' CurSpace=',FloatToStr(CurSpace),' ChildPadBorderX=',FloatToStr(ChildPadBorderX));
|
|
|
ChildWidth:=CurSpace-ChildPadBorderX;
|
|
|
end;
|
|
|
if not IsNan(ChildWidth) then
|
|
@@ -898,7 +870,7 @@ begin
|
|
|
end;
|
|
|
ChildPrefSize:=ChildEl.GetIntrinsicContentSize(aMode,CurSpace,NaN);
|
|
|
//if ChildEl.Name='ButtonLabel' then
|
|
|
- // writeln('TFLFlowLayouter.ComputeLayoutBorderBox ',ChildEl.GetPath,' MaxWidth=',CurSpace,' Preferred=',ChildPrefSize.ToString,' ChildPadBorderX=',ChildPadBorderX,' ChildPadBorderY=',ChildPadBorderY);
|
|
|
+ // writeln('TFLFlowLayouter.ComputeLayoutContent ',ChildEl.GetPath,' MaxWidth=',CurSpace,' Preferred=',ChildPrefSize.ToString,' ChildPadBorderX=',ChildPadBorderX,' ChildPadBorderY=',ChildPadBorderY);
|
|
|
// apply min, max
|
|
|
if IsNan(ChildWidth) then
|
|
|
begin
|
|
@@ -932,7 +904,7 @@ begin
|
|
|
end;
|
|
|
end;
|
|
|
|
|
|
- //debugln(['TFLFlowLayouter.ComputeLayoutBorderBox ',ChildEl.Name,' ',ChildHeight,':=BT ',ChildBorderTop,'+PT ',ChildPaddingTop,'+H ',ChildHeight,'+PB ',ChildPaddingBottom,'+BB ',ChildBorderBottom]);
|
|
|
+ //debugln(['TFLFlowLayouter.ComputeLayoutContent ',ChildEl.Name,' ',ChildHeight,':=BT ',ChildBorderTop,'+PT ',ChildPaddingTop,'+H ',ChildHeight,'+PB ',ChildPaddingBottom,'+BB ',ChildBorderBottom]);
|
|
|
if FLineItems.Count=0 then
|
|
|
begin
|
|
|
// first element in line
|
|
@@ -951,7 +923,7 @@ begin
|
|
|
FLineBorderBoxLeft:=ChildMBoxLeft+ChildMarginLeft;
|
|
|
FLineBorderBoxRight:=ChildMBoxRight-ChildMarginRight;
|
|
|
FLineBorderBoxHeight:=ChildHeight+ChildPadBorderY;
|
|
|
- //writeln('TFLFlowLayouter.ComputeLayoutBorderBox ',ChildEl.Name,' FBorderLeft=',FloatToStr(FBorderLeft),' FPaddingLeft=',FloatToStr(FPaddingLeft),' ChildMarginLeft=',FloatToStr(ChildMarginLeft),' FLineBorderBoxLeft=',FloatToStr(FLineBorderBoxLeft),' ChildLeft=',FloatToStr(ChildMBoxLeft),' ChildRight=',FloatToStr(ChildMBoxRight),
|
|
|
+ //writeln('TFLFlowLayouter.ComputeLayoutContent ',ChildEl.Name,' FBorderLeft=',FloatToStr(FBorderLeft),' FPaddingLeft=',FloatToStr(FPaddingLeft),' ChildMarginLeft=',FloatToStr(ChildMarginLeft),' FLineBorderBoxLeft=',FloatToStr(FLineBorderBoxLeft),' ChildLeft=',FloatToStr(ChildMBoxLeft),' ChildRight=',FloatToStr(ChildMBoxRight),
|
|
|
// ' ChildLeft=',FloatToStr(ChildMBoxLeft),
|
|
|
// ' ChildMarginLeft=',FloatToStr(ChildMarginLeft),' ChildBorderLeft=',FloatToStr(ChildBorderLeft),' ChildPaddingLeft=',FloatToStr(ChildPaddingLeft),
|
|
|
// ' ChildWidth=',FloatToStr(ChildWidth),
|
|
@@ -991,10 +963,49 @@ begin
|
|
|
AddLineNodeCache;
|
|
|
end;
|
|
|
|
|
|
- //writeln('TFLFlowLayouter.ComputeLayoutBorderBox ',ChildEl.Name,' Commit=',Commit,' ChildLeft=',ChildMBoxLeft,' ChildRight=',ChildMBoxRight,' ChildWidth=',ChildWidth,' ChildHeight=',ChildHeight);
|
|
|
+ //writeln('TFLFlowLayouter.ComputeLayoutContent ',ChildEl.Name,' Commit=',Commit,' ChildLeft=',ChildMBoxLeft,' ChildRight=',ChildMBoxRight,' ChildWidth=',ChildWidth,' ChildHeight=',ChildHeight);
|
|
|
end;
|
|
|
EndLine(Commit);
|
|
|
|
|
|
+ if Commit then
|
|
|
+ begin
|
|
|
+ // place absolute items
|
|
|
+ LastFlowNode:=nil;
|
|
|
+ for ChildIndex:=0 to El.NodeCount-1 do
|
|
|
+ begin
|
|
|
+ ChildEl:=El.Nodes[ChildIndex];
|
|
|
+ ChildNode:=TUsedLayoutNode(ChildEl.LayoutNode);
|
|
|
+
|
|
|
+ if ChildNode.SkipLayout then continue;
|
|
|
+
|
|
|
+ case ChildEl.ComputedPosition of
|
|
|
+ CSSRegistry.kwStatic,CSSRegistry.kwRelative,CSSRegistry.kwSticky:
|
|
|
+ LastFlowNode:=ChildNode;
|
|
|
+ else
|
|
|
+ // position is absolute or fixed
|
|
|
+ IsInline:=ChildEl.ComputedDisplayOutside=CSSRegistry.kwInline;
|
|
|
+ if LastFlowNode=nil then
|
|
|
+ begin
|
|
|
+ ChildDefPos.X:=0;
|
|
|
+ ChildDefPos.Y:=0;
|
|
|
+ end else if IsInline then
|
|
|
+ begin
|
|
|
+ // next inline position
|
|
|
+ LastFlowEl:=LastFlowNode.Element;
|
|
|
+ ChildDefPos.X:=LastFlowEl.UsedBorderBox.Right+LastFlowNode.MarginRight;
|
|
|
+ ChildDefPos.Y:=LastFlowNode.Top;
|
|
|
+ end else begin
|
|
|
+ // next block position
|
|
|
+ LastFlowEl:=LastFlowNode.Element;
|
|
|
+ ChildDefPos.X:=0;
|
|
|
+ ChildDefPos.Y:=LastFlowEl.UsedBorderBox.Bottom+LastFlowNode.MarginBottom; // todo: use actual line height
|
|
|
+ end;
|
|
|
+
|
|
|
+ PlaceAbsoluteItem(ChildNode,aMode,aMaxWidth,aMaxHeight,ChildDefPos,Commit);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
Result.Y:=Max(Result.Y,FLastLineBorderBoxBottom+FLastLineMarginBottom);
|
|
|
end;
|
|
|
|
|
@@ -1438,9 +1449,9 @@ end;
|
|
|
function TFLFlexLayouter.ComputeLayoutContent(aMode: TFresnelLayoutMode; aMaxWidth,
|
|
|
aMaxHeight: TFresnelLength; Commit: boolean): TFresnelPoint;
|
|
|
var
|
|
|
- NodeIndex: Integer;
|
|
|
+ ChildIndex: Integer;
|
|
|
ChildNode: TUsedLayoutNode;
|
|
|
- ChildEl: TFresnelElement;
|
|
|
+ ChildEl, El: TFresnelElement;
|
|
|
Item: TFlexItem;
|
|
|
MaxMainSize: TFresnelLength; // max content size in main direction, can be NaN
|
|
|
CurMainSize: TFresnelLength; // current line size in main direction
|
|
@@ -1464,18 +1475,14 @@ begin
|
|
|
StartLine;
|
|
|
Item:=nil;
|
|
|
ItemAdded:=false;
|
|
|
+ El:=Node.Element;
|
|
|
try
|
|
|
- for NodeIndex:=0 to Node.NodeCount-1 do
|
|
|
+ for ChildIndex:=0 to El.NodeCount-1 do
|
|
|
begin
|
|
|
- ChildNode:=TUsedLayoutNode(Node.Nodes[NodeIndex]);
|
|
|
- ChildEl:=ChildNode.Element;
|
|
|
+ ChildEl:=El.Nodes[ChildIndex];
|
|
|
+ ChildNode:=TUsedLayoutNode(ChildEl.LayoutNode);
|
|
|
|
|
|
- // skip collapsed
|
|
|
- if (ChildEl.ComputedVisibility=CSSRegistry.kwCollapse)
|
|
|
- or (ChildEl.ComputedDisplayOutside=CSSIDNone) then
|
|
|
- begin
|
|
|
- continue;
|
|
|
- end;
|
|
|
+ if ChildNode.SkipLayout then continue;
|
|
|
|
|
|
// skip position: absolute and fixed
|
|
|
if ChildEl.ComputedPosition in [CSSRegistry.kwAbsolute,CSSRegistry.kwFixed] then
|
|
@@ -1600,15 +1607,23 @@ begin
|
|
|
ChildEl:=ChildNode.Element;
|
|
|
|
|
|
//if ChildEl.Name='Div1' then
|
|
|
- // writeln('TFLFlowLayouter.PlaceAbsoluteItem ',ChildEl.GetPath,' ',aMode,' Commit=',Commit,' Left=',FloatToCSSStr(ChildNode.Left),',Top=',FloatToCSSStr(ChildNode.Top),',Right=',FloatToCSSStr(ChildNode.Right),',Bottom=',FloatToCSSStr(ChildNode.Bottom),' Width=',FloatToCSSStr(ChildNode.Width),' Height=',FloatToCSSStr(ChildNode.Height));
|
|
|
+ writeln('TFLFlowLayouter.PlaceAbsoluteItem ',ChildEl.GetPath,' ',aMode,' Commit=',Commit,' Left=',FloatToCSSStr(ChildNode.Left),',Top=',FloatToCSSStr(ChildNode.Top),',Right=',FloatToCSSStr(ChildNode.Right),',Bottom=',FloatToCSSStr(ChildNode.Bottom),' Width=',FloatToCSSStr(ChildNode.Width),' Height=',FloatToCSSStr(ChildNode.Height),' Default=',FloatToCSSStr(DefaultPos.X),',',FloatToCSSStr(DefaultPos.Y));
|
|
|
NewLeft:=ChildNode.Left;
|
|
|
NewTop:=ChildNode.Top;
|
|
|
NewRight:=ChildNode.Right;
|
|
|
NewBottom:=ChildNode.Bottom;
|
|
|
+ p.X:=NaN;
|
|
|
if IsNan(NewLeft) and IsNan(NewRight) then
|
|
|
- NewLeft:=DefaultPos.X;
|
|
|
+ begin
|
|
|
+ p:=ChildEl.GetContainerOffset;
|
|
|
+ NewLeft:=p.X+DefaultPos.X;
|
|
|
+ end;
|
|
|
if IsNan(NewTop) and IsNan(NewBottom) then
|
|
|
- NewTop:=DefaultPos.Y;
|
|
|
+ begin
|
|
|
+ if IsNan(p.X) then
|
|
|
+ p:=ChildEl.GetContainerOffset;
|
|
|
+ NewTop:=p.y+DefaultPos.Y;
|
|
|
+ end;
|
|
|
|
|
|
if Commit then
|
|
|
begin
|
|
@@ -1777,27 +1792,32 @@ begin
|
|
|
raise EFresnelLayout.Create(s);
|
|
|
end;
|
|
|
|
|
|
-procedure TViewportLayouter.Layout(Node: TUsedLayoutNode);
|
|
|
+procedure TViewportLayouter.Layout(El: TFresnelElement);
|
|
|
var
|
|
|
i: Integer;
|
|
|
+ Node: TUsedLayoutNode;
|
|
|
+ ChildEl: TFresnelElement;
|
|
|
begin
|
|
|
+ Node:=TUsedLayoutNode(El.LayoutNode);
|
|
|
if Node.SkipLayout then exit;
|
|
|
|
|
|
//writeln('TViewportLayouter.Layout ',Node.Element.GetPath,' Width=',FloatToCSSStr(Node.Width),' Height=',FloatToCSSStr(Node.Height),' Layouter=',DbgSName(Node.Layouter));
|
|
|
|
|
|
+ // sort for z-index
|
|
|
+ SortStackingContext(Node);
|
|
|
+
|
|
|
// compute used layout lengths of children, with access to used grand child lengths
|
|
|
- for i:=0 to Node.NodeCount-1 do
|
|
|
- TUsedLayoutNode(Node.Nodes[i]).ComputeUsedLengths(false);
|
|
|
- //writeln('TViewportLayouter.Layout AFTER child ComputeUsedLengths');
|
|
|
+ for i:=0 to El.NodeCount-1 do
|
|
|
+ begin
|
|
|
+ ChildEl:=El.Nodes[i];
|
|
|
+ TUsedLayoutNode(ChildEl.LayoutNode).ComputeUsedLengths(false);
|
|
|
+ end;
|
|
|
|
|
|
if Node.Layouter<>nil then
|
|
|
Node.Layouter.Apply;
|
|
|
|
|
|
- for i:=0 to Node.NodeCount-1 do
|
|
|
- Layout(TUsedLayoutNode(Node.Nodes[i]));
|
|
|
-
|
|
|
- // sort for z-index
|
|
|
- SortStackingContext(Node);
|
|
|
+ for i:=0 to El.NodeCount-1 do
|
|
|
+ Layout(El.Nodes[i]);
|
|
|
end;
|
|
|
|
|
|
function TViewportLayouter.CreateLayoutNode(El: TFresnelElement
|
|
@@ -1825,7 +1845,7 @@ procedure TViewportLayouter.Apply;
|
|
|
Node: TUsedLayoutNode;
|
|
|
begin
|
|
|
// create or free layout nodes
|
|
|
- ComputeCSSLayoutNode(El);
|
|
|
+ UpdateLayoutNode(El);
|
|
|
El.ComputeCSSAfterLayoutNode(Self);
|
|
|
|
|
|
// compute used layout lengths like padding and width *without*
|
|
@@ -1873,7 +1893,7 @@ begin
|
|
|
|
|
|
// layout
|
|
|
VPNode.ComputeUsedLengths(false);
|
|
|
- Layout(VPNode);
|
|
|
+ Layout(Viewport);
|
|
|
end;
|
|
|
|
|
|
procedure TViewportLayouter.UpdateLayouter(El: TFresnelElement;
|
|
@@ -1909,58 +1929,49 @@ begin
|
|
|
end;
|
|
|
end;
|
|
|
|
|
|
-procedure TViewportLayouter.UpdateLayoutParent(El: TFresnelElement;
|
|
|
- LNode: TUsedLayoutNode);
|
|
|
-var
|
|
|
- ZIndexStr: String;
|
|
|
- ZIndexInt, {%H-}Code: Integer;
|
|
|
- NewParent: TFresnelElement;
|
|
|
- ParentLNode: TUsedLayoutNode;
|
|
|
+function TViewportLayouter.GetContainer(El: TFresnelElement): TFresnelElement;
|
|
|
begin
|
|
|
- //FLLog(etDebug,['TSimpleFresnelLayouter.UpdateLayoutParent ',El.GetPath]);
|
|
|
- if LNode.SkipLayout or (El.Parent=nil) then
|
|
|
- LNode.Parent:=nil
|
|
|
- else begin
|
|
|
- case El.ComputedPosition of
|
|
|
- CSSRegistry.kwAbsolute,
|
|
|
- CSSRegistry.kwFixed,
|
|
|
- CSSRegistry.kwRelative,
|
|
|
- CSSRegistry.kwSticky:
|
|
|
+ case El.ComputedPosition of
|
|
|
+ CSSRegistry.kwAbsolute:
|
|
|
+ begin
|
|
|
+ // the containing block is nearest ancestor element that has a position value other than static
|
|
|
+ Result:=El.Parent;
|
|
|
+ while (Result<>nil) do
|
|
|
begin
|
|
|
- ZIndexStr:=El.ComputedAttribute[fcaZIndex];
|
|
|
- if (ZIndexStr='') or (ZIndexStr='auto') then
|
|
|
- LNode.ZIndex:=0.0
|
|
|
- else begin
|
|
|
- ZIndexInt:=0;
|
|
|
- val(ZIndexStr,ZIndexInt,Code);
|
|
|
- LNode.ZIndex:=ZIndexInt;
|
|
|
- end;
|
|
|
- LNode.ZIndex:=LNode.ZIndex+0.1; // slightly higher than no position
|
|
|
+ if not (Result.ComputedPosition in [CSSRegistry.kwStatic,CSSIDNone]) then
|
|
|
+ exit;
|
|
|
+ // Note:check for transform<>'none'
|
|
|
+ Result:=Result.Parent;
|
|
|
end;
|
|
|
- else
|
|
|
- LNode.ZIndex:=0;
|
|
|
end;
|
|
|
-
|
|
|
- NewParent:=LNode.Container;
|
|
|
- while (NewParent<>nil) and (TUsedLayoutNode(NewParent.LayoutNode).Layouter=nil) do
|
|
|
- NewParent:=NewParent.Parent;
|
|
|
- if NewParent=nil then
|
|
|
- LNode.Parent:=nil
|
|
|
- else begin
|
|
|
- ParentLNode:=TUsedLayoutNode(NewParent.LayoutNode);
|
|
|
- LNode.Parent:=ParentLNode;
|
|
|
- if ParentLNode.Layouter=nil then
|
|
|
- ParentLNode.SubParent:=true;
|
|
|
+ CSSRegistry.kwFixed:
|
|
|
+ // viewport
|
|
|
+ else
|
|
|
+ // static, relative, sticky
|
|
|
+ // the containing block is the nearest ancestor element that is either a
|
|
|
+ // block container (such as an inline-block, block, or list-item element)
|
|
|
+ // or establishes a formatting context (such as a table container,
|
|
|
+ // flex container, grid container, or the block container itself)
|
|
|
+ Result:=El.Parent;
|
|
|
+ while (Result<>nil) do
|
|
|
+ begin
|
|
|
+ if Result.IsBlockFormattingContext then
|
|
|
+ exit;
|
|
|
+ Result:=Result.Parent;
|
|
|
end;
|
|
|
end;
|
|
|
+
|
|
|
+ // default viewport
|
|
|
+ if El.Parent=nil then
|
|
|
+ Result:=nil
|
|
|
+ else
|
|
|
+ Result:=Viewport;
|
|
|
end;
|
|
|
|
|
|
-procedure TViewportLayouter.ComputeCSSLayoutNode(El: TFresnelElement);
|
|
|
+procedure TViewportLayouter.UpdateLayoutNode(El: TFresnelElement);
|
|
|
var
|
|
|
LNode, ParentLNode: TUsedLayoutNode;
|
|
|
- {%H-}Code: integer;
|
|
|
begin
|
|
|
- //FLLog(etDebug,['TSimpleFresnelLayouter.ComputeCSS ',El.GetPath]);
|
|
|
// every node gets a layout node
|
|
|
LNode:=CreateLayoutNode(El);
|
|
|
|
|
@@ -1994,15 +2005,21 @@ begin
|
|
|
end;
|
|
|
end;
|
|
|
|
|
|
+ // rendering hierarchy
|
|
|
+ if (El.Parent=nil) or LNode.SkipRendering then
|
|
|
+ LNode.Parent:=nil
|
|
|
+ else
|
|
|
+ LNode.Parent:=El.Parent.LayoutNode;
|
|
|
+ if El.ComputedPosition<>CSSRegistry.kwStatic then
|
|
|
+ LNode.ZIndex:=TFresnelLength(El.GetComputedZIndex)+0.5
|
|
|
+ else
|
|
|
+ LNode.ZIndex:=0;
|
|
|
+
|
|
|
// layouter
|
|
|
UpdateLayouter(El,LNode);
|
|
|
|
|
|
// block container
|
|
|
- LNode.Container:=El.GetContainer;
|
|
|
- //FLLog(etDebug,['TSimpleFresnelLayouter.ComputeCSS ',El.Name,' BlockContainer=',LNode.Container.ToString]);
|
|
|
-
|
|
|
- // LayoutParent
|
|
|
- UpdateLayoutParent(El,LNode);
|
|
|
+ LNode.Container:=GetContainer(El);
|
|
|
|
|
|
// fetch basic CSS layout values
|
|
|
if LNode.Layouter<>nil then
|
|
@@ -2025,11 +2042,7 @@ procedure TViewportLayouter.SortStackingContext(LNode: TUsedLayoutNode
|
|
|
begin
|
|
|
dec(i);
|
|
|
Node:=TUsedLayoutNode(LNode.Nodes[i]);
|
|
|
- if (Node.ZIndex<NextNode.ZIndex) then
|
|
|
- // ok
|
|
|
- else if (Node.ZIndex>NextNode.ZIndex) then
|
|
|
- exit(false) // need sorting
|
|
|
- else if Node.Element.DOMIndex>NextNode.Element.DOMIndex then
|
|
|
+ if (Node.ZIndex>NextNode.ZIndex) then
|
|
|
exit(false); // need sorting
|
|
|
NextNode:=Node;
|
|
|
end;
|
|
@@ -2038,14 +2051,14 @@ procedure TViewportLayouter.SortStackingContext(LNode: TUsedLayoutNode
|
|
|
|
|
|
begin
|
|
|
if IsSorted then exit;
|
|
|
- LNode.SortNodes(@CompareLayoutNodesZIndexDomIndex);
|
|
|
+ LNode.SortNodes(@CompareLayoutNodesZIndex);
|
|
|
{$IFDEF VerboseFresnelLayouter}
|
|
|
if not IsSorted then
|
|
|
raise Exception.Create('20221031180116 TSimpleFresnelLayouter.SortLayoutNodes');
|
|
|
{$ENDIF}
|
|
|
end;
|
|
|
|
|
|
-procedure TViewportLayouter.WriteLayoutTree;
|
|
|
+procedure TViewportLayouter.WriteRenderingTree;
|
|
|
|
|
|
procedure WriteNode(const Prefix: string; Node: TUsedLayoutNode);
|
|
|
var
|