Browse Source

dom: started flex justify-content

mattias 11 months ago
parent
commit
1b3f587b94
3 changed files with 387 additions and 231 deletions
  1. 7 7
      src/base/fresnel.dom.pas
  2. 379 224
      src/base/fresnel.layouter.pas
  3. 1 0
      tests/base/TCFlowLayout.pas

+ 7 - 7
src/base/fresnel.dom.pas

@@ -4683,7 +4683,7 @@ begin
   AddFresnelLonghand(fcaBorderRightWidth,false,@CheckBorderWidth,'medium');
   AddFresnelLonghand(fcaBorderRightWidth,false,@CheckBorderWidth,'medium');
   AddFresnelLonghand(fcaBorderBottomWidth,false,@CheckBorderWidth,'medium');
   AddFresnelLonghand(fcaBorderBottomWidth,false,@CheckBorderWidth,'medium');
   AddFresnelLonghand(fcaBorderLeftWidth,false,@CheckBorderWidth,'medium');
   AddFresnelLonghand(fcaBorderLeftWidth,false,@CheckBorderWidth,'medium');
-  Chk_BorderWidth_Dim.AllowedUnits:=cuAllLengthsAndPercent;
+  Chk_BorderWidth_Dim.AllowedUnits:=cuAllLengths;
   Chk_BorderWidth_Dim.AllowFrac:=true;
   Chk_BorderWidth_Dim.AllowFrac:=true;
   Chk_BorderWidth_Dim.AllowedKeywordIDs:=[kwThin,kwMedium,kwThick];
   Chk_BorderWidth_Dim.AllowedKeywordIDs:=[kwThin,kwMedium,kwThick];
 
 
@@ -6757,7 +6757,7 @@ begin
   Resolver.InitParseAttr(s);
   Resolver.InitParseAttr(s);
   if not CSSRegistry.ReadJustifyContent(Resolver,false,Result,SubKW) then
   if not CSSRegistry.ReadJustifyContent(Resolver,false,Result,SubKW) then
   begin
   begin
-    Result:=0;
+    Result:=CSSRegistry.kwNormal;
     SubKW:=0;
     SubKW:=0;
   end;
   end;
 end;
 end;
@@ -6771,7 +6771,7 @@ begin
   Resolver.InitParseAttr(s);
   Resolver.InitParseAttr(s);
   if not CSSRegistry.ReadJustifyItems(Resolver,false,Result,SubKW) then
   if not CSSRegistry.ReadJustifyItems(Resolver,false,Result,SubKW) then
   begin
   begin
-    Result:=0;
+    Result:=CSSRegistry.kwLegacy;
     SubKW:=0;
     SubKW:=0;
   end;
   end;
 end;
 end;
@@ -6785,7 +6785,7 @@ begin
   Resolver.InitParseAttr(s);
   Resolver.InitParseAttr(s);
   if not CSSRegistry.ReadJustifySelf(Resolver,false,Result,SubKW) then
   if not CSSRegistry.ReadJustifySelf(Resolver,false,Result,SubKW) then
   begin
   begin
-    Result:=0;
+    Result:=CSSRegistry.kwAuto;
     SubKW:=0;
     SubKW:=0;
   end;
   end;
 end;
 end;
@@ -6799,7 +6799,7 @@ begin
   Resolver.InitParseAttr(s);
   Resolver.InitParseAttr(s);
   if not CSSRegistry.ReadAlignContent(Resolver,false,Result,SubKW) then
   if not CSSRegistry.ReadAlignContent(Resolver,false,Result,SubKW) then
   begin
   begin
-    Result:=0;
+    Result:=CSSRegistry.kwNormal;
     SubKW:=0;
     SubKW:=0;
   end;
   end;
 end;
 end;
@@ -6813,7 +6813,7 @@ begin
   Resolver.InitParseAttr(s);
   Resolver.InitParseAttr(s);
   if not CSSRegistry.ReadAlignItems(Resolver,false,Result,SubKW) then
   if not CSSRegistry.ReadAlignItems(Resolver,false,Result,SubKW) then
   begin
   begin
-    Result:=0;
+    Result:=CSSRegistry.kwNormal;
     SubKW:=0;
     SubKW:=0;
   end;
   end;
 end;
 end;
@@ -6827,7 +6827,7 @@ begin
   Resolver.InitParseAttr(s);
   Resolver.InitParseAttr(s);
   if not CSSRegistry.ReadAlignSelf(Resolver,false,Result,SubKW) then
   if not CSSRegistry.ReadAlignSelf(Resolver,false,Result,SubKW) then
   begin
   begin
-    Result:=0;
+    Result:=CSSRegistry.kwAuto;
     SubKW:=0;
     SubKW:=0;
   end;
   end;
 end;
 end;

+ 379 - 224
src/base/fresnel.layouter.pas

@@ -6,6 +6,35 @@
   for details about the license.
   for details about the license.
  *****************************************************************************
  *****************************************************************************
 
 
+Todo:
+- ComputeCSS: base attributes, like display, font
+- layout:
+  - create layouter
+  - resolve layout length properties: if not c value
+  - set used values (width, height, border
+
+- can margin/border/padding be computed before layout and stored?
+  - ? percentage  margin+padding
+  - ? calc(min-content/10)
+- utility functions:
+  - ContentToPaddingWidth, PaddingToContentWidth
+  - ContentToBorderWidth, BorderToContentWidth
+  - ContentToMarginWidth, BorderToMarginWidth
+- min-content GetMinContentWidth/Height
+  - needs
+  - replaced elements
+  - flow layout
+  - flex layout
+- max-content width/height
+  - replaced elements
+  - flow layout
+  - flex layout
+- fit-content(max) width/height
+  - replaced elements
+  - flow layout
+  - flex layout
+- content(max) width/height
+
 }
 }
 unit Fresnel.Layouter;
 unit Fresnel.Layouter;
 
 
