Browse Source

flex: test one item

mattias 10 months ago
parent
commit
fa25a878c5
3 changed files with 166 additions and 105 deletions
  1. 3 3
      src/base/fresnel.dom.pas
  2. 99 99
      src/base/fresnel.layouter.pas
  3. 64 3
      tests/base/TCFlexLayout.pas

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

@@ -1184,8 +1184,8 @@ type
     property ComputedAttribute[Attr: TFresnelCSSAttribute]: string read GetComputedString write SetComputedCSSString;
     property ComputedBoxSizing: TCSSNumericalID read FComputedBoxSizing;
     property ComputedDirection: TCSSNumericalID read FComputedDirection;
-    property ComputedDisplayInside: TCSSNumericalID read FComputedDisplayInside;
-    property ComputedDisplayOutside: TCSSNumericalID read FComputedDisplayOutside;
+    property ComputedDisplayInside: TCSSNumericalID read FComputedDisplayInside; // none, flow, flow-root, flex, etc
+    property ComputedDisplayOutside: TCSSNumericalID read FComputedDisplayOutside; // none, block, inline
     property ComputedPosition: TCSSNumericalID read FComputedPosition;
     property ComputedVisibility: TCSSNumericalID read FComputedVisibility;
     property ComputedWritingMode: TCSSNumericalID read FComputedWritingMode;
@@ -4716,7 +4716,7 @@ begin
 
   // display
   Chk_DisplayBox_KeywordIDs:=[kwContents,kwNone];
-  Chk_DisplayInside_KeywordIDs:=[kwFlow,kwFlowRoot];
+  Chk_DisplayInside_KeywordIDs:=[kwFlow,kwFlowRoot,kwFlex];
   Chk_DisplayOutside_KeywordIDs:=[kwBlock,kwInline];
 
   AddFresnelLonghand(fcaDisplay,false,@CheckDisplay,'inline');

+ 99 - 99
src/base/fresnel.layouter.pas

@@ -44,8 +44,6 @@ unit Fresnel.Layouter;
 {$WARN 6060 off} // Case statement does not handle all possible cases
 {$ENDIF}
 
-{$DEFINE VerboseFlexBox}
-
 interface
 
 uses
@@ -534,7 +532,9 @@ begin
 
   DeductUsedLengths(NoChildren);
 