@@ -106,7 +135,7 @@ type
         MaxHeight: TFresnelLength;
         MaxHeight: TFresnelLength;
 
 
         // computed layout
         // computed layout
-        BorderBox: TFresnelRect; // left, top, right, bottom
+        MarginBox: TFresnelRect; // left, top, right, bottom
         ContentBoxWidth: TFresnelLength;
         ContentBoxWidth: TFresnelLength;
         ContentBoxHeight: TFresnelLength;
         ContentBoxHeight: TFresnelLength;
       end;
       end;
@@ -190,10 +219,12 @@ type
 
 
       TFlexItem = class(TLineItem)
       TFlexItem = class(TLineItem)
       public
       public
-        Basis: TFresnelLength;
+        Basis: TFresnelLength; // including border+padding
         Grow: TFresnelLength;
         Grow: TFresnelLength;
         Shrink: TFresnelLength;
         Shrink: TFresnelLength;
-        CurMainContentSize: TFresnelLength;
+        MainContentSize: TFresnelLength;
+        CrossMarginBoxSize: TFresnelLength; // including margin
+        AlignSelf, AlignSelfSub: TCSSNumericalID;
       end;
       end;
 
 
   protected
   protected
@@ -201,10 +232,14 @@ type
     FlexWrap: TCSSNumericalID;
     FlexWrap: TCSSNumericalID;
     JustifyContent, JustifyContentSub: TCSSNumericalID;
     JustifyContent, JustifyContentSub: TCSSNumericalID;
     AlignItems, AlignItemsSub: TCSSNumericalID;
     AlignItems, AlignItemsSub: TCSSNumericalID;
+    MainIsRow: boolean; // flex-direction is row or row-reverse
+    MainIsReverse: boolean; // flex-direction is row-reverse or column-reverse
     procedure EndLine(Commit: boolean); virtual;
     procedure EndLine(Commit: boolean); virtual;
     procedure StartLine; virtual;
     procedure StartLine; virtual;
-    procedure FlexLineMainDirection(MaxMainSize, MainGap: TFresnelLength); virtual;
-    procedure FlexLineCrossDirection; virtual;
+    procedure FlexLineMainDirection(MaxMainSize, MainGap: TFresnelLength;
+      Commit: boolean; var NewContentWidth, NewContentHeight: TFresnelLength); virtual;
+    procedure FlexLineCrossDirection(Commit: boolean;
+      var NewContentWidth, NewContentHeight: TFresnelLength); virtual;
     procedure PlaceLineItems; override;
     procedure PlaceLineItems; override;
     procedure ComputeChildAttributes(Item: TLineItem; El: TFresnelElement); override;
     procedure ComputeChildAttributes(Item: TLineItem; El: TFresnelElement); override;
   public
   public
@@ -352,9 +387,9 @@ begin
     //ChildNode:=BFCNode.Node;
     //ChildNode:=BFCNode.Node;
     //ChildEl:=ChildNode.Element;
     //ChildEl:=ChildNode.Element;
 
 
-    BFCNode.BorderBox.Top:=FLineBorderBoxTop-BFCNode.MarginTop;
+    BFCNode.MarginBox.Top:=FLineBorderBoxTop-BFCNode.MarginTop;
     //writeln('TFLBlockFormattingContext.PlaceLineNodes ',i,' ',ChildEl.GetPath,' ',FloatToCSSStr(BFCNode.Left),' ',FloatToCSSStr(ChildTop),' BFCNode.Height=',FloatToCSSStr(BFCNode.ContentBoxHeight));
     //writeln('TFLBlockFormattingContext.PlaceLineNodes ',i,' ',ChildEl.GetPath,' ',FloatToCSSStr(BFCNode.Left),' ',FloatToCSSStr(ChildTop),' BFCNode.Height=',FloatToCSSStr(BFCNode.ContentBoxHeight));
-    BFCNode.BorderBox.Bottom:=FLineBorderBoxTop
+    BFCNode.MarginBox.Bottom:=FLineBorderBoxTop
                 +BFCNode.BorderTop+BFCNode.PaddingTop
                 +BFCNode.BorderTop+BFCNode.PaddingTop
                 +BFCNode.ContentBoxHeight
                 +BFCNode.ContentBoxHeight
                 +BFCNode.PaddingBottom+BFCNode.BorderBottom+BFCNode.MarginBottom;
                 +BFCNode.PaddingBottom+BFCNode.BorderBottom+BFCNode.MarginBottom;
@@ -453,8 +488,8 @@ var
     N.MarginRight:=ChildMarginRight;
     N.MarginRight:=ChildMarginRight;
     N.MarginTop:=ChildMarginTop;
     N.MarginTop:=ChildMarginTop;
     N.MarginBottom:=ChildMarginBottom;
     N.MarginBottom:=ChildMarginBottom;
-    N.BorderBox.Left:=ChildLeft;
-    N.BorderBox.Right:=ChildRight;
+    N.MarginBox.Left:=ChildLeft-ChildMarginLeft;
+    N.MarginBox.Right:=ChildRight+ChildMarginRight;
     N.ContentBoxWidth:=ChildWidth;
     N.ContentBoxWidth:=ChildWidth;
     N.ContentBoxHeight:=ChildHeight;
     N.ContentBoxHeight:=ChildHeight;
   end;
   end;
@@ -717,112 +752,304 @@ begin
 end;
 end;
 
 
 procedure TFLFlexLayouter.FlexLineMainDirection(MaxMainSize,
 procedure TFLFlexLayouter.FlexLineMainDirection(MaxMainSize,
-  MainGap: TFresnelLength);
+  MainGap: TFresnelLength; Commit: boolean; var NewContentWidth,
+  NewContentHeight: TFresnelLength);
 const
 const
-  MinAdjust = 0.005;
+  MinAdjust = 0.0001;
+var
+  CurMainSize, aSpace: TFresnelLength;
+
+  procedure CalcMainSize;
+  var
+    i: Integer;
+    Item: TFlexItem;
+  begin
+    CurMainSize:=0;
+    for i:=0 to FLineItems.Count-1 do
+    begin
+      Item:=TFlexItem(FLineItems[i]);
+      CurMainSize:=CurMainSize+Item.MarginLeft+Item.BorderLeft+Item.PaddingLeft
+        +Item.MainContentSize+Item.PaddingRight+Item.BorderRight+Item.MarginRight;
+      if i>0 then
+        CurMainSize:=CurMainSize+MainGap;
+    end;
+    aSpace:=MaxMainSize-CurMainSize;
+  end;
+
+  function CalcSpaceItem(Count: integer; StartSpaceMul: TFresnelLength;
+    out aPos: TFresnelLength): TFresnelLength;
+  begin
+    Result:=Max(0,aSpace)/Count;
+    aPos:=Result*StartSpaceMul;
+    if FlexDirection in [CSSRegistry.kwRowReverse,CSSRegistry.kwColumnReverse] then
+    begin
+      aPos:=MaxMainSize-aPos;
+      Result:=-Result;
+    end;
+  end;
+
 var
 var
-  CurMainSize, Dist, SumGrow, SumShrink, NewMainSize: TFresnelLength;
+  SumGrow, SumShrink, p, aSpaceItem: TFresnelLength;
   i: Integer;
   i: Integer;
   Item: TFlexItem;
   Item: TFlexItem;
 begin
 begin
   for i:=0 to FLineItems.Count-1 do
   for i:=0 to FLineItems.Count-1 do
   begin
   begin
     Item:=TFlexItem(FLineItems[i]);
     Item:=TFlexItem(FLineItems[i]);
-    Item.CurMainContentSize:=Item.Basis;
+    Item.MainContentSize:=Item.Basis;
   end;
   end;
-  if not IsNan(MaxMainSize) then
-  begin
-    repeat
-      // compute difference between current size and max size
-      CurMainSize:=0;
+  repeat
+    // compute difference between current size and max size
+    CalcMainSize;
+    if aSpace>MinAdjust then
+    begin
+      // try to grow
+      // collect grow factors of all items able to grow
+      SumGrow:=0;
       for i:=0 to FLineItems.Count-1 do
       for i:=0 to FLineItems.Count-1 do
       begin
       begin
         Item:=TFlexItem(FLineItems[i]);
         Item:=TFlexItem(FLineItems[i]);
-        CurMainSize:=CurMainSize+Item.MarginLeft+Item.BorderLeft+Item.PaddingLeft
-          +Item.CurMainContentSize+Item.PaddingRight+Item.BorderRight+Item.MarginRight;
-        if i>0 then
-          CurMainSize:=CurMainSize+MainGap;
+        if Item.Grow<MinAdjust then continue;
+        if (not IsNan(Item.MaxWidth))
+            and (Item.MainContentSize>Item.MaxWidth-MinAdjust) then continue;
+        SumGrow:=SumGrow+Item.Grow;
       end;
       end;
-      Dist:=MaxMainSize-CurMainSize;
-      if Dist>MinAdjust then
+      if SumGrow=0 then
+        break; // can not grow
+      // grow
+      for i:=0 to FLineItems.Count-1 do
       begin
       begin