-  //writeln('TUsedLayoutNode.ComputeUsedLengths ',Element.GetPath,' NoChildren=',NoChildren,' Width=',FloatToCSSStr(Width),' Height=',FloatToCSSStr(Height),' MinWidth=',FloatToCSSStr(MinWidth),' MaxWidth=',FloatToCSSStr(MaxWidth));
+  {$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.DeductUsedLengths(NoChildren: boolean);
@@ -998,13 +998,6 @@ begin
   EndLine(Commit);
 
   Result.Y:=Max(Result.Y,FLastLineBorderBoxBottom+FLastLineMarginBottom);
-  if Commit then
-  begin
-    if IsNan(Node.Width) then
-      Node.Width:=Node.FitWidth(Result.X);
-    if IsNan(Node.Height) then
-      Node.Height:=Node.FitHeight(Result.Y);
-  end;
 end;
 
 { TFLGridLayouter }
@@ -1059,7 +1052,9 @@ var
       Item:=TFlexItem(FLineItems[i]);
       CurMainSize:=CurMainSize+Item.MainFrameLT+Item.MainContentSize+Item.MainFrameRB;
     end;
-    if not IsNan(MaxMainSize) then
+    if IsNan(MaxMainSize) then
+      aSpace:=0
+    else
       aSpace:=MaxMainSize-CurMainSize;
   end;
 
@@ -1095,92 +1090,90 @@ begin
     Item.MainContentSize:=Item.Basis;
   end;
 
-  if IsNan(MaxMainSize) then
-  begin
-    // computing min-content or max-content
-    CalcMainSize;
-    AdjustMainContentSize(CurMainSize);
-  end else begin
-    // shrink or grow
-    repeat
-      // compute difference between current size and max size
+  CalcMainSize;
+  // shrink or grow
+  repeat
+    // compute difference between current size and max size
+    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
+      begin
+        Item:=TFlexItem(FLineItems[i]);
+        if Item.Grow<MinAdjust then continue;
+        ItemNode:=Item.Node;
+        if (not IsNan(ItemNode.MaxWidth))
+            and (Item.MainContentSize>ItemNode.MaxWidth-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;
+        ItemNode:=Item.Node;
+        if (not IsNan(ItemNode.MaxWidth))
+            and (Item.MainContentSize>ItemNode.MaxWidth-MinAdjust) then continue;
+        Item.MainContentSize:=Item.MainContentSize+(Item.Grow/SumGrow)*aSpace;
+        if (not IsNan(ItemNode.MaxWidth))
+            and (Item.MainContentSize>ItemNode.MaxWidth-MinAdjust) then
+          Item.MainContentSize:=ItemNode.MaxWidth;
+      end;
       CalcMainSize;
-      if aSpace>MinAdjust then
+    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
-        // 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;
-          ItemNode:=Item.Node;
-          if (not IsNan(ItemNode.MaxWidth))
-              and (Item.MainContentSize>ItemNode.MaxWidth-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;
-          ItemNode:=Item.Node;
-          if (not IsNan(ItemNode.MaxWidth))
-              and (Item.MainContentSize>ItemNode.MaxWidth-MinAdjust) then continue;
-          Item.MainContentSize:=Item.MainContentSize+(Item.Grow/SumGrow)*aSpace;
-          if (not IsNan(ItemNode.MaxWidth))
-              and (Item.MainContentSize>ItemNode.MaxWidth-MinAdjust) then
-            Item.MainContentSize:=ItemNode.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;
-          ItemNode:=Item.Node;
-          if (not IsNan(ItemNode.MinWidth))
-              and (Item.MainContentSize<ItemNode.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;
-          ItemNode:=Item.Node;
-          if (not IsNan(ItemNode.MinWidth))
-              and (Item.MainContentSize<ItemNode.MinWidth+MinAdjust) then continue;
-          Item.MainContentSize:=Max(0,Item.MainContentSize+(Item.Shrink/SumShrink)*aSpace);
-          if (not IsNan(ItemNode.MinWidth))
-              and (Item.MainContentSize<ItemNode.MinWidth+MinAdjust) then
-            Item.MainContentSize:=ItemNode.MinWidth;
-        end;
-      end else
-        break;
-    until false;
+        Item:=TFlexItem(FLineItems[i]);
+        if Item.Shrink<MinAdjust then continue;
+        if Item.MainContentSize<MinAdjust then continue;
+        ItemNode:=Item.Node;
+        if (not IsNan(ItemNode.MinWidth))
+            and (Item.MainContentSize<ItemNode.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;
+        ItemNode:=Item.Node;
+        if (not IsNan(ItemNode.MinWidth))
+            and (Item.MainContentSize<ItemNode.MinWidth+MinAdjust) then continue;
+        Item.MainContentSize:=Max(0,Item.MainContentSize+(Item.Shrink/SumShrink)*aSpace);
+        if (not IsNan(ItemNode.MinWidth))
+            and (Item.MainContentSize<ItemNode.MinWidth+MinAdjust) then
+          Item.MainContentSize:=ItemNode.MinWidth;
+      end;
+      CalcMainSize;
+    end else
+      break;
+  until false;
 
-    // justify-content:
-    // todo  safe, unsafe: ?
+  // justify-content:
+  // todo  safe, unsafe: ?
 
-    CalcMainSize;
-    if Commit then
-    begin
-      if JustifyContent in [CSSRegistry.kwLeft,CSSRegistry.kwStart,CSSRegistry.kwFlexStart] then
-        AdjustMainContentSize(CurMainSize)
-      else
-        AdjustMainContentSize(MaxMainSize);
-    end else
-      AdjustMainContentSize(CurMainSize);
+  if Commit then
+  begin
+    if IsNan(MaxMainSize)
+        or (JustifyContent in [CSSRegistry.kwLeft,CSSRegistry.kwStart,CSSRegistry.kwFlexStart]) then
+      AdjustMainContentSize(CurMainSize)
+    else
+      AdjustMainContentSize(MaxMainSize);
+  end else
+    AdjustMainContentSize(CurMainSize);
 
+  if Commit then
+  begin
     p:=0;
     aSpaceItem:=0;
     case JustifyContent of
@@ -1225,7 +1218,7 @@ begin
       if FlexDirection in [CSSRegistry.kwRow,CSSRegistry.kwColumn] then
       begin
         if i>0 then p:=p+MainGap;
-        if FlexDirection=CSSRegistry.kwRow then
+        if MainIsRow then
           Item.StaticLeft:=p
         else
           Item.StaticTop:=p;
@@ -1233,11 +1226,15 @@ begin
       end else begin
         if i>0 then p:=p-MainGap;
         p:=p-(Item.MainFrameLT+Item.MainContentSize+Item.MainFrameRB);
-        if FlexDirection=CSSRegistry.kwRow then
+        if MainIsRow then
           Item.StaticLeft:=p
         else
           Item.StaticTop:=p;
       end;
+      if MainIsRow then
+        Item.ContentBoxWidth:=Item.MainContentSize
+      else
+        Item.ContentBoxHeight:=Item.MainContentSize;
 
       case JustifyContent of
       CSSRegistry.kwSpaceAround:
@@ -1463,8 +1460,6 @@ begin
     MaxMainSize:=aMaxHeight;
     MainGap:=GetComputedGap(false);
   end;
-  if IsNan(MaxMainSize) then
-    raise EFresnelLayout.Create('20240908163359');
 
   CurMainSize:=0;
 
@@ -1496,6 +1491,7 @@ begin
       end;
 
       Item:=TFlexItem.Create;
+      Item.Node:=ChildNode;
       ItemAdded:=false;
       ComputeChildAttributes(Item,ChildEl);
 
@@ -1585,7 +1581,9 @@ begin
     r.Bottom:=r.Top+Item.ContentBoxHeight;
     ChildEl.UsedContentBox:=r;
 
-    //writeln('TFLFlowLayouter.PlaceLineItems '+ChildEl.GetPath+' BorderBox='+ChildEl.UsedBorderBox.ToString);
+    {$IFDEF VerboseFresnelPlacing}
+    writeln('TFLFlowLayouter.PlaceLineItems '+ChildEl.GetPath+' BorderBox='+ChildEl.UsedBorderBox.ToString);
+    {$ENDIF}
   end;
 end;
 
@@ -1708,9 +1706,11 @@ begin
     r.Bottom:=R.Top+NewHeight;
     ChildEl.UsedContentBox:=r;
 
-    //writeln('TFLFlowLayouter.PlaceAbsoluteItem '+ChildEl.GetPath+' BorderBox='+ChildEl.UsedBorderBox.ToString);
+    {$IFDEF VerboseFresnelPlacing}
+    writeln('TFLFlowLayouter.PlaceAbsoluteItem '+ChildEl.GetPath+' BorderBox='+ChildEl.UsedBorderBox.ToString);
     //if ChildEl.Name='Div1' then
-    //  writeln('TFLFlowLayouter.PlaceAbsoluteItem ',ChildEl.GetPath,' ',aMode,' 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 Left=',FloatToCSSStr(ChildNode.Left),',Top=',FloatToCSSStr(ChildNode.Top),',Right=',FloatToCSSStr(ChildNode.Right),',Bottom=',FloatToCSSStr(ChildNode.Bottom),' Width=',FloatToCSSStr(ChildNode.Width),' Height=',FloatToCSSStr(ChildNode.Height));
+    {$ENDIF}
   end;
 end;
 

+ 64 - 3
tests/base/TCFlexLayout.pas

@@ -14,7 +14,8 @@ type
   TTestFlexLayout = class(TCustomTestFresnelCSS)
   published
     procedure TestFlexLayout_Empty;
-    // todo procedure TestFlexLayout_Empty_FlexInline;
+    procedure TestFlexLayout_Empty_FlexInline;
+    procedure TestFlexLayout_Row_OneItem;
     // todo: test flex-direction:row, flex-wrap:nowrap
     // todo: test flex-direction:row-reverse, flex-wrap:nowrap
     // todo: test flex-direction:row, flex-wrap:wrap
@@ -58,8 +59,68 @@ begin
 
   Viewport.Draw;
   AssertEquals('FlexDiv.Rendered',true,FlexDiv.Rendered);
-  AssertEquals('FlexDiv.ComputedDisplayInside',CSSRegistry.kwFlex,FlexDiv.ComputedDisplayInside);
-  AssertEquals('FlexDiv.ComputedDisplayOutside',CSSRegistry.kwBlock,FlexDiv.ComputedDisplayOutside);
+  AssertEquals('FlexDiv.GetComputedString(fcaWidth)','100px',FlexDiv.GetComputedString(fcaWidth));
+  AssertEquals('FlexDiv.GetComputedString(fcaDisplay)','flex',FlexDiv.GetComputedString(fcaDisplay));
+  AssertEquals('FlexDiv.ComputedDisplayInside',CSSRegistry.Keywords[CSSRegistry.kwFlex],CSSRegistry.Keywords[FlexDiv.ComputedDisplayInside]);
+  AssertEquals('FlexDiv.ComputedDisplayOutside',CSSRegistry.Keywords[CSSRegistry.kwBlock],CSSRegistry.Keywords[FlexDiv.ComputedDisplayOutside]);
+end;
+
+procedure TTestFlexLayout.TestFlexLayout_Empty_FlexInline;
+var
+  FlexDiv: TDiv;
+begin
+  FlexDiv:=TDiv.Create(Viewport);
+  FlexDiv.Name:='FlexDiv';
+  FlexDiv.Parent:=Viewport;
+
+  Viewport.Stylesheet.Text:=LinesToStr([
+  '#FlexDiv { width: 100px; height: 100px; display: inline flex; }'
+  ]);
+
+  Viewport.Draw;
+  AssertEquals('FlexDiv.Rendered',true,FlexDiv.Rendered);
+  AssertEquals('FlexDiv.GetComputedString(fcaWidth)','100px',FlexDiv.GetComputedString(fcaWidth));
+  AssertEquals('FlexDiv.GetComputedString(fcaDisplay)','inline flex',FlexDiv.GetComputedString(fcaDisplay));
+  AssertEquals('FlexDiv.ComputedDisplayInside',CSSRegistry.Keywords[CSSRegistry.kwFlex],CSSRegistry.Keywords[FlexDiv.ComputedDisplayInside]);
+  AssertEquals('FlexDiv.ComputedDisplayOutside',CSSRegistry.Keywords[CSSRegistry.kwInline],CSSRegistry.Keywords[FlexDiv.ComputedDisplayOutside]);
+end;
+
+procedure TTestFlexLayout.TestFlexLayout_Row_OneItem;
+var
+  FlexDiv, Div1: TDiv;
+  Body: TBody;
+begin
+  Body:=TBody.Create(Viewport);
+  Body.Name:='Body';
+  Body.Parent:=Viewport;
+
+  FlexDiv:=TDiv.Create(Viewport);
+  FlexDiv.Name:='FlexDiv';
+  FlexDiv.Parent:=Body;
+
+  Div1:=TDiv.Create(Viewport);
+  Div1.Name:='Div1';
+  Div1.Parent:=FlexDiv;
+
+  Viewport.Stylesheet.Text:=LinesToStr([
+  'body { margin: 0; }',
+  '#FlexDiv { display: flex; }',
+  '#Div1 { width: 30px; height: 20px; }'
+  ]);
+
+  Viewport.Draw;
+  AssertEquals('FlexDiv.Rendered',true,FlexDiv.Rendered);
+  AssertEquals('FlexDiv.GetComputedString(fcaDisplay)','flex',FlexDiv.GetComputedString(fcaDisplay));
+
+  AssertEquals('Div1.UsedContentBox.Left',0,Div1.UsedContentBox.Left);
+  AssertEquals('Div1.UsedContentBox.Top',0,Div1.UsedContentBox.Top);
+  AssertEquals('Div1.UsedContentBox.Width',30,Div1.UsedContentBox.Width);
+  AssertEquals('Div1.UsedContentBox.Height',20,Div1.UsedContentBox.Height);
+
+  AssertEquals('FlexDiv.UsedContentBox.Left',0,FlexDiv.UsedContentBox.Left);
+  AssertEquals('FlexDiv.UsedContentBox.Top',0,FlexDiv.UsedContentBox.Top);
+  AssertEquals('FlexDiv.UsedContentBox.Width',800,FlexDiv.UsedContentBox.Width);
+  AssertEquals('FlexDiv.UsedContentBox.Height',20,FlexDiv.UsedContentBox.Height);
 end;
 
 Initialization