-        // try to grow
-        // collect grow factors of all items able to grow
-        SumGrow:=0;
-        for i:=0 to FLineItems.Count-1 do
-        begin
-          Item:=TFlexItem(FLineItems[i]);
-          if Item.Grow<MinAdjust then continue;
-          SumGrow:=SumGrow+Item.Grow;
-        end;
-        if SumGrow=0 then
-          break; // can not grow
-        // grow
-        for i:=0 to FLineItems.Count-1 do
-        begin
-          Item:=TFlexItem(FLineItems[i]);
-          if Item.Grow<MinAdjust then continue;
-          Item.CurMainContentSize:=Item.CurMainContentSize+(Item.Grow/SumGrow)*Dist;
-        end;
-      end else if Dist<-MinAdjust then begin
-        // try to shrink
-        // collect shrink factors of all items able to shrink
-        SumShrink:=0;
-        for i:=0 to FLineItems.Count-1 do
-        begin
-          Item:=TFlexItem(FLineItems[i]);
-          if Item.Shrink<MinAdjust then continue;
-          if Item.CurMainContentSize<MinAdjust then continue;
-          SumShrink:=SumShrink+Item.Shrink;
-        end;
-        if SumShrink=0 then
-          break; // can not shrink
-        // shrink
-        for i:=0 to FLineItems.Count-1 do
-        begin
-          Item:=TFlexItem(FLineItems[i]);
-          if Item.Shrink<MinAdjust then continue;
-          if Item.CurMainContentSize<MinAdjust then continue;
-          Item.CurMainContentSize:=Max(0,Item.CurMainContentSize+(Item.Shrink/SumShrink)*Dist);
-        end;
-      end else
-        break;
-    until false;
+        Item:=TFlexItem(FLineItems[i]);
+        if Item.Grow<MinAdjust then continue;
+        if (not IsNan(Item.MaxWidth))
+            and (Item.MainContentSize>Item.MaxWidth-MinAdjust) then continue;
+        Item.MainContentSize:=Item.MainContentSize+(Item.Grow/SumGrow)*aSpace;
+        if (not IsNan(Item.MaxWidth))
+            and (Item.MainContentSize>Item.MaxWidth) then
+          Item.MainContentSize:=Item.MaxWidth;
+      end;
+    end else if aSpace<-MinAdjust then begin
+      // try to shrink
+      // collect shrink factors of all items able to shrink
+      SumShrink:=0;
+      for i:=0 to FLineItems.Count-1 do
+      begin
+        Item:=TFlexItem(FLineItems[i]);
+        if Item.Shrink<MinAdjust then continue;
+        if Item.MainContentSize<MinAdjust then continue;
+        if (not IsNan(Item.MinWidth))
+            and (Item.MainContentSize<Item.MinWidth+MinAdjust) then continue;
+        SumShrink:=SumShrink+Item.Shrink;
+      end;
+      if SumShrink=0 then
+        break; // can not shrink
+      // shrink
+      for i:=0 to FLineItems.Count-1 do
+      begin
+        Item:=TFlexItem(FLineItems[i]);
+        if Item.Shrink<MinAdjust then continue;
+        if Item.MainContentSize<MinAdjust then continue;
+        if (not IsNan(Item.MinWidth))
+            and (Item.MainContentSize<Item.MinWidth+MinAdjust) then continue;
+        Item.MainContentSize:=Max(0,Item.MainContentSize+(Item.Shrink/SumShrink)*aSpace);
+        if (not IsNan(Item.MinWidth))
+            and (Item.MainContentSize<Item.MinWidth) then
+          Item.MainContentSize:=Item.MinWidth;
+      end;
+    end else
+      break;
+  until false;
+
+  // justify-content:
+  // todo  safe: ?
+  //   unsafe: ?
+
+  CalcMainSize;
+  p:=0;
+  aSpaceItem:=0;
+  case JustifyContent of
+  CSSRegistry.kwLeft: ;
+    //   left: in row/row-reverse move line left, in column: top
+  CSSRegistry.kwRight:
+    //   right: in row/row-reverse move line right, in column: top
+    if MainIsRow then
+      p:=MaxMainSize;
+  CSSRegistry.kwStart: ;
+    //   start: move line left or top
+  CSSRegistry.kwEnd:
+    //   end: move line right or bottom
+    p:=MaxMainSize;
+  CSSRegistry.kwFlexStart,
+  CSSRegistry.kwStretch: ;
+    //   flex-start: main start
+  CSSRegistry.kwFlexEnd:
+    //   flex-end: main end
+    p:=MaxMainSize;
+  CSSRegistry.kwCenter:
+    //   center: main center
+    begin
+      p:=(MaxMainSize-CurMainSize)/2;
+      if MainIsReverse then
+        p:=MaxMainSize-p;
+    end;
+  CSSRegistry.kwSpaceAround:
+    //   space-around: distribute space between each pair and half at start and end
+    CalcSpaceItem(FLineItems.Count,0.5,p);
+  CSSRegistry.kwSpaceBetween:
+    //   space-between: distribute space between each pair
+    CalcSpaceItem(Max(1,FLineItems.Count-1),0,p);
+  CSSRegistry.kwSpaceEvenly:
+    //   space-evenly: distribute space between each pair and full at start and end
+    CalcSpaceItem(FLineItems.Count+1,1,p);
   end;
   end;
 
 
-  // todo: justify-content / align-content
   for i:=0 to FLineItems.Count-1 do
   for i:=0 to FLineItems.Count-1 do
   begin
   begin
     Item:=TFlexItem(FLineItems[i]);
     Item:=TFlexItem(FLineItems[i]);
-    NewMainSize:=Item.CurMainContentSize;
-    if FlexDirection in [CSSRegistry.kwRow,CSSRegistry.kwRowReverse] then
-    begin
-      if not IsNan(Item.MaxWidth) then
-        NewMainSize:=Min(NewMainSize,Item.MarginLeft+Item.BorderLeft+Item.PaddingLeft
-                          +Item.MaxWidth
-                          +Item.PaddingRight+Item.BorderRight+Item.MarginRight);
-      if not IsNan(Item.MinWidth) then
-        NewMainSize:=Max(NewMainSize,Item.MarginLeft+Item.BorderLeft+Item.PaddingLeft
-                          +Item.MinWidth
-                          +Item.PaddingRight+Item.BorderRight+Item.MarginRight);
-    end else begin
-      if not IsNan(Item.MaxHeight) then
-        NewMainSize:=Min(NewMainSize,Item.MarginTop+Item.BorderTop+Item.PaddingTop
-                          +Item.MaxHeight
-                          +Item.PaddingBottom+Item.BorderBottom+Item.MarginBottom);
-      if not IsNan(Item.MinHeight) then
-        NewMainSize:=Max(NewMainSize,Item.MarginTop+Item.BorderTop+Item.PaddingTop
-                          +Item.MinHeight
-                          +Item.PaddingBottom+Item.BorderBottom+Item.MarginBottom);
+    case FlexDirection of
+    CSSRegistry.kwRow:
+      begin
+        Item.MarginBox.Left:=p;
+        p:=p+Item.MainContentSize;
+        Item.MarginBox.Right:=p;
+      end;
+    CSSRegistry.kwRowReverse:
+      begin
+        Item.MarginBox.Right:=p;
+        p:=p-Item.MainContentSize;
+        Item.MarginBox.Left:=p;
+      end;
+    CSSRegistry.kwColumn:
+      begin
+        Item.MarginBox.Top:=p;
+        p:=p+Item.MainContentSize;
+        Item.MarginBox.Bottom:=p;
+      end;
+    CSSRegistry.kwColumnReverse:
+      begin
+        Item.MarginBox.Bottom:=p;
+        p:=p-Item.MainContentSize;
+        Item.MarginBox.Top:=p;
+      end;
+    end;
+
+    case JustifyContent of
+    CSSRegistry.kwSpaceAround:
+      //   space-around: distribute space between each pair and half at start and end
+      if i<FLineItems.Count-1 then
+        p:=p+aSpaceItem
+      else
+        p:=p+aSpaceItem/2;
+    CSSRegistry.kwSpaceBetween:
+      //   space-between: distribute space between each pair
+      if i<FLineItems.Count-1 then
+        p:=p+aSpaceItem;
+    CSSRegistry.kwSpaceEvenly:
+      //   space-evenly: distribute space between each pair and full at start and end
+      p:=p+aSpaceItem;
     end;
     end;
   end;
   end;
+
+  if MainIsRow then
+    NewContentWidth:=p
+  else
+    NewContentHeight:=p;
 end;
 end;
 
 
-procedure TFLFlexLayouter.FlexLineCrossDirection;
+procedure TFLFlexLayouter.FlexLineCrossDirection(Commit: boolean;
+  var NewContentWidth, NewContentHeight: TFresnelLength);
+var
+  i: Integer;
+  Item: TFlexItem;
+  Size, MaxSize, StartPos, EndPos: TFresnelLength;
 begin
 begin
   // all items on line now have their size and position in main direction
   // all items on line now have their size and position in main direction
   // -> now compute their cross direction size and position
   // -> now compute their cross direction size and position
-  // todo
+
+  // find biggest
+  MaxSize:=0;
+  for i:=0 to FLineItems.Count-1 do
+  begin
+    Item:=TFlexItem(FLineItems[i]);
+    //El:=Item.Node.Element;
+    if MainIsRow then
+    begin
+      Size:=Item.Height;
+      if IsNan(Size) then
+        Size:=100; // get content size using the item's main size
+      Size:=Size+Item.MarginTop+Item.BorderTop+Item.PaddingTop
+             +Item.PaddingBottom+Item.BorderBottom+Item.MarginBottom;
+    end else begin
+      Size:=Item.Width;
+      if IsNan(Size) then
+        Size:=100; // get content size using the item's main size
+      Size:=Size+Item.MarginLeft+Item.BorderLeft+Item.PaddingLeft
+            +Item.PaddingRight+Item.BorderRight+Item.MarginRight;
+    end;
+    Item.CrossMarginBoxSize:=Size;
+
+    MaxSize:=Max(MaxSize,Size);
+  end;
+
+  // align items
+  for i:=0 to FLineItems.Count-1 do
+  begin
+    Item:=TFlexItem(FLineItems[i]);
+    //El:=Item.Node.Element;
+    StartPos:=0;
+    EndPos:=Item.CrossMarginBoxSize;
+    case Item.AlignSelf of
+    CSSRegistry.kwLeft,
+    CSSRegistry.kwStart,
+    CSSRegistry.kwFlexStart:
+      ;
+    CSSRegistry.kwRight,
+    CSSRegistry.kwEnd,
+    CSSRegistry.kwFlexEnd:
+      begin
+        StartPos:=MaxSize-Item.CrossMarginBoxSize;
+        EndPos:=MaxSize;
+      end;
+    CSSRegistry.kwCenter:
+      begin
+        StartPos:=(MaxSize-Item.CrossMarginBoxSize)/2;
+        EndPos:=StartPos+Item.CrossMarginBoxSize;
+      end;
+    // todo: baseline, first baseline, last baseline
+    else
+      //CSSRegistry.kwNormal,
+      //CSSRegistry.kwStretch:
+      begin
+        EndPos:=MaxSize;
+        if MainIsRow then
+        begin
+          if IsNan(Item.MaxHeight) and (EndPos<Item.MaxHeight) then
+            EndPos:=Item.MaxHeight;
+          if IsNan(Item.MinHeight) and (EndPos>Item.MinHeight) then
+            EndPos:=Item.MinHeight;
+        end else begin
+          if IsNan(Item.MaxWidth) and (EndPos<Item.MaxWidth) then
+            EndPos:=Item.MaxWidth;
+          if IsNan(Item.MinWidth) and (EndPos>Item.MinWidth) then
+            EndPos:=Item.MinWidth;
+        end;
+      end;
+    end;
+
+    if MainIsRow then
+    begin
+      Item.MarginBox.Top:=StartPos;
+      Item.MarginBox.Bottom:=EndPos;
+    end else begin
+      Item.MarginBox.Left:=StartPos;
+      Item.MarginBox.Right:=EndPos;
+    end;
+  end;
+
+  if MainIsRow then
+    NewContentHeight:=MaxSize
+  else
+    NewContentWidth:=MaxSize;
 end;
 end;
 
 
 procedure TFLFlexLayouter.PlaceLineItems;
 procedure TFLFlexLayouter.PlaceLineItems;
@@ -842,11 +1069,14 @@ begin
   FlexItem.Basis:=El.GetComputedLength(fcaFlexBasis,true);
   FlexItem.Basis:=El.GetComputedLength(fcaFlexBasis,true);
   FlexItem.Grow:=El.GetComputedLength(fcaFlexGrow,true);
   FlexItem.Grow:=El.GetComputedLength(fcaFlexGrow,true);
   FlexItem.Shrink:=El.GetComputedLength(fcaFlexShrink,true);
   FlexItem.Shrink:=El.GetComputedLength(fcaFlexShrink,true);
+  FlexItem.AlignSelf:=El.GetComputedAlignSelf(FlexItem.AlignSelfSub);
+  if FlexItem.AlignSelf=CSSRegistry.kwAuto then
+    FlexItem.AlignSelf:=AlignItems;
 
 
   if IsNan(FlexItem.Basis) then
   if IsNan(FlexItem.Basis) then
   begin
   begin
-    if FlexDirection in [CSSRegistry.kwRow,CSSRegistry.kwRowReverse]
-    then begin
+    if MainIsRow then
+    begin
       FlexItem.Basis:=FlexItem.Width;
       FlexItem.Basis:=FlexItem.Width;
       if IsNan(FlexItem.Basis) then
       if IsNan(FlexItem.Basis) then
         FlexItem.Basis:=El.GetMaxWidthContentBox;
         FlexItem.Basis:=El.GetMaxWidthContentBox;
@@ -910,49 +1140,13 @@ begin
   inherited Init;
   inherited Init;
   El:=Node.Element;
   El:=Node.Element;
   FlexDirection:=El.GetComputedKeyword(fcaFlexDirection,CSSRegistry.Chk_FlexDirection_KeywordIDs);
   FlexDirection:=El.GetComputedKeyword(fcaFlexDirection,CSSRegistry.Chk_FlexDirection_KeywordIDs);
+  MainIsRow:=FlexDirection in [CSSRegistry.kwRow,CSSRegistry.kwRowReverse];
+  MainIsReverse:=FlexDirection in [CSSRegistry.kwRowReverse,CSSRegistry.kwColumnReverse];
   FlexWrap:=El.GetComputedKeyword(fcaFlexWrap,CSSRegistry.Chk_FlexWrap_KeywordIDs);
   FlexWrap:=El.GetComputedKeyword(fcaFlexWrap,CSSRegistry.Chk_FlexWrap_KeywordIDs);
   JustifyContent:=El.GetComputedJustifyContent(JustifyContentSub);
   JustifyContent:=El.GetComputedJustifyContent(JustifyContentSub);
-  AlignItems:=El.GetComputedJustifyContent(AlignItemsSub);
+  AlignItems:=El.GetComputedAlignItems(AlignItemsSub);
   //if El.ComputedDirection; // todo direction
   //if El.ComputedDirection; // todo direction
 
 
-  case FlexDirection of
-  CSSRegistry.kwRow:
-    case JustifyContent of
-    CSSRegistry.kwLeft,
-    CSSRegistry.kwStart: JustifyContent:=CSSRegistry.kwFlexStart;
-    CSSRegistry.kwRight,
-    CSSRegistry.kwEnd: JustifyContent:=CSSRegistry.kwFlexEnd;
-    end;
-  CSSRegistry.kwRowReverse:
-    case JustifyContent of
-    CSSRegistry.kwRight,
-    CSSRegistry.kwEnd: JustifyContent:=CSSRegistry.kwFlexStart;
-    CSSRegistry.kwLeft,
-    CSSRegistry.kwStart: JustifyContent:=CSSRegistry.kwFlexEnd;
-    end;
-  CSSRegistry.kwColumn:
-    case JustifyContent of
-    CSSRegistry.kwLeft,
-    CSSRegistry.kwRight,
-    CSSRegistry.kwStart: JustifyContent:=CSSRegistry.kwFlexStart;
-    CSSRegistry.kwEnd: JustifyContent:=CSSRegistry.kwFlexEnd;
-    end;
-  CSSRegistry.kwColumnReverse:
-    case JustifyContent of
-    CSSRegistry.kwLeft,
-    CSSRegistry.kwRight,
-    CSSRegistry.kwEnd: JustifyContent:=CSSRegistry.kwFlexStart;
-    CSSRegistry.kwStart: JustifyContent:=CSSRegistry.kwFlexEnd;
-    end;
-  end;
-  case JustifyContent of
-  CSSRegistry.kwLeft:;
-  //kwNormal,kwStretch,
-  //  kwStart,kwEnd,kwFlexStart,kwFlexEnd,kwCenter,kwLeft,kwRight,
-  //  kwSpaceBetween,kwSpaceAround,kwSpaceEvenly,
-  //  kwSafe,kwUnsafe
-  end;
-
   FGap[false]:=NaN;
   FGap[false]:=NaN;
   FGap[true]:=NaN;
   FGap[true]:=NaN;
 end;
 end;
@@ -995,51 +1189,32 @@ procedure TFLFlexLayouter.ComputeLayout(MaxContentWidth, MaxContentHeight: TFres
 var
 var
   NodeIndex: Integer;
   NodeIndex: Integer;
   ChildNode: TSimpleFresnelLayoutNode;
   ChildNode: TSimpleFresnelLayoutNode;
-  ChildEl, El, Container: TFresnelElement;
-  FlexNode: TFlexItem;
+  ChildEl: TFresnelElement;
+  Item: TFlexItem;
   MaxMainSize: TFresnelLength; // max content size in main direction
   MaxMainSize: TFresnelLength; // max content size in main direction
-  CurMainSize: TFresnelLength; // current line size in main direction  without ending margin
-  CurMainMargin: TFresnelLength; // current ending margin in main direction
-  MainGap: TFresnelLength; // gap in main direction
-  Fits: Boolean;
+  CurMainSize: TFresnelLength; // current line size in main direction
+  NewMainSize, MainGap: TFresnelLength; // gap in main direction
 begin
 begin
-  El:=Node.Element;
   NewContentWidth:=0;
   NewContentWidth:=0;
   NewContentHeight:=0;
   NewContentHeight:=0;
 
 
-  if FlexDirection in [CSSRegistry.kwRow,CSSRegistry.kwRowReverse] then
+  if MainIsRow then
   begin
   begin
-    if IsNan(MaxContentWidth) then
-      MaxMainSize:=ContentWidth
-    else
-      MaxMainSize:=MaxContentWidth;
+    MaxMainSize:=MaxContentWidth;
     MainGap:=GetComputedGap(true);
     MainGap:=GetComputedGap(true);
   end else begin
   end else begin
-    if IsNan(MaxContentHeight) then
-      MaxMainSize:=ContentHeight
-    else
-      MaxMainSize:=MaxContentHeight;
+    MaxMainSize:=MaxContentHeight;
     MainGap:=GetComputedGap(false);
     MainGap:=GetComputedGap(false);
   end;
   end;
   if IsNan(MaxMainSize) then
   if IsNan(MaxMainSize) then
-  begin
-    Container:=El.GetBlockContainer;
-    if Container<>nil then
-    begin
-      if FlexDirection in [CSSRegistry.kwRow,CSSRegistry.kwRowReverse] then
-        MaxMainSize:=Container.GetComputedLength(fcaWidth,true) // todo Container.BoxSizing
-      else
-        MaxMainSize:=Container.GetComputedLength(fcaHeight,true); // todo Container.BoxSizing
-    end;
-  end;
+    raise EFresnelLayout.Create('20240908163359');
 
 
   CurMainSize:=0;
   CurMainSize:=0;
-  CurMainMargin:=0;
 
 
   // add elements to the line until full
   // add elements to the line until full
   StartLine;
   StartLine;
   NodeIndex:=0;
   NodeIndex:=0;
-  FlexNode:=nil;
+  Item:=nil;
   try
   try
     while NodeIndex<Node.NodeCount do
     while NodeIndex<Node.NodeCount do
     begin
     begin
@@ -1064,44 +1239,25 @@ begin
       end;
       end;
 
 
       if FLineItems=nil then FLineItems:=TFPList.Create;
       if FLineItems=nil then FLineItems:=TFPList.Create;
-      FlexNode:=TFlexItem.Create;
-      ComputeChildAttributes(FlexNode,ChildEl);
+      Item:=TFlexItem.Create;
+      ComputeChildAttributes(Item,ChildEl);
 
 
       if FLineItems.Count>0 then
       if FLineItems.Count>0 then
       begin
       begin
-        Fits:=true;
-        if (FLineItems.Count>0) and (FlexWrap<>CSSRegistry.kwNoWrap) then
+        if MainIsRow then
+          NewMainSize:=CurMainSize+MainGap
+            +Item.MarginRight+Item.Basis+Item.MarginLeft
+        else
+          NewMainSize:=CurMainSize+MainGap
+            +Item.MarginBottom+Item.Basis+Item.MarginTop;
+
+        if (FLineItems.Count=1) or (FlexWrap=CSSRegistry.kwNoWrap)
+            or (NewMainSize<=MaxMainSize) then
         begin
         begin
-          // todo check if node fits into line
-        end;
-
-        if Fits then
-        begin
-          case FlexDirection of
-          CSSRegistry.kwRow:
-            begin
-              CurMainSize:=CurMainSize+MainGap+CurMainMargin+FlexNode.MarginLeft+FlexNode.Basis;
-              CurMainMargin:=FlexNode.MarginRight;
-            end;
-          CSSRegistry.kwRowReverse:
-            begin
-              CurMainSize:=CurMainSize+MainGap+CurMainMargin+FlexNode.MarginRight+FlexNode.Basis;
-              CurMainMargin:=FlexNode.MarginLeft;
-            end;
-          CSSRegistry.kwColumn:
-            begin
-              CurMainSize:=CurMainSize+MainGap+CurMainMargin+FlexNode.MarginTop+FlexNode.Basis;
-              CurMainMargin:=FlexNode.MarginBottom;
-            end;
-          CSSRegistry.kwColumnReverse:
-            begin
-              CurMainSize:=CurMainSize+MainGap+CurMainMargin+FlexNode.MarginBottom+FlexNode.Basis;
-              CurMainMargin:=FlexNode.MarginTop;
-            end;
-          end;
+          CurMainSize:=NewMainSize;
         end else begin
         end else begin
-          FlexLineMainDirection(MaxMainSize,MainGap);
-          FlexLineCrossDirection;
+          FlexLineMainDirection(MaxMainSize,MainGap,Commit,NewContentWidth,NewContentHeight);
+          FlexLineCrossDirection(Commit,NewContentWidth,NewContentHeight);
           PlaceLineItems;
           PlaceLineItems;
           ClearLineItems;
           ClearLineItems;
         end;
         end;
@@ -1110,29 +1266,11 @@ begin
       if FLineItems.Count=0 then
       if FLineItems.Count=0 then
       begin
       begin
         // first element in line
         // first element in line
-        FLineItems.Add(FlexNode);
-        case FlexDirection of
-        CSSRegistry.kwRow:
-          begin
-            CurMainSize:=FlexNode.MarginLeft+FlexNode.Basis;
-            CurMainMargin:=FlexNode.MarginRight;
-          end;
-        CSSRegistry.kwRowReverse:
-          begin
-            CurMainSize:=FlexNode.MarginRight+FlexNode.Basis;
-            CurMainMargin:=FlexNode.MarginLeft;
-          end;
-        CSSRegistry.kwColumn:
-          begin
-            CurMainSize:=FlexNode.MarginTop+FlexNode.Basis;
-            CurMainMargin:=FlexNode.MarginBottom;
-          end;
-        CSSRegistry.kwColumnReverse:
-          begin
-            CurMainSize:=FlexNode.MarginBottom+FlexNode.Basis;
-            CurMainMargin:=FlexNode.MarginTop;
-          end;
-        end;
+        FLineItems.Add(Item);
+        if MainIsRow then
+          CurMainSize:=Item.MarginRight+Item.Basis+Item.MarginLeft
+        else
+          CurMainSize:=Item.MarginTop+Item.Basis+Item.MarginBottom;
       end;
       end;
 
 
 
 
@@ -1141,7 +1279,7 @@ begin
     EndLine(Commit);
     EndLine(Commit);
 
 
   finally
   finally
-    FlexNode.Free;
+    Item.Free;
   end;
   end;
 
 
 end;
 end;
@@ -1220,22 +1358,39 @@ end;
 procedure TFLLineLayouter.PlaceLineItems;
 procedure TFLLineLayouter.PlaceLineItems;
 var
 var
   i: Integer;
   i: Integer;
-  LineNode: TLineItem;
+  Item: TLineItem;
   ChildNode: TSimpleFresnelLayoutNode;
   ChildNode: TSimpleFresnelLayoutNode;
   ChildEl: TFresnelElement;
   ChildEl: TFresnelElement;
+  aWidth, aHeight: TFresnelLength;
 begin
 begin
   for i:=0 to FLineItems.Count-1 do
   for i:=0 to FLineItems.Count-1 do
   begin
   begin
-    LineNode:=TLineItem(FLineItems[i]);
-    ChildNode:=LineNode.Node;
+    Item:=TLineItem(FLineItems[i]);
+    ChildNode:=Item.Node;
     ChildEl:=ChildNode.Element;
     ChildEl:=ChildNode.Element;
 
 
-    ChildEl.ComputedAttribute[fcaLeft]:=FloatToCSSPx(LineNode.BorderBox.Left);
-    ChildEl.ComputedAttribute[fcaRight]:=FloatToCSSPx(LineNode.BorderBox.Right);
-    ChildEl.ComputedAttribute[fcaTop]:=FloatToCSSPx(LineNode.BorderBox.Top);
-    ChildEl.ComputedAttribute[fcaBottom]:=FloatToCSSPx(LineNode.BorderBox.Bottom);
-    ChildEl.ComputedAttribute[fcaWidth]:=FloatToCSSPx(LineNode.ContentBoxWidth);
-    ChildEl.ComputedAttribute[fcaHeight]:=FloatToCSSPx(LineNode.ContentBoxHeight);
+    ChildEl.ComputedAttribute[fcaLeft]:=FloatToCSSPx(Item.MarginBox.Left);
+    ChildEl.ComputedAttribute[fcaRight]:=FloatToCSSPx(Item.MarginBox.Right);
+    ChildEl.ComputedAttribute[fcaTop]:=FloatToCSSPx(Item.MarginBox.Top);
+    ChildEl.ComputedAttribute[fcaBottom]:=FloatToCSSPx(Item.MarginBox.Bottom);
+
+    aWidth:=Item.ContentBoxWidth;
+    aHeight:=Item.ContentBoxHeight;
+    case ChildEl.ComputedBoxSizing of
+    CSSRegistry.kwBorderBox:
+      begin
+        aWidth:=aWidth+Item.BorderLeft+Item.PaddingLeft+Item.PaddingRight+Item.BorderRight;
+        aHeight:=aHeight+Item.BorderTop+Item.PaddingTop+Item.PaddingBottom+Item.BorderBottom;
+      end;
+    CSSRegistry.kwPaddingBox:
+      begin
+        aWidth:=aWidth+Item.PaddingLeft+Item.PaddingRight;
+        aHeight:=aHeight+Item.PaddingTop+Item.PaddingBottom;
+      end;
+    end;
+
+    ChildEl.ComputedAttribute[fcaWidth]:=FloatToCSSPx(aWidth);
+    ChildEl.ComputedAttribute[fcaHeight]:=FloatToCSSPx(aHeight);
 
 
     //writeln(
     //writeln(
     //  'TFLBlockFormattingContext.PlaceLineNodes '+ChildEl.GetPath+
     //  'TFLBlockFormattingContext.PlaceLineNodes '+ChildEl.GetPath+

+ 1 - 0
tests/base/TCFlowLayout.pas

@@ -17,6 +17,7 @@ type
     procedure TestFlowLayout_SliderRangePoint;
     procedure TestFlowLayout_SliderRangePoint;
     // todo: test padding-left,right,top,bottom percentage uses container's width
     // todo: test padding-left,right,top,bottom percentage uses container's width
     // todo: test margin-left,right,top,bottom percentage uses container's width
     // todo: test margin-left,right,top,bottom percentage uses container's width
+    // todo: test div position:absolute, child div: margin-left: 10%; width: 20px
   end;
   end;